Add an initial implementation of a notmuch_thread_t object.

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.
This commit is contained in:
Carl Worth 2009-10-25 23:12:20 -07:00
parent cd467cafb5
commit 1ba3d46fab
6 changed files with 280 additions and 27 deletions

View file

@ -18,6 +18,7 @@ MODULES= \
message-file.o \
query.o \
sha1.o \
thread.o \
libsha1.o \
xutil.o

View 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 *

View file

@ -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);
thread = notmuch_thread_results_get (results);
printf ("%s (", notmuch_message_get_message_id (message));
printf ("%s\n", notmuch_thread_get_thread_id (thread));
for (tags = notmuch_message_get_tags (message);
notmuch_tags_has_more (tags);
notmuch_tags_advance (tags))
{
if (! first)
printf (" ");
printf ("%s", notmuch_tags_get (tags));
first = 0;
}
printf (")\n");
notmuch_message_destroy (message);
notmuch_thread_destroy (thread);
}
notmuch_query_destroy (query);

107
notmuch.h
View file

@ -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.
*

View file

@ -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);
}

73
thread.cc Normal file
View file

@ -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);
}