[geary/wip/766133-gnotification: 10/15] Add API for (de)serialising FolderPath and EmailIdentifier



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

    Add API for (de)serialising FolderPath and EmailIdentifier
    
    Supports (de)serialising via GLib.Variant for use as GLib.Action targets
    transmission via DBus, etc.

 src/engine/api/geary-account.vala                  | 18 ++++++++-
 src/engine/api/geary-email-identifier.vala         | 29 +++++++++++---
 src/engine/api/geary-folder-path.vala              | 46 ++++++++++++++++++++--
 src/engine/imap-db/imap-db-email-identifier.vala   | 39 +++++++++++++++++-
 .../imap-engine/imap-engine-generic-account.vala   | 20 ++++++++++
 src/engine/imap/api/imap-folder-root.vala          |  3 +-
 src/engine/outbox/outbox-email-identifier.vala     | 27 +++++++++++++
 test/engine/api/geary-account-mock.vala            | 15 +++++++
 test/engine/api/geary-email-identifier-mock.vala   |  4 ++
 test/engine/api/geary-folder-path-test.vala        |  9 +++++
 .../imap-db/imap-db-email-identifier-test.vala     | 26 ++++++++++++
 .../outbox/outbox-email-identifier-test.vala       | 24 +++++++++++
 test/meson.build                                   |  2 +
 test/test-engine.vala                              |  2 +
 14 files changed, 251 insertions(+), 13 deletions(-)
---
diff --git a/src/engine/api/geary-account.vala b/src/engine/api/geary-account.vala
index 3dbd5d1f..ae0fc056 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-2019 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.
@@ -304,6 +306,20 @@ public abstract class Geary.Account : BaseObject {
      */
     public abstract async void rebuild_async(Cancellable? cancellable = null) throws Error;
 
+    /**
+     * Returns an 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.BAD_PARAMETERS;
+
     /**
      * 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
diff --git a/src/engine/api/geary-email-identifier.vala b/src/engine/api/geary-email-identifier.vala
index 017faef6..28d514f2 100644
--- a/src/engine/api/geary-email-identifier.vala
+++ b/src/engine/api/geary-email-identifier.vala
@@ -1,7 +1,9 @@
-/* Copyright 2016 Software Freedom Conservancy Inc.
+/*
+ * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2019 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.
+ * (version 2.1 or later). See the COPYING file in this distribution.
  */
 
 /**
@@ -28,6 +30,25 @@ public abstract class Geary.EmailIdentifier : BaseObject, Gee.Hashable<Geary.Ema
         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;
@@ -111,8 +132,4 @@ public abstract class Geary.EmailIdentifier : BaseObject, Gee.Hashable<Geary.Ema
         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 2dafabcf..746c5d58 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-2019 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.
@@ -12,11 +14,14 @@
  *
  * @see FolderRoot
  */
-
 public class Geary.FolderPath :
     BaseObject, Gee.Hashable<FolderPath>, Gee.Comparable<FolderPath> {
 
 
+    /** Type of the GLib.Variant used to represent folder paths */
+    public const string VARIANT_TYPE = "as";
+
+
     // Workaround for Vala issue #659. See children below.
     private class FolderPathWeakRef {
 
@@ -218,7 +223,21 @@ public class Geary.FolderPath :
     }
 
     /**
-     * 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 specified by {@link VARIANT_TYPE}.
+     * @see FolderRoot.from_folder_path
+     */
+    public GLib.Variant to_variant() {
+        return new GLib.Variant.strv(as_array());
+    }
+
+    /**
+     * Returns a representation useful for debugging.
      *
      * Do not use this for obtaining an IMAP mailbox name to send to a
      * server, use {@link
@@ -287,6 +306,7 @@ public class Geary.FolderPath :
 
 }
 
+
 /**
  * The root of a folder hierarchy.
  *
@@ -314,4 +334,24 @@ public class Geary.FolderRoot : FolderPath {
         this.default_case_sensitivity = default_case_sensitivity;
     }
 
+    /**
+     * Reconstructs a path under this root from a GLib variant.
+     *
+     * @see FolderPath.to_variant
+     */
+    public FolderPath from_variant(GLib.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()
+            );
+        }
+
+        FolderPath path = this;
+        foreach (string step in serialised.get_strv()) {
+            path = path.get_child(step);
+        }
+        return path;
+    }
+
 }
diff --git a/src/engine/imap-db/imap-db-email-identifier.vala 
b/src/engine/imap-db/imap-db-email-identifier.vala
index 734e5ea7..9829d52d 100644
--- a/src/engine/imap-db/imap-db-email-identifier.vala
+++ b/src/engine/imap-db/imap-db-email-identifier.vala
@@ -1,10 +1,17 @@
-/* Copyright 2016 Software Freedom Conservancy Inc.
+/*
+ * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2018-2019 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.
+ * (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; }
 
@@ -26,6 +33,22 @@ private class Geary.ImapDB.EmailIdentifier : Geary.EmailIdentifier {
         this.uid = uid;
     }
 
+    /** Reconstructs an identifier from its variant representation. */
+    public EmailIdentifier.from_variant(GLib.Variant serialised)
+        throws EngineError.BAD_PARAMETERS {
+        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 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
@@ -55,6 +78,17 @@ private class Geary.ImapDB.EmailIdentifier : Geary.EmailIdentifier {
         return uid.compare_to(other.uid);
     }
 
+    public override GLib.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 GLib.Variant.tuple(new Variant[] {
+                new GLib.Variant.byte('i'),
+                new GLib.Variant.int64(this.message_id),
+                new GLib.Variant.int64(uid_value)
+            });
+    }
+
     public override string to_string() {
         return "[%s/%s]".printf(message_id.to_string(), (uid == null ? "null" : uid.to_string()));
     }
@@ -68,4 +102,5 @@ private class Geary.ImapDB.EmailIdentifier : Geary.EmailIdentifier {
 
         return uids;
     }
+
 }
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala 
b/src/engine/imap-engine/imap-engine-generic-account.vala
index 1c997089..908d8d94 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -25,6 +25,9 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         Geary.SpecialFolderType.ARCHIVE,
     };
 
+    private static GLib.VariantType email_id_type = new GLib.VariantType("(y*)");
+
+
     /** Service for incoming IMAP connections. */
     public Imap.ClientService imap  { get; private set; }
 
@@ -412,6 +415,23 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         }
     }
 
+    /** {@inheritDoc} */
+    public override EmailIdentifier to_email_identifier(GLib.Variant serialised)
+        throws EngineError.BAD_PARAMETERS {
+        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 Outbox.EmailIdentifier.from_variant(serialised);
+
+        throw new EngineError.BAD_PARAMETERS("Unknown serialised type: %c", type);
+    }
+
     public override Gee.Collection<Geary.Folder> list_matching_folders(Geary.FolderPath? parent)
         throws Error {
         check_open();
diff --git a/src/engine/imap/api/imap-folder-root.vala b/src/engine/imap/api/imap-folder-root.vala
index 0f8a39ee..bbe525b6 100644
--- a/src/engine/imap/api/imap-folder-root.vala
+++ b/src/engine/imap/api/imap-folder-root.vala
@@ -1,5 +1,6 @@
 /*
  * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2019 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,7 +18,7 @@
 public class Geary.Imap.FolderRoot : Geary.FolderRoot {
 
 
-    /**
+   /**
      * The canonical path for the IMAP inbox.
      *
      * This specific path object will always be returned when a child
diff --git a/src/engine/outbox/outbox-email-identifier.vala b/src/engine/outbox/outbox-email-identifier.vala
index 2828eeb9..f3b93a62 100644
--- a/src/engine/outbox/outbox-email-identifier.vala
+++ b/src/engine/outbox/outbox-email-identifier.vala
@@ -1,5 +1,6 @@
 /*
  * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2018-2019 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.
@@ -8,14 +9,30 @@
 private class Geary.Outbox.EmailIdentifier : Geary.EmailIdentifier {
 
 
+    private const string VARIANT_TYPE = "(yxx)";
+
+    public int64 message_id { get; private set; }
     public int64 ordering { get; private set; }
 
 
     public EmailIdentifier(int64 message_id, int64 ordering) {
         base("Outbox.EmailIdentifier:%s".printf(message_id.to_string()));
+        this.message_id = message_id;
         this.ordering = ordering;
     }
 
+    internal EmailIdentifier.from_variant(GLib.Variant serialised)
+        throws EngineError.BAD_PARAMETERS {
+        if (serialised.get_type_string() != VARIANT_TYPE) {
+            throw new EngineError.BAD_PARAMETERS(
+                "Invalid serialised id type: %s", serialised.get_type_string()
+            );
+        }
+        GLib.Variant mid = serialised.get_child_value(1);
+        GLib.Variant uid = serialised.get_child_value(2);
+        this(mid.get_int64(), uid.get_int64());
+    }
+
     public override int natural_sort_comparator(Geary.EmailIdentifier o) {
         EmailIdentifier? other = o as EmailIdentifier;
         if (other == null) {
@@ -24,4 +41,14 @@ private class Geary.Outbox.EmailIdentifier : Geary.EmailIdentifier {
         return (int) (ordering - other.ordering).clamp(-1, 1);
     }
 
+    public override GLib.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 GLib.Variant.tuple(new Variant[] {
+                new GLib.Variant.byte('s'),
+                new GLib.Variant.int64(this.message_id),
+                new GLib.Variant.int64(this.ordering)
+            });
+    }
+
 }
diff --git a/test/engine/api/geary-account-mock.vala b/test/engine/api/geary-account-mock.vala
index b605d9d1..fb1189a6 100644
--- a/test/engine/api/geary-account-mock.vala
+++ b/test/engine/api/geary-account-mock.vala
@@ -120,6 +120,21 @@ public class Geary.MockAccount : Account, MockObject {
         }
     }
 
+    public override EmailIdentifier to_email_identifier(GLib.Variant serialised)
+        throws EngineError.BAD_PARAMETERS {
+        try {
+            return object_or_throw_call(
+                "to_email_identifier",
+                { box_arg(serialised) },
+                new EngineError.BAD_PARAMETERS("Mock error")
+            );
+        } catch (EngineError.BAD_PARAMETERS err) {
+            throw err;
+        } catch (GLib.Error err) {
+            return new MockEmailIdentifer(0);
+        }
+    }
+
     public override Gee.Collection<Folder> list_folders() throws Error {
         return object_call<Gee.Collection<Folder>>(
             "list_folders", {}, Gee.List.empty<Folder>()
diff --git a/test/engine/api/geary-email-identifier-mock.vala 
b/test/engine/api/geary-email-identifier-mock.vala
index 83367ead..2928ca16 100644
--- a/test/engine/api/geary-email-identifier-mock.vala
+++ b/test/engine/api/geary-email-identifier-mock.vala
@@ -21,4 +21,8 @@ public class Geary.MockEmailIdentifer : EmailIdentifier {
         return (other_mock == null) ? 1 : this.id - other_mock.id;
     }
 
+    public override GLib.Variant to_variant() {
+        return new GLib.Variant.int32(id);
+    }
+
 }
diff --git a/test/engine/api/geary-folder-path-test.vala b/test/engine/api/geary-folder-path-test.vala
index 9f9a5f6f..952ca44c 100644
--- a/test/engine/api/geary-folder-path-test.vala
+++ b/test/engine/api/geary-folder-path-test.vala
@@ -26,6 +26,7 @@ public class Geary.FolderPathTest : TestCase {
         add_test("path_compare", path_compare);
         add_test("path_compare_normalised", path_compare_normalised);
         add_test("distinct_roots_compare", distinct_roots_compare);
+        add_test("variant_representation", variant_representation);
     }
 
     public override void set_up() {
@@ -305,4 +306,12 @@ public class Geary.FolderPathTest : TestCase {
 
     }
 
+    public void variant_representation() throws GLib.Error {
+        FolderPath orig = this.root.get_child("test");
+        GLib.Variant variant = orig.to_variant();
+        FolderPath copy = this.root.from_variant(variant);
+
+        assert_true(orig.equal_to(copy));
+    }
+
 }
diff --git a/test/engine/imap-db/imap-db-email-identifier-test.vala 
b/test/engine/imap-db/imap-db-email-identifier-test.vala
new file mode 100644
index 00000000..a8d04311
--- /dev/null
+++ b/test/engine/imap-db/imap-db-email-identifier-test.vala
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 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.
+ */
+
+public class Geary.ImapDB.EmailIdentifierTest : TestCase {
+
+
+    public EmailIdentifierTest() {
+        base("Geary.ImapDB.EmailIdentifierTest");
+        add_test("variant_representation", variant_representation);
+    }
+
+    public void variant_representation() throws GLib.Error {
+        EmailIdentifier orig = new EmailIdentifier(
+            123, new Imap.UID(321)
+        );
+        GLib.Variant variant = orig.to_variant();
+        EmailIdentifier copy = new EmailIdentifier.from_variant(variant);
+
+        assert_true(orig.equal_to(copy));
+    }
+
+}
diff --git a/test/engine/outbox/outbox-email-identifier-test.vala 
b/test/engine/outbox/outbox-email-identifier-test.vala
new file mode 100644
index 00000000..ad14ae9b
--- /dev/null
+++ b/test/engine/outbox/outbox-email-identifier-test.vala
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019 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.
+ */
+
+public class Geary.Outbox.EmailIdentifierTest : TestCase {
+
+
+    public EmailIdentifierTest() {
+        base("Geary.Outbox.EmailIdentifierTest");
+        add_test("variant_representation", variant_representation);
+    }
+
+    public void variant_representation() throws GLib.Error {
+        EmailIdentifier orig = new EmailIdentifier(123, 321);
+        GLib.Variant variant = orig.to_variant();
+        EmailIdentifier copy = new EmailIdentifier.from_variant(variant);
+
+        assert_true(orig.equal_to(copy));
+    }
+
+}
diff --git a/test/meson.build b/test/meson.build
index d93cd883..143d40fd 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -40,9 +40,11 @@ geary_test_engine_sources = [
   'engine/imap-db/imap-db-account-test.vala',
   'engine/imap-db/imap-db-attachment-test.vala',
   'engine/imap-db/imap-db-database-test.vala',
+  'engine/imap-db/imap-db-email-identifier-test.vala',
   'engine/imap-db/imap-db-folder-test.vala',
   'engine/imap-engine/account-processor-test.vala',
   'engine/mime-content-type-test.vala',
+  'engine/outbox/outbox-email-identifier-test.vala',
   'engine/rfc822-mailbox-address-test.vala',
   'engine/rfc822-mailbox-addresses-test.vala',
   'engine/rfc822-message-test.vala',
diff --git a/test/test-engine.vala b/test/test-engine.vala
index 6a9a17a2..8a575708 100644
--- a/test/test-engine.vala
+++ b/test/test-engine.vala
@@ -50,11 +50,13 @@ int main(string[] args) {
     engine.add_suite(new Geary.ImapDB.AttachmentTest().get_suite());
     engine.add_suite(new Geary.ImapDB.AttachmentIoTest().get_suite());
     engine.add_suite(new Geary.ImapDB.DatabaseTest().get_suite());
+    engine.add_suite(new Geary.ImapDB.EmailIdentifierTest().get_suite());
     engine.add_suite(new Geary.ImapDB.FolderTest().get_suite());
     engine.add_suite(new Geary.ImapEngine.AccountProcessorTest().get_suite());
     engine.add_suite(new Geary.Inet.Test().get_suite());
     engine.add_suite(new Geary.JS.Test().get_suite());
     engine.add_suite(new Geary.Mime.ContentTypeTest().get_suite());
+    engine.add_suite(new Geary.Outbox.EmailIdentifierTest().get_suite());
     engine.add_suite(new Geary.RFC822.MailboxAddressTest().get_suite());
     engine.add_suite(new Geary.RFC822.MailboxAddressesTest().get_suite());
     engine.add_suite(new Geary.RFC822.MessageTest().get_suite());


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