[WIP patch 1/9] lib: initial API for prefixed metadata
David Bremner
david at tethera.net
Sat Jan 9 18:51:33 PST 2016
Start with get and set of a single key
---
lib/Makefile.local | 1 +
lib/metadata.cc | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++
lib/notmuch.h | 25 +++++++++
test/T590-metadata.sh | 58 +++++++++++++++++++
4 files changed, 234 insertions(+)
create mode 100644 lib/metadata.cc
create mode 100755 test/T590-metadata.sh
diff --git a/lib/Makefile.local b/lib/Makefile.local
index 3a07090..ccc1e49 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -48,6 +48,7 @@ libnotmuch_cxx_srcs = \
$(dir)/index.cc \
$(dir)/message.cc \
$(dir)/query.cc \
+ $(dir)/metadata.cc \
$(dir)/thread.cc
libnotmuch_modules := $(libnotmuch_c_srcs:.c=.o) $(libnotmuch_cxx_srcs:.cc=.o)
diff --git a/lib/metadata.cc b/lib/metadata.cc
new file mode 100644
index 0000000..a068ed1
--- /dev/null
+++ b/lib/metadata.cc
@@ -0,0 +1,150 @@
+/* metadata.cc - API for database metadata
+ *
+ * Copyright © 2015 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 at tethera.net>
+ */
+
+#include "notmuch.h"
+#include "notmuch-private.h"
+#include "database-private.h"
+
+static
+const char *
+_find_metadata_prefix (notmuch_metadata_class_t mclass)
+{
+ switch (mclass) {
+ case NOTMUCH_METADATA_CONFIG:
+ return "C";
+ default:
+ return NULL;
+ }
+}
+
+notmuch_status_t _make_key(void *ctx, notmuch_metadata_class_t mclass,
+ const char *in, const char **out)
+{
+ const char *term;
+ const char *prefix = NULL;
+
+ if (!out)
+ return NOTMUCH_STATUS_NULL_POINTER;
+
+ prefix = _find_metadata_prefix(mclass);
+ if (!prefix)
+ return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;
+
+ term = talloc_asprintf (ctx, "%s%s",
+ prefix, in);
+ if (!term)
+ return NOTMUCH_STATUS_OUT_OF_MEMORY;
+
+ *out = term;
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
+notmuch_status_t
+notmuch_database_set_metadata (notmuch_database_t *notmuch,
+ notmuch_metadata_class_t mclass,
+ const char *key,
+ const char *value)
+{
+ notmuch_status_t status;
+ Xapian::WritableDatabase *db;
+ const char *key_term = NULL;
+ void *local;
+
+ local = talloc_new (NULL);
+
+ status = _notmuch_database_ensure_writable (notmuch);
+ if (status)
+ goto DONE;
+
+ status = _make_key (local, mclass, key, &key_term);
+ if (status)
+ goto DONE;
+
+ try {
+ db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
+ db->set_metadata (key_term, value);
+ } catch (const Xapian::Error &error) {
+ status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+ notmuch->exception_reported = TRUE;
+ if (! notmuch->exception_reported) {
+ _notmuch_database_log (notmuch, "Error: A Xapian exception occurred setting metadata: %s\n",
+ error.get_msg().c_str());
+ }
+ }
+ DONE:
+ talloc_free (local);
+
+ return status;
+}
+
+static notmuch_status_t
+_metadata_value (notmuch_database_t *notmuch,
+ notmuch_metadata_class_t mclass,
+ const char *key,
+ std::string &value)
+{
+ notmuch_status_t status;
+
+ const char *key_term = NULL;
+ void *local;
+
+ local = talloc_new (NULL);
+
+ status = _make_key (local, mclass, key, &key_term);
+ if (status)
+ goto DONE;
+
+ try {
+
+ value = notmuch->xapian_db->get_metadata (key_term);
+
+ } catch (const Xapian::Error &error) {
+ status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+ notmuch->exception_reported = TRUE;
+ if (! notmuch->exception_reported) {
+ _notmuch_database_log (notmuch, "Error: A Xapian exception occurred getting metadata: %s\n",
+ error.get_msg().c_str());
+ }
+ }
+
+ DONE:
+ talloc_free (local);
+ return status;
+}
+
+notmuch_status_t
+notmuch_database_get_metadata (notmuch_database_t *notmuch,
+ notmuch_metadata_class_t mclass,
+ const char *key,
+ char **value) {
+ std::string strval;
+ notmuch_status_t status;
+
+ if (!value)
+ return NOTMUCH_STATUS_NULL_POINTER;
+
+ status = _metadata_value (notmuch, mclass, key, strval);
+ if (status)
+ return status;
+
+ *value = strdup (strval.c_str ());
+
+ return NOTMUCH_STATUS_SUCCESS;
+}
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 310a8b8..448f405 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -1829,6 +1829,31 @@ notmuch_filenames_move_to_next (notmuch_filenames_t *filenames);
void
notmuch_filenames_destroy (notmuch_filenames_t *filenames);
+/**
+ * metadata class
+ */
+
+typedef enum _notmuch_metadata_class {
+ NOTMUCH_METADATA_FIRST_CLASS = 1,
+ NOTMUCH_METADATA_CONFIG = 1,
+ NOTMUCH_METADATA_LAST_CLASS
+} notmuch_metadata_class_t;
+
+/**
+ * set metadata
+ *
+ */
+notmuch_status_t
+notmuch_database_set_metadata (notmuch_database_t *db, notmuch_metadata_class_t mclass, const char *key, const char *value);
+
+/**
+ * retrieve one metadata value
+ *
+ * return value is allocated by malloc and should be freed by the caller.
+ */
+notmuch_status_t
+notmuch_database_get_metadata (notmuch_database_t *db, notmuch_metadata_class_t mclass, const char *key, char **value);
+
/* @} */
NOTMUCH_END_DECLS
diff --git a/test/T590-metadata.sh b/test/T590-metadata.sh
new file mode 100755
index 0000000..29aeaa2
--- /dev/null
+++ b/test/T590-metadata.sh
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+test_description="metadata 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;
+ char *val;
+ notmuch_status_t stat;
+
+ RUN(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db));
+
+EOF
+
+cat <<EOF > c_tail
+ RUN(notmuch_database_destroy(db));
+}
+EOF
+
+test_begin_subtest "notmuch_database_{set,get}_metadata"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+{
+ RUN(notmuch_database_set_metadata (db, NOTMUCH_METADATA_CONFIG, "testkey1", "testvalue1"));
+ RUN(notmuch_database_set_metadata (db, NOTMUCH_METADATA_CONFIG, "testkey2", "testvalue2"));
+ RUN(notmuch_database_get_metadata (db, NOTMUCH_METADATA_CONFIG, "testkey1", &val));
+ printf("testkey1 = %s\n", val);
+ RUN(notmuch_database_get_metadata (db, NOTMUCH_METADATA_CONFIG, "testkey2", &val));
+ printf("testkey2 = %s\n", val);
+}
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+testkey1 = testvalue1
+testkey2 = testvalue2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_done
--
2.6.4
More information about the notmuch
mailing list