[PATCH 5/9] lib: iterator API for message properties
authorDavid Bremner <david@tethera.net>
Sat, 6 Aug 2016 13:52:35 +0000 (22:52 +0900)
committerW. Trevor King <wking@tremily.us>
Sat, 20 Aug 2016 23:22:19 +0000 (16:22 -0700)
0c/d1484cb71d564c736296580e07122527fd06cc [new file with mode: 0644]

diff --git a/0c/d1484cb71d564c736296580e07122527fd06cc b/0c/d1484cb71d564c736296580e07122527fd06cc
new file mode 100644 (file)
index 0000000..eab72dd
--- /dev/null
@@ -0,0 +1,336 @@
+Return-Path: <bremner@tesseract.cs.unb.ca>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+ by arlo.cworth.org (Postfix) with ESMTP id 17B7C6DE035C\r
+ for <notmuch@notmuchmail.org>; Sat,  6 Aug 2016 06:53:41 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at cworth.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.003\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.003 tagged_above=-999 required=5\r
+ tests=[AWL=-0.004, HEADER_FROM_DIFFERENT_DOMAINS=0.001]\r
+ autolearn=disabled\r
+Received: from arlo.cworth.org ([127.0.0.1])\r
+ by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024)\r
+ with ESMTP id d8IKhSyQOqfv for <notmuch@notmuchmail.org>;\r
+ Sat,  6 Aug 2016 06:53:33 -0700 (PDT)\r
+Received: from fethera.tethera.net (fethera.tethera.net [198.245.60.197])\r
+ by arlo.cworth.org (Postfix) with ESMTPS id 901A06DE012F\r
+ for <notmuch@notmuchmail.org>; Sat,  6 Aug 2016 06:53:02 -0700 (PDT)\r
+Received: from remotemail by fethera.tethera.net with local (Exim 4.84_2)\r
+ (envelope-from <bremner@tesseract.cs.unb.ca>)\r
+ id 1bW22P-0007EG-Fa; Sat, 06 Aug 2016 09:53:17 -0400\r
+Received: (nullmailer pid 4133 invoked by uid 1000);\r
+ Sat, 06 Aug 2016 13:52:44 -0000\r
+From: David Bremner <david@tethera.net>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH 5/9] lib: iterator API for message properties\r
+Date: Sat,  6 Aug 2016 22:52:35 +0900\r
+Message-Id: <1470491559-3946-6-git-send-email-david@tethera.net>\r
+X-Mailer: git-send-email 2.8.1\r
+In-Reply-To: <1470491559-3946-1-git-send-email-david@tethera.net>\r
+References: <1470491559-3946-1-git-send-email-david@tethera.net>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.20\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+ <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <https://notmuchmail.org/mailman/options/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch/>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <https://notmuchmail.org/mailman/listinfo/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Sat, 06 Aug 2016 13:53:41 -0000\r
+\r
+This is a thin wrapper around the string map iterator API just introduced.\r
+---\r
+ lib/message-property.cc       |  38 +++++++++++++++\r
+ lib/notmuch.h                 |  95 +++++++++++++++++++++++++++++++++++++\r
+ test/T610-message-property.sh | 107 ++++++++++++++++++++++++++++++++++++++++++\r
+ 3 files changed, 240 insertions(+)\r
+\r
+diff --git a/lib/message-property.cc b/lib/message-property.cc\r
+index 1f04a20..0b13cac 100644\r
+--- a/lib/message-property.cc\r
++++ b/lib/message-property.cc\r
+@@ -106,3 +106,41 @@ notmuch_message_remove_all_properties (notmuch_message_t *message, const char *k\r
\r
+     return NOTMUCH_STATUS_SUCCESS;\r
+ }\r
++\r
++notmuch_message_properties_t *\r
++notmuch_message_get_properties (notmuch_message_t *message, const char *key, notmuch_bool_t exact)\r
++{\r
++    notmuch_string_map_t *map;\r
++    map = _notmuch_message_property_map (message);\r
++    return _notmuch_string_map_iterator_create (map, key, exact);\r
++}\r
++\r
++notmuch_bool_t\r
++notmuch_message_properties_valid (notmuch_message_properties_t *properties)\r
++{\r
++    return _notmuch_string_map_iterator_valid (properties);\r
++}\r
++\r
++void\r
++notmuch_message_properties_move_to_next (notmuch_message_properties_t *properties)\r
++{\r
++    return _notmuch_string_map_iterator_move_to_next (properties);\r
++}\r
++\r
++const char *\r
++notmuch_message_properties_key (notmuch_message_properties_t *properties)\r
++{\r
++    return _notmuch_string_map_iterator_key (properties);\r
++}\r
++\r
++const char *\r
++notmuch_message_properties_value (notmuch_message_properties_t *properties)\r
++{\r
++    return _notmuch_string_map_iterator_value (properties);\r
++}\r
++\r
++void\r
++notmuch_message_properties_destroy (notmuch_message_properties_t *properties)\r
++{\r
++    _notmuch_string_map_iterator_destroy (properties);\r
++}\r
+diff --git a/lib/notmuch.h b/lib/notmuch.h\r
+index b304dca..e03a05d 100644\r
+--- a/lib/notmuch.h\r
++++ b/lib/notmuch.h\r
+@@ -1723,6 +1723,101 @@ notmuch_message_remove_property (notmuch_message_t *message, const char *key, co\r
+ notmuch_status_t\r
+ notmuch_message_remove_all_properties (notmuch_message_t *message, const char *key);\r
\r
++/**\r
++ * Opaque message property iterator\r
++ */\r
++typedef struct _notmuch_string_map_iterator notmuch_message_properties_t;\r
++\r
++/**\r
++ * Get the properties for *message*, returning a\r
++ * notmuch_message_properties_t object which can be used to iterate\r
++ * over all properties.\r
++ *\r
++ * The notmuch_message_properties_t object is owned by the message and\r
++ * as such, will only be valid for as long as the message is valid,\r
++ * (which is until the query from which it derived is destroyed).\r
++ *\r
++ * @param[in] message  The message to examine\r
++ * @param[in] key      key or key prefix\r
++ * @param[in] exact    if TRUE, require exact match with key. Otherwise\r
++ *                   treat as prefix.\r
++ *\r
++ * Typical usage might be:\r
++ *\r
++ *     notmuch_message_properties_t *list;\r
++ *\r
++ *     for (list = notmuch_message_get_properties (message, "testkey1", TRUE);\r
++ *          notmuch_message_properties_valid (list); notmuch_message_properties_move_to_next (list)) {\r
++ *        printf("%s\n", notmuch_message_properties_value(list));\r
++ *     }\r
++ *\r
++ *     notmuch_message_properties_destroy (list);\r
++ *\r
++ * Note that there's no explicit destructor needed for the\r
++ * notmuch_message_properties_t object. (For consistency, we do\r
++ * provide a notmuch_message_properities_destroy function, but there's\r
++ * no good reason to call it if the message is about to be destroyed).\r
++ */\r
++notmuch_message_properties_t *\r
++notmuch_message_get_properties (notmuch_message_t *message, const char *key, notmuch_bool_t exact);\r
++\r
++/**\r
++ * Is the given *properties* iterator pointing at a valid (key,value)\r
++ * pair.\r
++ *\r
++ * When this function returns TRUE,\r
++ * notmuch_message_properties_{key,value} will return a valid string,\r
++ * and notmuch_message_properties_move_to_next will do what it\r
++ * says. Whereas when this function returns FALSE, calling any of\r
++ * these functions results in undefined behaviour.\r
++ *\r
++ * See the documentation of notmuch_message_properties_get for example\r
++ * code showing how to iterate over a notmuch_message_properties_t\r
++ * object.\r
++ */\r
++notmuch_bool_t\r
++notmuch_message_properties_valid (notmuch_message_properties_t *properties);\r
++\r
++/**\r
++ * Move the *properties* iterator to the next (key,value) pair\r
++ *\r
++ * If *properties* is already pointing at the last pair then the iterator\r
++ * will be moved to a point just beyond that last pair, (where\r
++ * notmuch_message_properties_valid will return FALSE).\r
++ *\r
++ * See the documentation of notmuch_message_get_properties for example\r
++ * code showing how to iterate over a notmuch_message_properties_t object.\r
++ */\r
++void\r
++notmuch_message_properties_move_to_next (notmuch_message_properties_t *properties);\r
++\r
++/**\r
++ * Return the key from the current (key,value) pair.\r
++ *\r
++ * this could be useful if iterating for a prefix\r
++ */\r
++const char *\r
++notmuch_message_properties_key (notmuch_message_properties_t *properties);\r
++\r
++/**\r
++ * Return the key from the current (key,value) pair.\r
++ *\r
++ * This could be useful if iterating for a prefix.\r
++ */\r
++const char *\r
++notmuch_message_properties_value (notmuch_message_properties_t *properties);\r
++\r
++\r
++/**\r
++ * Destroy a notmuch_message_properties_t object.\r
++ *\r
++ * It's not strictly necessary to call this function. All memory from\r
++ * the notmuch_message_properties_t object will be reclaimed when the\r
++ * containing message object is destroyed.\r
++ */\r
++void\r
++notmuch_message_properties_destroy (notmuch_message_properties_t *properties);\r
++\r
+ /**@}*/\r
\r
+ /**\r
+diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh\r
+index 0217950..b5ddb7a 100755\r
+--- a/test/T610-message-property.sh\r
++++ b/test/T610-message-property.sh\r
+@@ -9,8 +9,18 @@ cat <<EOF > c_head\r
+ #include <stdio.h>\r
+ #include <string.h>\r
+ #include <stdlib.h>\r
++#include <talloc.h>\r
+ #include <notmuch-test.h>\r
\r
++void print_properties (notmuch_message_t *message, const char *prefix, notmuch_bool_t exact) {\r
++    notmuch_message_properties_t *list;\r
++    for (list = notmuch_message_get_properties (message, prefix, exact);\r
++         notmuch_message_properties_valid (list); notmuch_message_properties_move_to_next (list)) {\r
++       printf("%s\n", notmuch_message_properties_value(list));\r
++    }\r
++    notmuch_message_properties_destroy (list);\r
++}\r
++\r
+ int main (int argc, char** argv)\r
+ {\r
+    notmuch_database_t *db;\r
+@@ -79,6 +89,103 @@ testkey2 = NULL\r
+ EOF\r
+ test_expect_equal_file EXPECTED OUTPUT\r
\r
++test_begin_subtest "notmuch_message_get_properties: empty list"\r
++cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}\r
++{\r
++   notmuch_message_properties_t *list;\r
++   list = notmuch_message_get_properties (message, "nonexistent", TRUE);\r
++   printf("valid = %d\n", notmuch_message_properties_valid (list));\r
++   notmuch_message_properties_destroy (list);\r
++}\r
++EOF\r
++cat <<'EOF' >EXPECTED\r
++== stdout ==\r
++valid = 0\r
++== stderr ==\r
++EOF\r
++test_expect_equal_file EXPECTED OUTPUT\r
\r
++test_begin_subtest "notmuch_message_properties: one value"\r
++cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}\r
++print_properties (message, "testkey1", TRUE);\r
++EOF\r
++cat <<'EOF' >EXPECTED\r
++== stdout ==\r
++testvalue1\r
++== stderr ==\r
++EOF\r
++test_expect_equal_file EXPECTED OUTPUT\r
++\r
++test_begin_subtest "notmuch_message_properties: multiple values"\r
++cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}\r
++EXPECT0(notmuch_message_add_property (message, "testkey1", "bob"));\r
++EXPECT0(notmuch_message_add_property (message, "testkey1", "testvalue2"));\r
++EXPECT0(notmuch_message_add_property (message, "testkey1", "alice"));\r
++print_properties (message, "testkey1", TRUE);\r
++EOF\r
++cat <<'EOF' >EXPECTED\r
++== stdout ==\r
++alice\r
++bob\r
++testvalue1\r
++testvalue2\r
++== stderr ==\r
++EOF\r
++test_expect_equal_file EXPECTED OUTPUT\r
++\r
++test_begin_subtest "notmuch_message_properties: prefix"\r
++cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}\r
++EXPECT0(notmuch_message_add_property (message, "testkey3", "bob3"));\r
++EXPECT0(notmuch_message_add_property (message, "testkey3", "testvalue3"));\r
++EXPECT0(notmuch_message_add_property (message, "testkey3", "alice3"));\r
++print_properties (message, "testkey", FALSE);\r
++EOF\r
++cat <<'EOF' >EXPECTED\r
++== stdout ==\r
++alice\r
++bob\r
++testvalue1\r
++testvalue2\r
++alice3\r
++bob3\r
++testvalue3\r
++== stderr ==\r
++EOF\r
++test_expect_equal_file EXPECTED OUTPUT\r
++\r
++test_begin_subtest "notmuch_message_properties: modify during iteration"\r
++cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}\r
++{\r
++    const char *keys[1000] = {NULL};\r
++    const char *vals[1000] = {NULL};\r
++    notmuch_message_properties_t *properties;\r
++    int i;\r
++\r
++    for (properties = notmuch_message_get_properties (message, "", FALSE), i=0;\r
++       notmuch_message_properties_valid (properties);\r
++       notmuch_message_properties_move_to_next (properties), i++)\r
++    {\r
++      const char *key, *value;\r
++\r
++      keys[i]=talloc_strdup(message,\r
++                  notmuch_message_properties_key (properties));\r
++        vals[i]=talloc_strdup(message,\r
++                  notmuch_message_properties_value (properties));\r
++\r
++      EXPECT0(notmuch_message_remove_property (message, keys[i], vals[i]));\r
++    }\r
++\r
++    print_properties (message, "", FALSE);\r
++\r
++    for (i = 0; keys[i] && vals[i]; i++) {\r
++        EXPECT0(notmuch_message_add_property (message, keys[i], vals[i]));\r
++    }\r
++}\r
++EOF\r
++cat <<'EOF' >EXPECTED\r
++== stdout ==\r
++== stderr ==\r
++EOF\r
++test_expect_equal_file EXPECTED OUTPUT\r
\r
+ test_done\r
+-- \r
+2.8.1\r
+\r