From: David Bremner <david@tethera.net>
To: notmuch@notmuchmail.org
Subject: [RFC2 Patch 3/5] lib: basic message-property API
Date: Mon, 30 May 2016 08:49:57 -0300 [thread overview]
Message-ID: <1464608999-14774-4-git-send-email-david@tethera.net> (raw)
In-Reply-To: <1464608999-14774-1-git-send-email-david@tethera.net>
Initially, support get, set and remove of single key/value pair
---
lib/Makefile.local | 1 +
lib/message-private.h | 10 +++++
lib/message-property.cc | 86 +++++++++++++++++++++++++++++++++++++++
lib/message.cc | 48 ++++++++++++++++++++++
lib/notmuch.h | 14 +++++++
test/T610-message-property.sh | 94 +++++++++++++++++++++++++++++++++++++++++++
6 files changed, 253 insertions(+)
create mode 100644 lib/message-private.h
create mode 100644 lib/message-property.cc
create mode 100755 test/T610-message-property.sh
diff --git a/lib/Makefile.local b/lib/Makefile.local
index 9280880..c012ed1 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -49,6 +49,7 @@ libnotmuch_cxx_srcs = \
$(dir)/directory.cc \
$(dir)/index.cc \
$(dir)/message.cc \
+ $(dir)/message-property.cc \
$(dir)/query.cc \
$(dir)/query-fp.cc \
$(dir)/config.cc \
diff --git a/lib/message-private.h b/lib/message-private.h
new file mode 100644
index 0000000..61e5bac
--- /dev/null
+++ b/lib/message-private.h
@@ -0,0 +1,10 @@
+#ifndef MESSAGE_PRIVATE_H
+#define MESSAGE_PRIVATE_H
+
+notmuch_string_map_t *
+_notmuch_message_property_map (notmuch_message_t *message);
+
+notmuch_bool_t
+_notmuch_message_frozen (notmuch_message_t *message);
+
+#endif
diff --git a/lib/message-property.cc b/lib/message-property.cc
new file mode 100644
index 0000000..21348a3
--- /dev/null
+++ b/lib/message-property.cc
@@ -0,0 +1,86 @@
+/* message-property.cc - Properties are like tags, but (key,value) pairs.
+ * keys are allowed to repeat.
+ *
+ * This file is part of notmuch.
+ *
+ * Copyright © 2016 David Bremner
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: David Bremner <david@tethera.net>
+ */
+
+#include "notmuch-private.h"
+#include "database-private.h"
+#include "message-private.h"
+
+notmuch_status_t
+notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value)
+{
+ if (! value)
+ return NOTMUCH_STATUS_NULL_POINTER;
+
+ *value = _notmuch_string_map_get (_notmuch_message_property_map (message), key);
+
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
+static notmuch_status_t
+_notmuch_message_modify_property (notmuch_message_t *message, const char *key, const char *value,
+ notmuch_bool_t delete_it)
+{
+ notmuch_private_status_t private_status;
+ notmuch_status_t status;
+ char *term = NULL;
+
+ status = _notmuch_database_ensure_writable (_notmuch_message_database (message));
+ if (status)
+ return status;
+
+ if (key == NULL || value == NULL)
+ return NOTMUCH_STATUS_NULL_POINTER;
+
+ if (index (key, '=') || index (value, '='))
+ return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
+
+ term = talloc_asprintf (message, "%s=%s", key, value);
+
+ if (delete_it)
+ private_status = _notmuch_message_remove_term (message, "property", term);
+ else
+ private_status = _notmuch_message_add_term (message, "property", term);
+
+ if (private_status)
+ return COERCE_STATUS (private_status,
+ "Unhandled error modifying message property");
+ if (! _notmuch_message_frozen (message))
+ _notmuch_message_sync (message);
+
+ if (term)
+ talloc_free (term);
+
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
+notmuch_status_t
+notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value)
+{
+ return _notmuch_message_modify_property (message, key, value, FALSE);
+}
+
+notmuch_status_t
+notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value)
+{
+ return _notmuch_message_modify_property (message, key, value, TRUE);
+}
diff --git a/lib/message.cc b/lib/message.cc
index 47946a3..0eb7e2c 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -20,6 +20,7 @@
#include "notmuch-private.h"
#include "database-private.h"
+#include "message-private.h"
#include <stdint.h>
@@ -1799,3 +1800,50 @@ _notmuch_message_database (notmuch_message_t *message)
{
return message->notmuch;
}
+
+void
+_notmuch_message_ensure_property_map (notmuch_message_t *message)
+{
+ notmuch_string_node_t *node;
+
+ if (message->property_map)
+ return;
+
+ if (!message->property_term_list)
+ _notmuch_message_ensure_metadata (message);
+
+ message->property_map = _notmuch_string_map_create (message);
+
+ for (node = message->property_term_list->head; node; node = node->next) {
+ const char *key;
+ char *value;
+
+ value = index(node->string, '=');
+ if (!value)
+ INTERNAL_ERROR ("malformed property term");
+
+ *value = '\0';
+ value++;
+ key = node->string;
+
+ _notmuch_string_map_append (message->property_map, key, value);
+
+ }
+
+ talloc_free (message->property_term_list);
+ message->property_term_list = NULL;
+}
+
+notmuch_string_map_t *
+_notmuch_message_property_map (notmuch_message_t *message)
+{
+ _notmuch_message_ensure_property_map (message);
+
+ return message->property_map;
+}
+
+notmuch_bool_t
+_notmuch_message_frozen (notmuch_message_t *message)
+{
+ return message->frozen;
+}
diff --git a/lib/notmuch.h b/lib/notmuch.h
index bd977c3..c9e654e 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -180,6 +180,11 @@ typedef enum _notmuch_status {
*/
NOTMUCH_STATUS_PATH_ERROR,
/**
+ * One of the arguments violates the preconditions for the
+ * function, in a way not covered by a more specific argument.
+ */
+ NOTMUCH_STATUS_ILLEGAL_ARGUMENT,
+ /**
* Not an actual status value. Just a way to find out how many
* valid status values there are.
*/
@@ -1651,6 +1656,15 @@ notmuch_message_thaw (notmuch_message_t *message);
void
notmuch_message_destroy (notmuch_message_t *message);
+notmuch_status_t
+notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value);
+
+notmuch_status_t
+notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value);
+
+notmuch_status_t
+notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value);
+
/**
* Is the given 'tags' iterator pointing at a valid tag.
*
diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh
new file mode 100755
index 0000000..45ed66b
--- /dev/null
+++ b/test/T610-message-property.sh
@@ -0,0 +1,94 @@
+#!/usr/bin/env bash
+test_description="message property API"
+
+. ./test-lib.sh || exit 1
+
+add_email_corpus
+
+cat <<EOF > c_head
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <notmuch.h>
+
+void run(int line, notmuch_status_t ret)
+{
+ if (ret) {
+ fprintf (stderr, "line %d: %s\n", line, ret);
+ exit (1);
+ }
+}
+
+#define RUN(v) run(__LINE__, v);
+
+int main (int argc, char** argv)
+{
+ notmuch_database_t *db;
+ notmuch_message_t *message = NULL;
+ const char *val;
+ notmuch_status_t stat;
+
+ RUN(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db));
+ RUN(notmuch_database_find_message(db, "4EFC743A.3060609@april.org", &message));
+ if (message == NULL) {
+ fprintf (stderr, "unable to find message");
+ exit (1);
+ }
+EOF
+
+cat <<EOF > c_tail
+ RUN(notmuch_database_destroy(db));
+}
+EOF
+
+test_begin_subtest "notmuch_message_{add,get,remove}_property"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+{
+ RUN(notmuch_message_add_property (message, "testkey1", "testvalue1"));
+ RUN(notmuch_message_get_property (message, "testkey1", &val));
+ printf("testkey1[1] = %s\n", val);
+ RUN(notmuch_message_add_property (message, "testkey2", "testvalue2"));
+ RUN(notmuch_message_get_property (message, "testkey1", &val));
+ printf("testkey1[2] = %s\n", val);
+ RUN(notmuch_message_get_property (message, "testkey1", &val));
+
+ RUN(notmuch_message_get_property (message, "testkey2", &val));
+ printf("testkey2 = %s\n", val);
+
+ /* Add second value for key */
+ RUN(notmuch_message_add_property (message, "testkey2", "testvalue3"));
+ RUN(notmuch_message_get_property (message, "testkey2", &val));
+ printf("testkey2 = %s\n", val);
+
+ /* remove first value for key */
+ RUN(notmuch_message_remove_property (message, "testkey2", "testvalue2"));
+ RUN(notmuch_message_get_property (message, "testkey2", &val));
+ printf("testkey2 = %s\n", val);
+
+ /* remove non-existant value for key */
+ RUN(notmuch_message_remove_property (message, "testkey2", "testvalue2"));
+ RUN(notmuch_message_get_property (message, "testkey2", &val));
+ printf("testkey2 = %s\n", val);
+
+ /* remove only value for key */
+ RUN(notmuch_message_remove_property (message, "testkey2", "testvalue3"));
+ RUN(notmuch_message_get_property (message, "testkey2", &val));
+ printf("testkey2 = %s\n", val == NULL ? "NULL" : val);
+}
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+testkey1[1] = testvalue1
+testkey1[2] = testvalue1
+testkey2 = testvalue2
+testkey2 = testvalue2
+testkey2 = testvalue3
+testkey2 = testvalue3
+testkey2 = NULL
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+
+
+test_done
--
2.8.1
next prev parent reply other threads:[~2016-05-30 11:50 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-05-22 14:28 RFC: message property API David Bremner
2016-05-22 14:28 ` [RFC patch 1/2] lib: refactor _notmuch_message_has_term David Bremner
2016-05-22 14:28 ` [RFC patch 2/2] RFC message-property API David Bremner
2016-05-30 11:49 ` message properties, round 2 David Bremner
2016-05-30 11:49 ` [RFC2 Patch 1/5] lib: read "property" terms from messages David Bremner
2016-05-30 11:49 ` [RFC2 Patch 2/5] lib: private string map (associative array) API David Bremner
2016-05-30 11:49 ` David Bremner [this message]
2016-05-30 11:49 ` [RFC2 Patch 4/5] lib: extend private string map API with iterators David Bremner
2016-05-30 11:49 ` [RFC2 Patch 5/5] lib: iterator API for message properties David Bremner
2016-06-01 1:12 ` David Bremner
2016-06-01 1:52 ` Daniel Kahn Gillmor
2016-06-01 5:04 ` Tomi Ollila
2016-06-01 10:04 ` David Bremner
2016-06-01 14:13 ` Daniel Kahn Gillmor
2016-06-01 23:29 ` David Bremner
2016-06-02 17:33 ` Daniel Kahn Gillmor
2016-06-03 12:54 ` David Bremner
2016-06-03 14:38 ` Daniel Kahn Gillmor
2016-06-03 23:12 ` David Bremner
2016-06-04 16:23 ` Daniel Kahn Gillmor
2016-06-05 10:24 ` [PATCH] doc: document notmuch-dump header line David Bremner
2016-06-05 22:23 ` David Bremner
2016-06-06 6:38 ` Tomi Ollila
2016-06-07 10:55 ` David Bremner
2016-06-01 4:38 ` [RFC2 Patch 5/5] lib: iterator API for message properties Tomi Ollila
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://notmuchmail.org/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1464608999-14774-4-git-send-email-david@tethera.net \
--to=david@tethera.net \
--cc=notmuch@notmuchmail.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://yhetil.org/notmuch.git/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).