Add an initial implementation of a notmuch_thread_t object.
authorCarl Worth <cworth@cworth.org>
Mon, 26 Oct 2009 06:12:20 +0000 (23:12 -0700)
committerCarl Worth <cworth@cworth.org>
Mon, 26 Oct 2009 06:12:20 +0000 (23:12 -0700)
We've now got a new notmuch_query_search_threads and a
notmuch_threads_result_t iterator. The thread object itself
doesn't do much yet, (just allows one to get the thread_id),
but that's at least enough to see that "notmuch search" is
actually doing something now, (since it has been converted
to print thread IDs instead of message IDs).

And maybe that's all we need. Getting the messages belonging
to a thread is as simple as a notmuch_query_search_messages
with a string of "thread:<thread-id>".

Though it would be convenient to add notmuch_thread_get_messages
which could use the existing notmuch_message_results_t iterator.

Now we just need an implementation of "notmuch show" and we'll
have something somewhat usable.

Makefile
notmuch-private.h
notmuch.c
notmuch.h
query.cc
thread.cc [new file with mode: 0644]

index 6af7faf510ed15f1cd86bdcbe2ea5bc490c84334..747408a6b64313ecd672f95b98bb95a71080e0ed 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,7 @@ MODULES=              \
        message-file.o  \
        query.o         \
        sha1.o          \
+       thread.o        \
        libsha1.o       \
        xutil.o
 
index aac67b2e4bcf51737bb9b5be6452380f3b5fc33f..d257f2b92c52294efe235d3d6377ce9750c82f72 100644 (file)
@@ -137,6 +137,13 @@ typedef enum _notmuch_private_status {
      :                                                                 \
      (notmuch_status_t) private_status)
 
+/* thread.cc */
+
+notmuch_thread_t *
+_notmuch_thread_create (const void *talloc_owner,
+                       notmuch_database_t *notmuch,
+                       const char *thread_id);
+
 /* message.cc */
 
 notmuch_message_t *
index 47969e1f542b6e6da612e586e2f23728ffe61cf4..5ab155e51f930aad9ed789fd714c6a560b823aa2 100644 (file)
--- a/notmuch.c
+++ b/notmuch.c
@@ -608,9 +608,8 @@ search_command (int argc, char *argv[])
     void *local = talloc_new (NULL);
     notmuch_database_t *notmuch = NULL;
     notmuch_query_t *query;
-    notmuch_message_results_t *results;
-    notmuch_message_t *message;
-    notmuch_tags_t *tags;
+    notmuch_thread_results_t *results;
+    notmuch_thread_t *thread;
     char *query_str;
     int i;
     notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
@@ -638,30 +637,15 @@ search_command (int argc, char *argv[])
        goto DONE;
     }
 
-    for (results = notmuch_query_search_messages (query);
-        notmuch_message_results_has_more (results);
-        notmuch_message_results_advance (results))
+    for (results = notmuch_query_search_threads (query);
+        notmuch_thread_results_has_more (results);
+        notmuch_thread_results_advance (results))
     {
-       int first = 1;
-       message = notmuch_message_results_get (results);
-
-       printf ("%s (", notmuch_message_get_message_id (message));
-
-       for (tags = notmuch_message_get_tags (message);
-            notmuch_tags_has_more (tags);
-            notmuch_tags_advance (tags))
-       {
-           if (! first)
-               printf (" ");
+       thread = notmuch_thread_results_get (results);
 
-           printf ("%s", notmuch_tags_get (tags));
+       printf ("%s\n", notmuch_thread_get_thread_id (thread));
 
-           first = 0;
-       }
-
-       printf (")\n");
-
-       notmuch_message_destroy (message);
+       notmuch_thread_destroy (thread);
     }
 
     notmuch_query_destroy (query);
index d383e7d8af1bf6b6aca4ec8b6809ab0a7f7c0614..522bf1b97eeafa833dc316bf7fd6c5a41b7ec89a 100644 (file)
--- a/notmuch.h
+++ b/notmuch.h
@@ -102,6 +102,8 @@ notmuch_status_to_string (notmuch_status_t status);
  * notmuch_<foo> functions below. */
 typedef struct _notmuch_database notmuch_database_t;
 typedef struct _notmuch_query notmuch_query_t;
+typedef struct _notmuch_thread_results notmuch_thread_results_t;
+typedef struct _notmuch_thread notmuch_thread_t;
 typedef struct _notmuch_message_results notmuch_message_results_t;
 typedef struct _notmuch_message notmuch_message_t;
 typedef struct _notmuch_tags notmuch_tags_t;
@@ -313,6 +315,45 @@ typedef enum {
 void
 notmuch_query_set_sort (notmuch_query_t *query, notmuch_sort_t sort);
 
+/* Execute a query for threads, returning a notmuch_thread_results_t
+ * object which can be used to iterate over the results. The results
+ * object is owned by the query and as such, will only be valid until
+ * notmuch_query_destroy.
+ *
+ * Typical usage might be:
+ *
+ *     notmuch_query_t *query;
+ *     notmuch_thread_results_t *results;
+ *     notmuch_thread_t *thread;
+ *
+ *     query = notmuch_query_create (database, query_string);
+ *
+ *     for (results = notmuch_query_search_threads (query);
+ *          notmuch_thread_results_has_more (results);
+ *          notmuch_thread_results_advance (results))
+ *     {
+ *         thread = notmuch_thread_results_get (results);
+ *         ....
+ *         notmuch_thread_destroy (thread);
+ *     }
+ *
+ *     notmuch_query_destroy (query);
+ *
+ * Note: If you are finished with a thread before its containing
+ * query, you can call notmuch_thread_destroy to clean up some memory
+ * sooner (as in the above example). Otherwise, if your thread objects
+ * are long-lived, then you don't need to call notmuch_thread_destroy
+ * and all the memory will still be reclaimed when the query is
+ * destroyed.
+ *
+ * Note that there's no explicit destructor needed for the
+ * notmuch_thread_results_t object. (For consistency, we do provide a
+ * notmuch_thread_results_destroy function, but there's no good reason
+ * to call it if the query is about to be destroyed).
+ */
+notmuch_thread_results_t *
+notmuch_query_search_threads (notmuch_query_t *query);
+
 /* Execute a query for messages, returning a notmuch_message_results_t
  * object which can be used to iterate over the results. The results
  * object is owned by the query and as such, will only be valid until
@@ -354,13 +395,73 @@ notmuch_query_search_messages (notmuch_query_t *query);
 
 /* Destroy a notmuch_query_t along with any associated resources.
  *
- * This will in turn destroy any notmuch_results_t objects generated
- * by this query, (and in turn any notmuch_message_t objects generated
- * from those results, etc.).
+ * This will in turn destroy any notmuch_thread_results_t and
+ * notmuch_message_results_t objects generated by this query, (and in
+ * turn any notmuch_thrad_t and notmuch_message_t objects generated
+ * from those results, etc.), if such objects haven't already been
+ * destroyed.
  */
 void
 notmuch_query_destroy (notmuch_query_t *query);
 
+/* Does the given notmuch_thread_results_t object contain any more
+ * results.
+ *
+ * When this function returns TRUE, notmuch_thread_results_get will
+ * return a valid object. Whereas when this function returns FALSE,
+ * notmuch_thread_results_get will return NULL.
+ *
+ * See the documentation of notmuch_query_search_threads for example
+ * code showing how to iterate over a notmuch_thread_results_t object.
+ */
+notmuch_bool_t
+notmuch_thread_results_has_more (notmuch_thread_results_t *results);
+
+/* Get the current result from 'results' as a notmuch_thread_t.
+ *
+ * Note: The returned thread belongs to 'results' and has a lifetime
+ * identical to it (and the query to which it belongs).
+ *
+ * See the documentation of notmuch_query_search_threads for example
+ * code showing how to iterate over a notmuch_thread_results_t object.
+ *
+ * If an out-of-memory situation occurs, this function will return
+ * NULL.
+ */
+notmuch_thread_t *
+notmuch_thread_results_get (notmuch_thread_results_t *results);
+
+/* Advance the 'results' iterator to the next result.
+ *
+ * See the documentation of notmuch_query_search_threads for example
+ * code showing how to iterate over a notmuch_thread_results_t object.
+ */
+void
+notmuch_thread_results_advance (notmuch_thread_results_t *results);
+
+/* Destroy a notmuch_thread_results_t object.
+ *
+ * It's not strictly necessary to call this function. All memory from
+ * the notmuch_thread_results_t object will be reclaimed when the
+ * containg query object is destroyed.
+ */
+void
+notmuch_thread_results_destroy (notmuch_thread_results_t *results);
+
+/* Get the thread ID of 'thread'.
+ *
+ * The returned string belongs to 'thread' and as such, should not be
+ * modified by the caller and will only be valid for as long as the
+ * thread is valid, (which is until notmuch_thread_destroy or until
+ * the query from which it derived is destroyed).
+ */
+const char *
+notmuch_thread_get_thread_id (notmuch_thread_t *thread);
+
+/* Destroy a notmuch_thread_t object. */
+void
+notmuch_thread_destroy (notmuch_thread_t *thread);
+
 /* Does the given notmuch_message_results_t object contain any more
  * results.
  *
index c153dad940bbae88587094287a594a3fe83464db..b454560ace1723b315d336c9e4165559a3b9b0d6 100644 (file)
--- a/query.cc
+++ b/query.cc
@@ -21,6 +21,8 @@
 #include "notmuch-private.h"
 #include "database-private.h"
 
+#include <glib.h> /* GHashTable, GPtrArray */
+
 #include <xapian.h>
 
 struct _notmuch_query {
@@ -35,6 +37,12 @@ struct _notmuch_message_results {
     Xapian::MSetIterator iterator_end;
 };
 
+struct _notmuch_thread_results {
+    notmuch_database_t *notmuch;
+    GPtrArray *thread_ids;
+    unsigned int index;
+};
+
 notmuch_query_t *
 notmuch_query_create (notmuch_database_t *notmuch,
                      const char *query_string)
@@ -150,6 +158,50 @@ notmuch_query_search_messages (notmuch_query_t *query)
     return results;
 }
 
+notmuch_thread_results_t *
+notmuch_query_search_threads (notmuch_query_t *query)
+{
+    notmuch_thread_results_t *thread_results;
+    notmuch_message_results_t *message_results;
+    notmuch_message_t *message;
+    const char *thread_id;
+    GHashTable *seen;
+
+    thread_results = talloc (query, notmuch_thread_results_t);
+    if (thread_results == NULL)
+       return NULL;
+
+    thread_results->notmuch = query->notmuch;
+    thread_results->thread_ids = g_ptr_array_new ();
+    thread_results->index = 0;
+
+    seen = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                 free, NULL);
+
+    for (message_results = notmuch_query_search_messages (query);
+        notmuch_message_results_has_more (message_results);
+        notmuch_message_results_advance (message_results))
+    {
+       message = notmuch_message_results_get (message_results);
+       thread_id = notmuch_message_get_thread_id (message);
+
+       if (g_hash_table_lookup_extended (seen,
+                                         thread_id, NULL, NULL))
+       {
+           continue;
+       }
+
+       g_hash_table_insert (seen, xstrdup (thread_id), NULL);
+
+       g_ptr_array_add (thread_results->thread_ids,
+                        talloc_strdup (thread_results, thread_id));
+    }
+
+    g_hash_table_unref (seen);
+
+    return thread_results;
+}
+
 void
 notmuch_query_destroy (notmuch_query_t *query)
 {
@@ -195,3 +247,38 @@ notmuch_message_results_destroy (notmuch_message_results_t *results)
 {
     talloc_free (results);
 }
+
+notmuch_bool_t
+notmuch_thread_results_has_more (notmuch_thread_results_t *results)
+{
+    return (results->index < results->thread_ids->len);
+}
+
+notmuch_thread_t *
+notmuch_thread_results_get (notmuch_thread_results_t *results)
+{
+    notmuch_thread_t *thread;
+    const char *thread_id;
+
+    thread_id = (const char *) g_ptr_array_index (results->thread_ids,
+                                                 results->index);
+
+    thread = _notmuch_thread_create (results,
+                                    results->notmuch,
+                                    thread_id);
+
+    return thread;
+}
+
+void
+notmuch_thread_results_advance (notmuch_thread_results_t *results)
+{
+    results->index++;
+}
+
+void
+notmuch_thread_results_destroy (notmuch_thread_results_t *results)
+{
+    g_ptr_array_free (results->thread_ids, TRUE);
+    talloc_free (results);
+}
diff --git a/thread.cc b/thread.cc
new file mode 100644 (file)
index 0000000..8915729
--- /dev/null
+++ b/thread.cc
@@ -0,0 +1,73 @@
+/* thread.cc - Results of thread-based searches from a notmuch database
+ *
+ * Copyright © 2009 Carl Worth
+ *
+ * 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: Carl Worth <cworth@cworth.org>
+ */
+
+#include "notmuch-private.h"
+#include "database-private.h"
+
+#include <xapian.h>
+
+struct _notmuch_thread {
+    notmuch_database_t *notmuch;
+    char *thread_id;
+};
+
+/* Create a new notmuch_thread_t object for an existing document in
+ * the database.
+ *
+ * Here, 'talloc owner' is an optional talloc context to which the new
+ * thread will belong. This allows for the caller to not bother
+ * calling notmuch_thread_destroy on the thread, and know that all
+ * memory will be reclaimed with 'talloc_owner' is freed. The caller
+ * still can call notmuch_thread_destroy when finished with the
+ * thread if desired.
+ *
+ * The 'talloc_owner' argument can also be NULL, in which case the
+ * caller *is* responsible for calling notmuch_thread_destroy.
+ *
+ * This function returns NULL in the case of any error.
+ */
+notmuch_thread_t *
+_notmuch_thread_create (const void *talloc_owner,
+                       notmuch_database_t *notmuch,
+                       const char *thread_id)
+{
+    notmuch_thread_t *thread;
+
+    thread = talloc (talloc_owner, notmuch_thread_t);
+    if (unlikely (thread == NULL))
+       return NULL;
+
+    thread->notmuch = notmuch;
+    thread->thread_id = talloc_strdup (thread, thread_id);
+
+    return thread;
+}
+
+const char *
+notmuch_thread_get_thread_id (notmuch_thread_t *thread)
+{
+    return thread->thread_id;
+}
+
+void
+notmuch_thread_destroy (notmuch_thread_t *thread)
+{
+    talloc_free (thread);
+}