[geary/wip/730682-refine-convo-list] Add a GVariant API for (de)serialising FolderPath and EmailIdentifier.



commit 85e146c7f94c98e4e5207e61ceb7aa8e39cdd9d6
Author: Michael James Gratton <mike vee net>
Date:   Wed Jan 3 15:01:21 2018 +1100

    Add a GVariant API for (de)serialising FolderPath and EmailIdentifier.
    
    * src/engine/api/geary-account.vala (Account): Add to_email_identifier
      and to_folder_path methods for de-serialising these objects, replacing
      new_folder_path. Update subclasses and call sites.
    
    * src/engine/api/geary-email-identifier.vala (EmailIdentifier),
      src/engine/api/geary-folder-path.vala (FolderPath): Add to_variant()
      and tidy up the class source.
    
    * src/engine/imap-db/imap-db-email-identifier.vala (EmailIdentifier),
      src/engine/imap-db/outbox/smtp-outbox-email-identifier.vala
      (SmtpOutboxEmailIdentifier): Add from_variant ctor, implement
      to_variant. Clean up class source.
    
    * src/engine/imap/api/imap-folder-root.vala (FolderRoot): Implement
      from_variant factory method.
    
    * src/client/components/main-window.vala (path_to_variant): Use new
      engine API to pass FolderPaths around via action params.

 src/client/components/main-window.vala             |   42 +++----
 src/engine/api/geary-account.vala                  |   45 +++++--
 src/engine/api/geary-email-identifier.vala         |  129 +++++++++++---------
 src/engine/api/geary-folder-path.vala              |  112 ++++++++++-------
 src/engine/imap-db/imap-db-email-identifier.vala   |   72 ++++++++---
 .../outbox/smtp-outbox-email-identifier.vala       |   42 ++++++-
 .../imap-engine/imap-engine-generic-account.vala   |   37 ++++--
 src/engine/imap/api/imap-folder-root.vala          |   40 +++++--
 8 files changed, 337 insertions(+), 182 deletions(-)
---
diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala
index 8585b6d..1979b86 100644
--- a/src/client/components/main-window.vala
+++ b/src/client/components/main-window.vala
@@ -661,22 +661,6 @@ public class MainWindow : Gtk.ApplicationWindow {
         return (SimpleAction) lookup_action(name);
     }
 
-    private Geary.FolderPath? variant_to_path(Variant? strv) {
-        Geary.FolderPath? path = null;
-        if (this.current_folder != null &&
-            strv != null &&
-            strv.get_type_string() == "as") {
-            path = this.current_folder.account.new_folder_path(
-                new Gee.ArrayList<string>.wrap(strv.get_strv())
-            );
-        }
-        return path;
-    }
-
-    private Variant path_to_variant(Geary.FolderPath path) {
-        return new Variant.strv(path.as_list().to_array());
-    }
-
     private void on_folder_selected(Geary.Folder? folder) {
         if (folder != null) {
             update_folder(folder);
@@ -871,8 +855,15 @@ public class MainWindow : Gtk.ApplicationWindow {
     }
 
     private void on_conversation_copy(Action action, Variant? param) {
-        Geary.FolderPath? destination = variant_to_path(param);
-        if (path != null) {
+        Geary.FolderPath? destination = null;
+        if (param != null) {
+            try {
+                destination = this.current_folder.account.to_folder_path(param);
+            } catch (Geary.EngineError err) {
+                debug("Failed to deserialise folder path: %s", err.message);
+            }
+        }
+        if (destination != null) {
             this.application.controller.copy_conversations.begin(
                 this.conversation_list.get_highlighted_conversations(),
                 destination,
@@ -1009,8 +1000,15 @@ public class MainWindow : Gtk.ApplicationWindow {
     }
 
     private void on_conversation_move(Action action, Variant? param) {
-        Geary.FolderPath? destination = variant_to_path(param);
-        if (path != null) {
+        Geary.FolderPath? destination = null;
+        if (param != null) {
+            try {
+                destination = this.current_folder.account.to_folder_path(param);
+            } catch (Geary.EngineError err) {
+                debug("Failed to deserialise folder path: %s", err.message);
+            }
+        }
+        if (destination != null) {
             this.application.controller.move_conversations.begin(
                 this.conversation_list.get_highlighted_conversations(),
                 destination,
@@ -1055,11 +1053,11 @@ public class MainWindow : Gtk.ApplicationWindow {
     }
 
     public void on_copy_folder(Geary.Folder target) {
-        get_action(ACTION_COPY).activate(path_to_variant(target.path));
+        get_action(ACTION_COPY).activate(target.path.to_variant());
     }
 
     public void on_move_folder(Geary.Folder target) {
-        get_action(ACTION_MOVE).activate(path_to_variant(target.path));
+        get_action(ACTION_MOVE).activate(target.path.to_variant());
     }
 
     private void on_selection_mode_enabled() {
diff --git a/src/engine/api/geary-account.vala b/src/engine/api/geary-account.vala
index 6773ed9..6daf9b3 100644
--- a/src/engine/api/geary-account.vala
+++ b/src/engine/api/geary-account.vala
@@ -1,4 +1,6 @@
-/* Copyright 2016 Software Freedom Conservancy Inc.
+/*
+ * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2018 Michael Gratton <mike vee net>.
  *
  * This software is licensed under the GNU Lesser General Public License
  * (version 2.1 or later).  See the COPYING file in this distribution.
@@ -187,6 +189,35 @@ public abstract class Geary.Account : BaseObject {
         throws Error;
 
     /**
+     * Returns the email identifier from its serialised form.
+     *
+     * This is useful for converting a string representation of a
+     * email id back into an actual instance of an id. This does not
+     * guarantee that the email represented by the id will exist.
+     *
+     * @see EmailIdentifier.to_variant
+     * @throws EngineError.BAD_PARAMETERS when the variant is not the
+     * have the correct type.
+     */
+    public abstract EmailIdentifier to_email_identifier(GLib.Variant serialised)
+        throws EngineError;
+
+    /**
+     * Returns the folder path from its serialised form.
+     *
+     * This is useful for converting a string representation of a
+     * folder path back into an actual instance of a path. This does
+     * not guarantee that the folder represented by the path will
+     * exist.
+     *
+     * @see FolderPath.to_variant
+     * @throws EngineError.BAD_PARAMETERS when the variant is not the
+     * have the correct type.
+     */
+    public abstract FolderPath to_folder_path(GLib.Variant serialised)
+        throws EngineError;
+
+    /**
      * Lists all the currently-available folders found under the parent path
      * unless it's null, in which case it lists all the root folders.  If the
      * parent path cannot be found, EngineError.NOT_FOUND is thrown.  If no
@@ -209,18 +240,6 @@ public abstract class Geary.Account : BaseObject {
     public abstract Gee.Collection<Geary.Folder> list_folders() throws Error;
 
     /**
-     * Returns a path for a list of folder names.
-     *
-     * This is useful for converting a string representation of a
-     * folder path back into an actual instance of a folder path. This
-     * does not guarantee that the folder represented by the path will
-     * exist.
-     *
-     * {@see FolderPath.as_list}
-     */
-    public abstract FolderPath new_folder_path(Gee.List<string> name_list);
-
-    /**
      * Gets a perpetually update-to-date collection of autocompletion contacts.
      */
     public abstract Geary.ContactStore get_contact_store();
diff --git a/src/engine/api/geary-email-identifier.vala b/src/engine/api/geary-email-identifier.vala
index e044ab1..53488c0 100644
--- a/src/engine/api/geary-email-identifier.vala
+++ b/src/engine/api/geary-email-identifier.vala
@@ -1,4 +1,6 @@
-/* Copyright 2016 Software Freedom Conservancy Inc.
+/*
+ * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2018 Michael Gratton <mike vee net>.
  *
  * This software is licensed under the GNU Lesser General Public License
  * (version 2.1 or later).  See the COPYING file in this distribution.
@@ -17,24 +19,87 @@
  */
 
 public abstract class Geary.EmailIdentifier : BaseObject, Gee.Hashable<Geary.EmailIdentifier> {
+
+    /**
+     * Sorts the supplied Collection of {@link EmailIdentifier} by their natural sort order.
+     *
+     * This method uses {@link natural_sort_comparator}, so read its provisions about comparison.
+     * In essence, this method should only be used against EmailIdentifiers that originated from
+     * the same Folder.
+     */
+    public static Gee.SortedSet<Geary.EmailIdentifier> sort(Gee.Collection<Geary.EmailIdentifier> ids) {
+        Gee.SortedSet<Geary.EmailIdentifier> sorted = new Gee.TreeSet<Geary.EmailIdentifier>(
+            (a, b) => {
+                int cmp = a.natural_sort_comparator(b);
+                if (cmp == 0)
+                    cmp = a.stable_sort_comparator(b);
+
+                return cmp;
+            });
+        sorted.add_all(ids);
+
+        return sorted;
+    }
+
+    /**
+     * Sorts the supplied Collection of {@link EmailIdentifier} by their natural sort order.
+     *
+     * This method uses {@link natural_sort_comparator}, so read its provisions about comparison.
+     * In essence, this method should only be used against EmailIdentifiers that originated from
+     * the same Folder.
+     */
+    public static Gee.SortedSet<Geary.Email> sort_emails(Gee.Collection<Geary.Email> emails) {
+        Gee.SortedSet<Geary.Email> sorted = new Gee.TreeSet<Geary.Email>(
+            (a, b) => {
+                int cmp = a.id.natural_sort_comparator(b.id);
+                if (cmp == 0)
+                    cmp = a.id.stable_sort_comparator(b.id);
+
+                return cmp;
+            });
+        sorted.add_all(emails);
+
+        return sorted;
+    }
+
+
     // Warning: only change this if you know what you are doing.
     protected string unique;
-    
+
     protected EmailIdentifier(string unique) {
         this.unique = unique;
     }
-    
+
     public virtual uint hash() {
         return unique.hash();
     }
-    
+
+    /**
+     * Returns a representation useful for serialisation.
+     *
+     * This can be used to transmit ids as D-Bus method and GLib
+     * Action parameters, and so on.
+     *
+     * @returns a serialised form of this id, that will match the
+     * GVariantType `(*)`
+     * @see Account.to_email_identifier
+     */
+    public abstract GLib.Variant to_variant();
+
+    /**
+     * Returns a representation useful for debugging.
+     */
+    public virtual string to_string() {
+        return "[%s]".printf(unique.to_string());
+    }
+
     public virtual bool equal_to(Geary.EmailIdentifier other) {
         if (this == other)
             return true;
-        
+
         return unique == other.unique;
     }
-    
+
     /**
      * A comparator for stabilizing sorts.
      *
@@ -44,10 +109,10 @@ public abstract class Geary.EmailIdentifier : BaseObject, Gee.Hashable<Geary.Ema
     public virtual int stable_sort_comparator(Geary.EmailIdentifier other) {
         if (this == other)
             return 0;
-        
+
         return strcmp(unique, other.unique);
     }
-    
+
     /**
      * A comparator for finding which {@link EmailIdentifier} is earliest in the "natural"
      * sorting of a {@link Folder}'s list.
@@ -68,51 +133,5 @@ public abstract class Geary.EmailIdentifier : BaseObject, Gee.Hashable<Geary.Ema
      * @see Folder.list_email_by_id_async
      */
     public abstract int natural_sort_comparator(Geary.EmailIdentifier other);
-    
-    /**
-     * Sorts the supplied Collection of {@link EmailIdentifier} by their natural sort order.
-     *
-     * This method uses {@link natural_sort_comparator}, so read its provisions about comparison.
-     * In essence, this method should only be used against EmailIdentifiers that originated from
-     * the same Folder.
-     */
-    public static Gee.SortedSet<Geary.EmailIdentifier> sort(Gee.Collection<Geary.EmailIdentifier> ids) {
-        Gee.SortedSet<Geary.EmailIdentifier> sorted = new Gee.TreeSet<Geary.EmailIdentifier>(
-            (a, b) => {
-                int cmp = a.natural_sort_comparator(b);
-                if (cmp == 0)
-                    cmp = a.stable_sort_comparator(b);
-                
-                return cmp;
-            });
-        sorted.add_all(ids);
-        
-        return sorted;
-    }
-    
-    /**
-     * Sorts the supplied Collection of {@link EmailIdentifier} by their natural sort order.
-     *
-     * This method uses {@link natural_sort_comparator}, so read its provisions about comparison.
-     * In essence, this method should only be used against EmailIdentifiers that originated from
-     * the same Folder.
-     */
-    public static Gee.SortedSet<Geary.Email> sort_emails(Gee.Collection<Geary.Email> emails) {
-        Gee.SortedSet<Geary.Email> sorted = new Gee.TreeSet<Geary.Email>(
-            (a, b) => {
-                int cmp = a.id.natural_sort_comparator(b.id);
-                if (cmp == 0)
-                    cmp = a.id.stable_sort_comparator(b.id);
-                
-                return cmp;
-            });
-        sorted.add_all(emails);
-        
-        return sorted;
-    }
-    
-    public virtual string to_string() {
-        return "[%s]".printf(unique.to_string());
-    }
-}
 
+}
diff --git a/src/engine/api/geary-folder-path.vala b/src/engine/api/geary-folder-path.vala
index 7f0bf7b..2828a94 100644
--- a/src/engine/api/geary-folder-path.vala
+++ b/src/engine/api/geary-folder-path.vala
@@ -1,4 +1,6 @@
-/* Copyright 2016 Software Freedom Conservancy Inc.
+/*
+ * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2018 Michael Gratton <mike vee net>.
  *
  * This software is licensed under the GNU Lesser General Public License
  * (version 2.1 or later).  See the COPYING file in this distribution.
@@ -15,11 +17,16 @@
 
 public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
     Gee.Comparable<Geary.FolderPath> {
+
+
+    public const string VARIANT_TYPE = "as";
+
+
     /**
      * The name of this folder (without any child or parent names or delimiters).
      */
     public string basename { get; private set; }
-    
+
     /**
      * Whether this path is lexiographically case-sensitive.
      *
@@ -29,22 +36,22 @@ public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
 
     private Gee.List<Geary.FolderPath>? path = null;
     private uint stored_hash = uint.MAX;
-    
+
     protected FolderPath(string basename, bool case_sensitive) {
         assert(this is FolderRoot);
-        
+
         this.basename = basename;
         this.case_sensitive = case_sensitive;
     }
-    
+
     private FolderPath.child(Gee.List<Geary.FolderPath> path, string basename, bool case_sensitive) {
         assert(path[0] is FolderRoot);
-        
+
         this.path = path;
         this.basename = basename;
         this.case_sensitive = case_sensitive;
     }
-    
+
     /**
      * Returns true if this {@link FolderPath} is a root folder.
      *
@@ -56,14 +63,14 @@ public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
     public bool is_root() {
         return (path == null || path.size == 0);
     }
-    
+
     /**
      * Returns the {@link FolderRoot} of this path.
      */
     public Geary.FolderRoot get_root() {
         return (FolderRoot) ((path != null && path.size > 0) ? path[0] : this);
     }
-    
+
     /**
      * Returns the parent {@link FolderPath} of this folder or null if this is the root.
      *
@@ -72,7 +79,7 @@ public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
     public Geary.FolderPath? get_parent() {
         return (path != null && path.size > 0) ? path.last() : null;
     }
-    
+
     /**
      * Returns the number of folders in this path, not including any children of this object.
      */
@@ -80,7 +87,7 @@ public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
         // include self, which is not stored in the path list
         return (path != null) ? path.size + 1 : 1;
     }
-    
+
     /**
      * Returns the {@link FolderPath} object at the index, with this FolderPath object being
      * the farthest child.
@@ -98,17 +105,17 @@ public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
         // look like "this" is stored at the end of the path list
         if (path == null)
             return (index == 0) ? this : null;
-        
+
         int length = path.size;
         if (index < length)
             return path[index];
-        
+
         if (index == length)
             return this;
-        
+
         return null;
     }
-    
+
     /**
      * Returns the {@link FolderPath} as a List of {@link basename} strings, this FolderPath's
      * being the last in the list.
@@ -117,17 +124,17 @@ public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
      */
     public Gee.List<string> as_list() {
         Gee.List<string> list = new Gee.ArrayList<string>();
-        
+
         if (path != null) {
             foreach (Geary.FolderPath folder in path)
                 list.add(folder.basename);
         }
-        
+
         list.add(basename);
-        
+
         return list;
     }
-    
+
     /**
      * Creates a {@link FolderPath} object that is a child of this folder.
      *
@@ -140,11 +147,11 @@ public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
         if (path != null)
             child_path.add_all(path);
         child_path.add(this);
-        
+
         return new FolderPath.child(child_path, basename,
             child_case_sensitive.to_boolean(get_root().default_case_sensitivity));
     }
-    
+
     /**
      * Returns true if the other {@link FolderPath} has the same parent as this one.
      *
@@ -154,35 +161,35 @@ public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
     public bool has_same_parent(FolderPath other) {
         FolderPath? parent = get_parent();
         FolderPath? other_parent = other.get_parent();
-        
+
         if (parent == other_parent)
             return true;
-        
+
         if (parent != null && other_parent != null)
             return parent.equal_to(other_parent);
-        
+
         return false;
     }
 
     private uint get_basename_hash() {
         return case_sensitive ? str_hash(basename) : str_hash(basename.down());
     }
-    
+
     private int compare_internal(Geary.FolderPath other, bool allow_case_sensitive, bool normalize) {
         if (this == other)
             return 0;
-        
+
         // walk elements using as_list() as that includes the basename (whereas path does not),
         // avoids the null problem, and makes comparisons straightforward
         Gee.List<string> this_list = as_list();
         Gee.List<string> other_list = other.as_list();
-        
+
         // if paths exist, do comparison of each parent in order
         int min = int.min(this_list.size, other_list.size);
         for (int ctr = 0; ctr < min; ctr++) {
             string this_element = this_list[ctr];
             string other_element = other_list[ctr];
-            
+
             if (normalize) {
                 this_element = this_element.normalize();
                 other_element = other_element.normalize();
@@ -193,17 +200,17 @@ public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
                 this_element = this_element.casefold();
                 other_element = other_element.casefold();
             }
-            
+
             int result = this_element.collate(other_element);
             if (result != 0)
                 return result;
         }
-        
+
         // paths up to the min element count are equal, shortest path is less-than, otherwise
         // equal paths
         return this_list.size - other_list.size;
     }
-    
+
     /**
      * Does a Unicode-normalized, case insensitive match.  Useful for getting a rough idea if
      * a folder matches a name, but shouldn't be used to determine strict equality.
@@ -211,7 +218,7 @@ public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
     public int compare_normalized_ci(Geary.FolderPath other) {
         return compare_internal(other, false, true);
     }
-    
+
     /**
      * {@inheritDoc}
      *
@@ -229,7 +236,7 @@ public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
     public int compare_to(Geary.FolderPath other) {
         return compare_internal(other, true, false);
     }
-    
+
     /**
      * {@inheritDoc}
      *
@@ -238,22 +245,22 @@ public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
     public uint hash() {
         if (stored_hash != uint.MAX)
             return stored_hash;
-        
+
         // always one element in path
         stored_hash = get_folder_at(0).get_basename_hash();
-        
+
         int path_length = get_path_length();
         for (int ctr = 1; ctr < path_length; ctr++)
             stored_hash ^= get_folder_at(ctr).get_basename_hash();
-        
+
         return stored_hash;
     }
-    
+
     private bool is_basename_equal(string cmp, bool other_cs) {
         // case-sensitive comparison if either is sensitive
         return (other_cs || case_sensitive) ? (basename == cmp) : (basename.down() == cmp.down());
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -261,21 +268,35 @@ public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
         int path_length = get_path_length();
         if (other.get_path_length() != path_length)
             return false;
-        
+
         for (int ctr = 0; ctr < path_length; ctr++) {
             // this should never return null as length is already checked
             FolderPath? other_folder = other.get_folder_at(ctr);
             assert(other_folder != null);
-            
+
             if (!get_folder_at(ctr).is_basename_equal(other_folder.basename, other_folder.case_sensitive))
                 return false;
         }
-        
+
         return true;
     }
 
     /**
-     * Returns a string version of the path using a default separator.
+     * Returns a representation useful for serialisation.
+     *
+     * This can be used to transmit folder paths as D-Bus method and
+     * GLib Action parameters, and so on.
+     *
+     * @returns a serialised form of this path, that will match the
+     * GVariantType `as`
+     * @see Account.to_folder_path
+     */
+    public Variant to_variant() {
+        return new Variant.strv(as_list().to_array());
+    }
+
+    /**
+     * Returns a representation useful for debugging.
      *
      * Do not use this for obtaining an IMAP mailbox name to send to a
      * server, use {@link Geary.Imap.MailboxSpecifier.from_folder_path}
@@ -292,8 +313,10 @@ public class Geary.FolderPath : BaseObject, Gee.Hashable<Geary.FolderPath>,
         builder.append(basename);
         return builder.str;
     }
+
 }
 
+
 /**
  * The root of a folder heirarchy.
  *
@@ -312,11 +335,10 @@ public abstract class Geary.FolderRoot : Geary.FolderPath {
      * @see FolderPath.get_child
      */
     public bool default_case_sensitivity { get; private set; }
-    
+
     protected FolderRoot(string basename, bool case_sensitive, bool default_case_sensitivity) {
         base (basename, case_sensitive);
-        
+
         this.default_case_sensitivity = default_case_sensitivity;
     }
 }
-
diff --git a/src/engine/imap-db/imap-db-email-identifier.vala 
b/src/engine/imap-db/imap-db-email-identifier.vala
index 5f0fb0e..e7b1a44 100644
--- a/src/engine/imap-db/imap-db-email-identifier.vala
+++ b/src/engine/imap-db/imap-db-email-identifier.vala
@@ -1,85 +1,117 @@
-/* Copyright 2016 Software Freedom Conservancy Inc.
+/*
+ * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2018 Michael Gratton <mike vee net>.
  *
  * This software is licensed under the GNU Lesser General Public License
  * (version 2.1 or later).  See the COPYING file in this distribution.
  */
 
 private class Geary.ImapDB.EmailIdentifier : Geary.EmailIdentifier {
+
+
+    private const string VARIANT_TYPE = "(yxx)";
+
+
     public int64 message_id { get; private set; }
     public Imap.UID? uid { get; private set; }
-    
+
     public EmailIdentifier(int64 message_id, Imap.UID? uid) {
         assert(message_id != Db.INVALID_ROWID);
-        
+
         base (message_id.to_string());
-        
+
         this.message_id = message_id;
         this.uid = uid;
     }
-    
+
+    public EmailIdentifier.from_variant(Variant serialised) throws EngineError {
+        if (serialised.get_type_string() != VARIANT_TYPE) {
+            throw new EngineError.BAD_PARAMETERS(
+                "Invalid serialised id type: %s", serialised.get_type_string()
+            );
+        }
+        Imap.UID? uid = null;
+        int64 uid_value = serialised.get_child_value(2).get_int64();
+        if (uid_value >= 0) {
+            uid = new Imap.UID(uid_value);
+        }
+        this(serialised.get_child_value(1).get_int64(), uid);
+    }
+
     // Used when a new message comes off the wire and doesn't have a rowid associated with it (yet)
     // Requires a UID in order to find or create such an association
     public EmailIdentifier.no_message_id(Imap.UID uid) {
         base (Db.INVALID_ROWID.to_string());
-        
+
         message_id = Db.INVALID_ROWID;
         this.uid = uid;
     }
-    
+
     // Used to promote an id created with no_message_id to one that has a
     // message id.  Warning: this causes the hash value to change, so if you
     // have any EmailIdentifiers in a hashed data structure, this will cause
     // you not to be able to find them.
     public void promote_with_message_id(int64 message_id) {
         assert(this.message_id == Db.INVALID_ROWID);
-        
+
         unique = message_id.to_string();
         this.message_id = message_id;
     }
-    
+
     public bool has_uid() {
         return (uid != null) && uid.is_valid();
     }
-    
+
     public override int natural_sort_comparator(Geary.EmailIdentifier o) {
         ImapDB.EmailIdentifier? other = o as ImapDB.EmailIdentifier;
         if (other == null)
             return 1;
-        
+
         if (uid == null)
             return 1;
-        
+
         if (other.uid == null)
             return -1;
-        
+
         return uid.compare_to(other.uid);
     }
-    
+
+    public override Variant to_variant() {
+        // Return a tuple to satisfy the API contract, add an 'i' to
+        // inform GenericAccount that it's an IMAP id.
+        int64 uid_value = this.uid != null ? this.uid.value : -1;
+        return new Variant.tuple(new Variant[] {
+                new Variant.byte('i'),
+                new Variant.int64(this.message_id),
+                new Variant.int64(uid_value)
+            });
+    }
+
     public override string to_string() {
         return "[%s/%s]".printf(message_id.to_string(), (uid == null ? "null" : uid.to_string()));
     }
-    
+
     // Email's with no UID get sorted after emails with
     public static int compare_email_uid_ascending(Geary.Email a, Geary.Email b) {
         Imap.UID? auid = ((ImapDB.EmailIdentifier) a.id).uid;
         Imap.UID? buid = ((ImapDB.EmailIdentifier) b.id).uid;
-        
+
         if (auid == null)
             return (buid != null) ? 1 : 0;
-        
+
         if (buid == null)
             return -1;
-        
+
         return auid.compare_to(buid);
     }
-    
+
     public static Gee.Set<Imap.UID> to_uids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
         Gee.HashSet<Imap.UID> uids = new Gee.HashSet<Imap.UID>();
         foreach (ImapDB.EmailIdentifier id in ids) {
             if (id.uid != null)
                 uids.add(id.uid);
         }
-        
+
         return uids;
     }
 }
diff --git a/src/engine/imap-db/outbox/smtp-outbox-email-identifier.vala 
b/src/engine/imap-db/outbox/smtp-outbox-email-identifier.vala
index 2500acc..6acc2cf 100644
--- a/src/engine/imap-db/outbox/smtp-outbox-email-identifier.vala
+++ b/src/engine/imap-db/outbox/smtp-outbox-email-identifier.vala
@@ -1,24 +1,54 @@
-/* Copyright 2016 Software Freedom Conservancy Inc.
+/*
+ * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2018 Michael Gratton <mike vee net>.
  *
  * This software is licensed under the GNU Lesser General Public License
  * (version 2.1 or later).  See the COPYING file in this distribution.
  */
 
 private class Geary.SmtpOutboxEmailIdentifier : Geary.EmailIdentifier {
+
+
+    private const string VARIANT_TYPE = "(yxx)";
+
+    public int64 message_id { get; private set; }
     public int64 ordering { get; private set; }
-    
+
+
     public SmtpOutboxEmailIdentifier(int64 message_id, int64 ordering) {
         base ("SmtpOutboxEmailIdentifer:%s".printf(message_id.to_string()));
-        
+        this.message_id = message_id;
         this.ordering = ordering;
     }
-    
+
+    internal SmtpOutboxEmailIdentifier.from_variant(Variant serialised)
+        throws EngineError {
+        if (serialised.get_type_string() != VARIANT_TYPE) {
+            throw new EngineError.BAD_PARAMETERS(
+                "Invalid serialised id type: %s", serialised.get_type_string()
+            );
+        }
+        Variant mid = serialised.get_child_value(1);
+        Variant uid = serialised.get_child_value(2);
+        this(mid.get_int64(), uid.get_int64());
+    }
+
     public override int natural_sort_comparator(Geary.EmailIdentifier o) {
         SmtpOutboxEmailIdentifier? other = o as SmtpOutboxEmailIdentifier;
         if (other == null)
             return 1;
-        
+
         return (int) (ordering - other.ordering).clamp(-1, 1);
     }
-}
 
+    public override Variant to_variant() {
+        // Return a tuple to satisfy the API contract, add an 's' to
+        // inform GenericAccount that it's an SMTP id.
+        return new Variant.tuple(new Variant[] {
+                new Variant.byte('s'),
+                new Variant.int64(this.message_id),
+                new Variant.int64(this.ordering)
+            });
+    }
+
+}
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala 
b/src/engine/imap-engine/imap-engine-generic-account.vala
index 6872e0a..6e8340b 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -1,4 +1,6 @@
-/* Copyright 2016 Software Freedom Conservancy Inc.
+/*
+ * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2018 Michael Gratton <mike vee net>.
  *
  * This software is licensed under the GNU Lesser General Public License
  * (version 2.1 or later).  See the COPYING file in this distribution.
@@ -18,6 +20,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
 
     private static Geary.FolderPath? outbox_path = null;
     private static Geary.FolderPath? search_path = null;
+    private static VariantType email_id_type = new VariantType("(y*)");
 
     private Imap.Account remote;
     private ImapDB.Account local;
@@ -304,6 +307,27 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         yield this.remote.open_async();
     }
 
+    public override EmailIdentifier to_email_identifier(GLib.Variant serialised)
+        throws EngineError {
+        if (serialised.is_of_type(GenericAccount.email_id_type)) {
+            throw new EngineError.BAD_PARAMETERS(
+                "Invalid outer serialised type: (y*)"
+            );
+        }
+        char type = (char) serialised.get_child_value(0).get_byte();
+        if (type == 'i')
+            return new ImapDB.EmailIdentifier.from_variant(serialised);
+        if (type == 's')
+            return new SmtpOutboxEmailIdentifier.from_variant(serialised);
+
+        throw new EngineError.BAD_PARAMETERS("Unknown serialised type: %c", type);
+    }
+
+    public override FolderPath to_folder_path(GLib.Variant serialised)
+        throws EngineError {
+        return Imap.FolderRoot.from_variant(serialised);
+    }
+
     // Subclasses should implement this to return their flavor of a MinimalFolder with the
     // appropriate interfaces attached.  The returned folder should have its SpecialFolderType
     // set using either the properties from the local folder or its path.
@@ -370,17 +394,6 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
 
         return all_folders;
     }
-
-    public override FolderPath new_folder_path(Gee.List<string> name_list) {
-        Gee.Iterator<string> names = name_list.iterator();
-        names.next();
-        Geary.FolderPath path = new Imap.FolderRoot(names.get());
-        while (names.next()) {
-            path = path.get_child(names.get());
-        }
-        return path;
-    }
-
     private void reschedule_unseen_update(Geary.Folder folder) {
         if (!folder_map.has_key(folder.path))
             return;
diff --git a/src/engine/imap/api/imap-folder-root.vala b/src/engine/imap/api/imap-folder-root.vala
index e19a4a1..7d8be1c 100644
--- a/src/engine/imap/api/imap-folder-root.vala
+++ b/src/engine/imap/api/imap-folder-root.vala
@@ -1,4 +1,6 @@
-/* Copyright 2016 Software Freedom Conservancy Inc.
+/*
+ * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2018 Michael Gratton <mike vee net>.
  *
  * This software is licensed under the GNU Lesser General Public License
  * (version 2.1 or later).  See the COPYING file in this distribution.
@@ -13,28 +15,48 @@
  */
 
 private class Geary.Imap.FolderRoot : Geary.FolderRoot {
+
+
+    public static FolderPath from_variant(Variant serialised)
+        throws EngineError {
+        if (serialised.get_type_string() != VARIANT_TYPE) {
+            throw new EngineError.BAD_PARAMETERS(
+                "Invalid serialised id type: %s", serialised.get_type_string()
+            );
+        }
+
+        Variant strv = serialised.get_child_value(0);
+        FolderPath path = new FolderRoot(strv.get_child_value(0).get_string());
+        for (int i = 1; i < strv.n_children(); i++) {
+            path = path.get_child(strv.get_child_value(0).get_string());
+        }
+        return path;
+    }
+
+
     public bool is_inbox { get; private set; }
-    
+
+
     public FolderRoot(string basename) {
         bool init_is_inbox;
         string normalized_basename = init(basename, out init_is_inbox);
-        
+
         base (normalized_basename, !init_is_inbox, true);
-        
+
         is_inbox = init_is_inbox;
     }
-    
+
     // This is the magic that ensures the canonical IMAP Inbox name is used throughout the engine
     private static string init(string basename, out bool is_inbox) {
         if (MailboxSpecifier.is_inbox_name(basename)) {
             is_inbox = true;
-            
+
             return MailboxSpecifier.CANONICAL_INBOX_NAME;
         }
-        
+
         is_inbox = false;
-        
+
         return basename;
     }
-}
 
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]