12de4c8da0a39ce5634039e1ca69a70b3bedd2c0
[notmuch] / bindings / go / src / notmuch / notmuch.go
1 // Wrapper around the notmuch library
2
3 package notmuch
4
5 /*
6 #cgo LDFLAGS: -lnotmuch
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <time.h>
11 #include "notmuch.h"
12 */
13 import "C"
14 import "unsafe"
15
16 // Status codes used for the return values of most functions
17 type Status C.notmuch_status_t
18
19 const (
20         STATUS_SUCCESS Status = iota
21         STATUS_OUT_OF_MEMORY
22         STATUS_READ_ONLY_DATABASE
23         STATUS_XAPIAN_EXCEPTION
24         STATUS_FILE_ERROR
25         STATUS_FILE_NOT_EMAIL
26         STATUS_DUPLICATE_MESSAGE_ID
27         STATUS_NULL_POINTER
28         STATUS_TAG_TOO_LONG
29         STATUS_UNBALANCED_FREEZE_THAW
30         STATUS_UNBALANCED_ATOMIC
31
32         STATUS_LAST_STATUS
33 )
34
35 func (self Status) String() string {
36         var p *C.char
37
38         // p is read-only
39         p = C.notmuch_status_to_string(C.notmuch_status_t(self))
40         if p != nil {
41                 s := C.GoString(p)
42                 return s
43         }
44         return ""
45 }
46
47 /* Various opaque data types. For each notmuch_<foo>_t see the various
48  * notmuch_<foo> functions below. */
49
50 type Database struct {
51         db *C.notmuch_database_t
52 }
53
54 type Query struct {
55         query *C.notmuch_query_t
56 }
57
58 type Threads struct {
59         threads *C.notmuch_threads_t
60 }
61
62 type Thread struct {
63         thread *C.notmuch_thread_t
64 }
65
66 type Messages struct {
67         messages *C.notmuch_messages_t
68 }
69
70 type Message struct {
71         message *C.notmuch_message_t
72 }
73
74 type Tags struct {
75         tags *C.notmuch_tags_t
76 }
77
78 type Directory struct {
79         dir *C.notmuch_directory_t
80 }
81
82 type Filenames struct {
83         fnames *C.notmuch_filenames_t
84 }
85
86 type DatabaseMode C.notmuch_database_mode_t
87
88 const (
89         DATABASE_MODE_READ_ONLY DatabaseMode = 0
90         DATABASE_MODE_READ_WRITE
91 )
92
93 // Create a new, empty notmuch database located at 'path'
94 func NewDatabase(path string) (*Database, Status) {
95
96         var c_path *C.char = C.CString(path)
97         defer C.free(unsafe.Pointer(c_path))
98
99         if c_path == nil {
100                 return nil, STATUS_OUT_OF_MEMORY
101         }
102
103         self := &Database{db: nil}
104         st := Status(C.notmuch_database_create(c_path, &self.db))
105         if st != STATUS_SUCCESS {
106                 return nil, st
107         }
108         return self, st
109 }
110
111 /* Open an existing notmuch database located at 'path'.
112  *
113  * The database should have been created at some time in the past,
114  * (not necessarily by this process), by calling
115  * notmuch_database_create with 'path'. By default the database should be
116  * opened for reading only. In order to write to the database you need to
117  * pass the NOTMUCH_DATABASE_MODE_READ_WRITE mode.
118  *
119  * An existing notmuch database can be identified by the presence of a
120  * directory named ".notmuch" below 'path'.
121  *
122  * The caller should call notmuch_database_destroy when finished with
123  * this database.
124  *
125  * In case of any failure, this function returns NULL, (after printing
126  * an error message on stderr).
127  */
128 func OpenDatabase(path string, mode DatabaseMode) (*Database, Status) {
129
130         var c_path *C.char = C.CString(path)
131         defer C.free(unsafe.Pointer(c_path))
132
133         if c_path == nil {
134                 return nil, STATUS_OUT_OF_MEMORY
135         }
136
137         self := &Database{db: nil}
138         st := Status(C.notmuch_database_open(c_path, C.notmuch_database_mode_t(mode), &self.db))
139         if st != STATUS_SUCCESS {
140                 return nil, st
141         }
142         return self, st
143 }
144
145 /* Close the given notmuch database, freeing all associated
146  * resources. See notmuch_database_open. */
147 func (self *Database) Close() {
148         C.notmuch_database_destroy(self.db)
149 }
150
151 /* Return the database path of the given database.
152  */
153 func (self *Database) GetPath() string {
154
155         /* The return value is a string owned by notmuch so should not be
156          * modified nor freed by the caller. */
157         var p *C.char = C.notmuch_database_get_path(self.db)
158         if p != nil {
159                 s := C.GoString(p)
160                 return s
161         }
162         return ""
163 }
164
165 /* Return the database format version of the given database. */
166 func (self *Database) GetVersion() uint {
167         return uint(C.notmuch_database_get_version(self.db))
168 }
169
170 /* Does this database need to be upgraded before writing to it?
171  *
172  * If this function returns TRUE then no functions that modify the
173  * database (notmuch_database_add_message, notmuch_message_add_tag,
174  * notmuch_directory_set_mtime, etc.) will work unless the function
175  * notmuch_database_upgrade is called successfully first. */
176 func (self *Database) NeedsUpgrade() bool {
177         do_upgrade := C.notmuch_database_needs_upgrade(self.db)
178         if do_upgrade == 0 {
179                 return false
180         }
181         return true
182 }
183
184 // TODO: notmuch_database_upgrade
185
186 /* Retrieve a directory object from the database for 'path'.
187  *
188  * Here, 'path' should be a path relative to the path of 'database'
189  * (see notmuch_database_get_path), or else should be an absolute path
190  * with initial components that match the path of 'database'.
191  *
192  * Can return NULL if a Xapian exception occurs.
193  */
194 func (self *Database) GetDirectory(path string) *Directory {
195         var c_path *C.char = C.CString(path)
196         defer C.free(unsafe.Pointer(c_path))
197
198         if c_path == nil {
199                 return nil
200         }
201
202         c_dir := C.notmuch_database_get_directory(self.db, c_path)
203         if c_dir == nil {
204                 return nil
205         }
206         return &Directory{dir: c_dir}
207 }
208
209 /* Add a new message to the given notmuch database.
210  *
211  * Here,'filename' should be a path relative to the path of
212  * 'database' (see notmuch_database_get_path), or else should be an
213  * absolute filename with initial components that match the path of
214  * 'database'.
215  *
216  * The file should be a single mail message (not a multi-message mbox)
217  * that is expected to remain at its current location, (since the
218  * notmuch database will reference the filename, and will not copy the
219  * entire contents of the file.
220  *
221  * If 'message' is not NULL, then, on successful return '*message'
222  * will be initialized to a message object that can be used for things
223  * such as adding tags to the just-added message. The user should call
224  * notmuch_message_destroy when done with the message. On any failure
225  * '*message' will be set to NULL.
226  *
227  * Return value:
228  *
229  * NOTMUCH_STATUS_SUCCESS: Message successfully added to database.
230  *
231  * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred,
232  *      message not added.
233  *
234  * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: Message has the same message
235  *      ID as another message already in the database. The new
236  *      filename was successfully added to the message in the database
237  *      (if not already present).
238  *
239  * NOTMUCH_STATUS_FILE_ERROR: an error occurred trying to open the
240  *      file, (such as permission denied, or file not found,
241  *      etc.). Nothing added to the database.
242  *
243  * NOTMUCH_STATUS_FILE_NOT_EMAIL: the contents of filename don't look
244  *      like an email message. Nothing added to the database.
245  *
246  * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
247  *      mode so no message can be added.
248  */
249 func (self *Database) AddMessage(fname string) (*Message, Status) {
250         var c_fname *C.char = C.CString(fname)
251         defer C.free(unsafe.Pointer(c_fname))
252
253         if c_fname == nil {
254                 return nil, STATUS_OUT_OF_MEMORY
255         }
256
257         var c_msg *C.notmuch_message_t = new(C.notmuch_message_t)
258         st := Status(C.notmuch_database_add_message(self.db, c_fname, &c_msg))
259
260         return &Message{message: c_msg}, st
261 }
262
263 /* Remove a message from the given notmuch database.
264  *
265  * Note that only this particular filename association is removed from
266  * the database. If the same message (as determined by the message ID)
267  * is still available via other filenames, then the message will
268  * persist in the database for those filenames. When the last filename
269  * is removed for a particular message, the database content for that
270  * message will be entirely removed.
271  *
272  * Return value:
273  *
274  * NOTMUCH_STATUS_SUCCESS: The last filename was removed and the
275  *      message was removed from the database.
276  *
277  * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred,
278  *      message not removed.
279  *
280  * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: This filename was removed but
281  *      the message persists in the database with at least one other
282  *      filename.
283  *
284  * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
285  *      mode so no message can be removed.
286  */
287 func (self *Database) RemoveMessage(fname string) Status {
288
289         var c_fname *C.char = C.CString(fname)
290         defer C.free(unsafe.Pointer(c_fname))
291
292         if c_fname == nil {
293                 return STATUS_OUT_OF_MEMORY
294         }
295
296         st := C.notmuch_database_remove_message(self.db, c_fname)
297         return Status(st)
298 }
299
300 /* Find a message with the given message_id.
301  *
302  * If the database contains a message with the given message_id, then
303  * a new notmuch_message_t object is returned. The caller should call
304  * notmuch_message_destroy when done with the message.
305  *
306  * This function returns NULL in the following situations:
307  *
308  *      * No message is found with the given message_id
309  *      * An out-of-memory situation occurs
310  *      * A Xapian exception occurs
311  */
312 func (self *Database) FindMessage(message_id string) (*Message, Status) {
313
314         var c_msg_id *C.char = C.CString(message_id)
315         defer C.free(unsafe.Pointer(c_msg_id))
316
317         if c_msg_id == nil {
318                 return nil, STATUS_OUT_OF_MEMORY
319         }
320
321         msg := &Message{message: nil}
322         st := Status(C.notmuch_database_find_message(self.db, c_msg_id, &msg.message))
323         if st != STATUS_SUCCESS {
324                 return nil, st
325         }
326         return msg, st
327 }
328
329 /* Return a list of all tags found in the database.
330  *
331  * This function creates a list of all tags found in the database. The
332  * resulting list contains all tags from all messages found in the database.
333  *
334  * On error this function returns NULL.
335  */
336 func (self *Database) GetAllTags() *Tags {
337         tags := C.notmuch_database_get_all_tags(self.db)
338         if tags == nil {
339                 return nil
340         }
341         return &Tags{tags: tags}
342 }
343
344 /* Create a new query for 'database'.
345  *
346  * Here, 'database' should be an open database, (see
347  * notmuch_database_open and notmuch_database_create).
348  *
349  * For the query string, we'll document the syntax here more
350  * completely in the future, but it's likely to be a specialized
351  * version of the general Xapian query syntax:
352  *
353  * http://xapian.org/docs/queryparser.html
354  *
355  * As a special case, passing either a length-zero string, (that is ""),
356  * or a string consisting of a single asterisk (that is "*"), will
357  * result in a query that returns all messages in the database.
358  *
359  * See notmuch_query_set_sort for controlling the order of results.
360  * See notmuch_query_search_messages and notmuch_query_search_threads
361  * to actually execute the query.
362  *
363  * User should call notmuch_query_destroy when finished with this
364  * query.
365  *
366  * Will return NULL if insufficient memory is available.
367  */
368 func (self *Database) CreateQuery(query string) *Query {
369
370         var c_query *C.char = C.CString(query)
371         defer C.free(unsafe.Pointer(c_query))
372
373         if c_query == nil {
374                 return nil
375         }
376
377         q := C.notmuch_query_create(self.db, c_query)
378         if q == nil {
379                 return nil
380         }
381         return &Query{query: q}
382 }
383
384 /* Sort values for notmuch_query_set_sort */
385 type Sort C.notmuch_sort_t
386
387 const (
388         SORT_OLDEST_FIRST Sort = 0
389         SORT_NEWEST_FIRST
390         SORT_MESSAGE_ID
391         SORT_UNSORTED
392 )
393
394 /* Return the query_string of this query. See notmuch_query_create. */
395 func (self *Query) String() string {
396         // FIXME: do we own 'q' or not ?
397         q := C.notmuch_query_get_query_string(self.query)
398         //defer C.free(unsafe.Pointer(q))
399
400         if q != nil {
401                 s := C.GoString(q)
402                 return s
403         }
404
405         return ""
406 }
407
408 /* Specify the sorting desired for this query. */
409 func (self *Query) SetSort(sort Sort) {
410         C.notmuch_query_set_sort(self.query, C.notmuch_sort_t(sort))
411 }
412
413 /* Return the sort specified for this query. See notmuch_query_set_sort. */
414 func (self *Query) GetSort() Sort {
415         return Sort(C.notmuch_query_get_sort(self.query))
416 }
417
418 /* Execute a query for threads, returning a notmuch_threads_t object
419  * which can be used to iterate over the results. The returned threads
420  * object is owned by the query and as such, will only be valid until
421  * notmuch_query_destroy.
422  *
423  * Typical usage might be:
424  *
425  *     notmuch_query_t *query;
426  *     notmuch_threads_t *threads;
427  *     notmuch_thread_t *thread;
428  *
429  *     query = notmuch_query_create (database, query_string);
430  *
431  *     for (threads = notmuch_query_search_threads (query);
432  *          notmuch_threads_valid (threads);
433  *          notmuch_threads_move_to_next (threads))
434  *     {
435  *         thread = notmuch_threads_get (threads);
436  *         ....
437  *         notmuch_thread_destroy (thread);
438  *     }
439  *
440  *     notmuch_query_destroy (query);
441  *
442  * Note: If you are finished with a thread before its containing
443  * query, you can call notmuch_thread_destroy to clean up some memory
444  * sooner (as in the above example). Otherwise, if your thread objects
445  * are long-lived, then you don't need to call notmuch_thread_destroy
446  * and all the memory will still be reclaimed when the query is
447  * destroyed.
448  *
449  * Note that there's no explicit destructor needed for the
450  * notmuch_threads_t object. (For consistency, we do provide a
451  * notmuch_threads_destroy function, but there's no good reason
452  * to call it if the query is about to be destroyed).
453  *
454  * If a Xapian exception occurs this function will return NULL.
455  */
456 func (self *Query) SearchThreads() *Threads {
457         threads := C.notmuch_query_search_threads(self.query)
458         if threads == nil {
459                 return nil
460         }
461         return &Threads{threads: threads}
462 }
463
464 /* Execute a query for messages, returning a notmuch_messages_t object
465  * which can be used to iterate over the results. The returned
466  * messages object is owned by the query and as such, will only be
467  * valid until notmuch_query_destroy.
468  *
469  * Typical usage might be:
470  *
471  *     notmuch_query_t *query;
472  *     notmuch_messages_t *messages;
473  *     notmuch_message_t *message;
474  *
475  *     query = notmuch_query_create (database, query_string);
476  *
477  *     for (messages = notmuch_query_search_messages (query);
478  *          notmuch_messages_valid (messages);
479  *          notmuch_messages_move_to_next (messages))
480  *     {
481  *         message = notmuch_messages_get (messages);
482  *         ....
483  *         notmuch_message_destroy (message);
484  *     }
485  *
486  *     notmuch_query_destroy (query);
487  *
488  * Note: If you are finished with a message before its containing
489  * query, you can call notmuch_message_destroy to clean up some memory
490  * sooner (as in the above example). Otherwise, if your message
491  * objects are long-lived, then you don't need to call
492  * notmuch_message_destroy and all the memory will still be reclaimed
493  * when the query is destroyed.
494  *
495  * Note that there's no explicit destructor needed for the
496  * notmuch_messages_t object. (For consistency, we do provide a
497  * notmuch_messages_destroy function, but there's no good
498  * reason to call it if the query is about to be destroyed).
499  *
500  * If a Xapian exception occurs this function will return NULL.
501  */
502 func (self *Query) SearchMessages() *Messages {
503         msgs := C.notmuch_query_search_messages(self.query)
504         if msgs == nil {
505                 return nil
506         }
507         return &Messages{messages: msgs}
508 }
509
510 /* Destroy a notmuch_query_t along with any associated resources.
511  *
512  * This will in turn destroy any notmuch_threads_t and
513  * notmuch_messages_t objects generated by this query, (and in
514  * turn any notmuch_thread_t and notmuch_message_t objects generated
515  * from those results, etc.), if such objects haven't already been
516  * destroyed.
517  */
518 func (self *Query) Destroy() {
519         if self.query != nil {
520                 C.notmuch_query_destroy(self.query)
521         }
522 }
523
524 /* Return an estimate of the number of messages matching a search
525  *
526  * This function performs a search and returns Xapian's best
527  * guess as to number of matching messages.
528  *
529  * If a Xapian exception occurs, this function may return 0 (after
530  * printing a message).
531  */
532 func (self *Query) CountMessages() uint {
533         return uint(C.notmuch_query_count_messages(self.query))
534 }
535
536 // TODO: wrap threads and thread
537
538 /* Is the given 'threads' iterator pointing at a valid thread.
539  *
540  * When this function returns TRUE, notmuch_threads_get will return a
541  * valid object. Whereas when this function returns FALSE,
542  * notmuch_threads_get will return NULL.
543  *
544  * See the documentation of notmuch_query_search_threads for example
545  * code showing how to iterate over a notmuch_threads_t object.
546  */
547 func (self *Threads) Valid() bool {
548         if self.threads == nil {
549                 return false
550         }
551         valid := C.notmuch_threads_valid(self.threads)
552         if valid == 0 {
553                 return false
554         }
555         return true
556 }
557
558 /* Destroy a notmuch_threads_t object.
559  *
560  * It's not strictly necessary to call this function. All memory from
561  * the notmuch_threads_t object will be reclaimed when the
562  * containg query object is destroyed.
563  */
564 func (self *Threads) Destroy() {
565         if self.threads != nil {
566                 C.notmuch_threads_destroy(self.threads)
567         }
568 }
569
570 /* Is the given 'messages' iterator pointing at a valid message.
571  *
572  * When this function returns TRUE, notmuch_messages_get will return a
573  * valid object. Whereas when this function returns FALSE,
574  * notmuch_messages_get will return NULL.
575  *
576  * See the documentation of notmuch_query_search_messages for example
577  * code showing how to iterate over a notmuch_messages_t object.
578  */
579 func (self *Messages) Valid() bool {
580         if self.messages == nil {
581                 return false
582         }
583         valid := C.notmuch_messages_valid(self.messages)
584         if valid == 0 {
585                 return false
586         }
587         return true
588 }
589
590 /* Get the current message from 'messages' as a notmuch_message_t.
591  *
592  * Note: The returned message belongs to 'messages' and has a lifetime
593  * identical to it (and the query to which it belongs).
594  *
595  * See the documentation of notmuch_query_search_messages for example
596  * code showing how to iterate over a notmuch_messages_t object.
597  *
598  * If an out-of-memory situation occurs, this function will return
599  * NULL.
600  */
601 func (self *Messages) Get() *Message {
602         if self.messages == nil {
603                 return nil
604         }
605         msg := C.notmuch_messages_get(self.messages)
606         if msg == nil {
607                 return nil
608         }
609         return &Message{message: msg}
610 }
611
612 /* Move the 'messages' iterator to the next message.
613  *
614  * If 'messages' is already pointing at the last message then the
615  * iterator will be moved to a point just beyond that last message,
616  * (where notmuch_messages_valid will return FALSE and
617  * notmuch_messages_get will return NULL).
618  *
619  * See the documentation of notmuch_query_search_messages for example
620  * code showing how to iterate over a notmuch_messages_t object.
621  */
622 func (self *Messages) MoveToNext() {
623         if self.messages == nil {
624                 return
625         }
626         C.notmuch_messages_move_to_next(self.messages)
627 }
628
629 /* Destroy a notmuch_messages_t object.
630  *
631  * It's not strictly necessary to call this function. All memory from
632  * the notmuch_messages_t object will be reclaimed when the containing
633  * query object is destroyed.
634  */
635 func (self *Messages) Destroy() {
636         if self.messages != nil {
637                 C.notmuch_messages_destroy(self.messages)
638         }
639 }
640
641 /* Return a list of tags from all messages.
642  *
643  * The resulting list is guaranteed not to contain duplicated tags.
644  *
645  * WARNING: You can no longer iterate over messages after calling this
646  * function, because the iterator will point at the end of the list.
647  * We do not have a function to reset the iterator yet and the only
648  * way how you can iterate over the list again is to recreate the
649  * message list.
650  *
651  * The function returns NULL on error.
652  */
653 func (self *Messages) CollectTags() *Tags {
654         if self.messages == nil {
655                 return nil
656         }
657         tags := C.notmuch_messages_collect_tags(self.messages)
658         if tags == nil {
659                 return nil
660         }
661         return &Tags{tags: tags}
662 }
663
664 /* Get the message ID of 'message'.
665  *
666  * The returned string belongs to 'message' and as such, should not be
667  * modified by the caller and will only be valid for as long as the
668  * message is valid, (which is until the query from which it derived
669  * is destroyed).
670  *
671  * This function will not return NULL since Notmuch ensures that every
672  * message has a unique message ID, (Notmuch will generate an ID for a
673  * message if the original file does not contain one).
674  */
675 func (self *Message) GetMessageId() string {
676
677         if self.message == nil {
678                 return ""
679         }
680         id := C.notmuch_message_get_message_id(self.message)
681         // we dont own id
682         // defer C.free(unsafe.Pointer(id))
683         if id == nil {
684                 return ""
685         }
686         return C.GoString(id)
687 }
688
689 /* Get the thread ID of 'message'.
690  *
691  * The returned string belongs to 'message' and as such, should not be
692  * modified by the caller and will only be valid for as long as the
693  * message is valid, (for example, until the user calls
694  * notmuch_message_destroy on 'message' or until a query from which it
695  * derived is destroyed).
696  *
697  * This function will not return NULL since Notmuch ensures that every
698  * message belongs to a single thread.
699  */
700 func (self *Message) GetThreadId() string {
701
702         if self.message == nil {
703                 return ""
704         }
705         id := C.notmuch_message_get_thread_id(self.message)
706         // we dont own id
707         // defer C.free(unsafe.Pointer(id))
708
709         if id == nil {
710                 return ""
711         }
712
713         return C.GoString(id)
714 }
715
716 /* Get a notmuch_messages_t iterator for all of the replies to
717  * 'message'.
718  *
719  * Note: This call only makes sense if 'message' was ultimately
720  * obtained from a notmuch_thread_t object, (such as by coming
721  * directly from the result of calling notmuch_thread_get_
722  * toplevel_messages or by any number of subsequent
723  * calls to notmuch_message_get_replies).
724  *
725  * If 'message' was obtained through some non-thread means, (such as
726  * by a call to notmuch_query_search_messages), then this function
727  * will return NULL.
728  *
729  * If there are no replies to 'message', this function will return
730  * NULL. (Note that notmuch_messages_valid will accept that NULL
731  * value as legitimate, and simply return FALSE for it.)
732  */
733 func (self *Message) GetReplies() *Messages {
734         if self.message == nil {
735                 return nil
736         }
737         msgs := C.notmuch_message_get_replies(self.message)
738         if msgs == nil {
739                 return nil
740         }
741         return &Messages{messages: msgs}
742 }
743
744 /* Get a filename for the email corresponding to 'message'.
745  *
746  * The returned filename is an absolute filename, (the initial
747  * component will match notmuch_database_get_path() ).
748  *
749  * The returned string belongs to the message so should not be
750  * modified or freed by the caller (nor should it be referenced after
751  * the message is destroyed).
752  *
753  * Note: If this message corresponds to multiple files in the mail
754  * store, (that is, multiple files contain identical message IDs),
755  * this function will arbitrarily return a single one of those
756  * filenames.
757  */
758 func (self *Message) GetFileName() string {
759         if self.message == nil {
760                 return ""
761         }
762         fname := C.notmuch_message_get_filename(self.message)
763         // we dont own fname
764         // defer C.free(unsafe.Pointer(fname))
765
766         if fname == nil {
767                 return ""
768         }
769
770         return C.GoString(fname)
771 }
772
773 type Flag C.notmuch_message_flag_t
774
775 const (
776         MESSAGE_FLAG_MATCH Flag = 0
777 )
778
779 /* Get a value of a flag for the email corresponding to 'message'. */
780 func (self *Message) GetFlag(flag Flag) bool {
781         if self.message == nil {
782                 return false
783         }
784         v := C.notmuch_message_get_flag(self.message, C.notmuch_message_flag_t(flag))
785         if v == 0 {
786                 return false
787         }
788         return true
789 }
790
791 /* Set a value of a flag for the email corresponding to 'message'. */
792 func (self *Message) SetFlag(flag Flag, value bool) {
793         if self.message == nil {
794                 return
795         }
796         var v C.notmuch_bool_t = 0
797         if value {
798                 v = 1
799         }
800         C.notmuch_message_set_flag(self.message, C.notmuch_message_flag_t(flag), v)
801 }
802
803 // TODO: wrap notmuch_message_get_date
804
805 /* Get the value of the specified header from 'message'.
806  *
807  * The value will be read from the actual message file, not from the
808  * notmuch database. The header name is case insensitive.
809  *
810  * The returned string belongs to the message so should not be
811  * modified or freed by the caller (nor should it be referenced after
812  * the message is destroyed).
813  *
814  * Returns an empty string ("") if the message does not contain a
815  * header line matching 'header'. Returns NULL if any error occurs.
816  */
817 func (self *Message) GetHeader(header string) string {
818         if self.message == nil {
819                 return ""
820         }
821
822         var c_header *C.char = C.CString(header)
823         defer C.free(unsafe.Pointer(c_header))
824
825         /* we dont own value */
826         value := C.notmuch_message_get_header(self.message, c_header)
827         if value == nil {
828                 return ""
829         }
830
831         return C.GoString(value)
832 }
833
834 /* Get the tags for 'message', returning a notmuch_tags_t object which
835  * can be used to iterate over all tags.
836  *
837  * The tags object is owned by the message and as such, will only be
838  * valid for as long as the message is valid, (which is until the
839  * query from which it derived is destroyed).
840  *
841  * Typical usage might be:
842  *
843  *     notmuch_message_t *message;
844  *     notmuch_tags_t *tags;
845  *     const char *tag;
846  *
847  *     message = notmuch_database_find_message (database, message_id);
848  *
849  *     for (tags = notmuch_message_get_tags (message);
850  *          notmuch_tags_valid (tags);
851  *          notmuch_result_move_to_next (tags))
852  *     {
853  *         tag = notmuch_tags_get (tags);
854  *         ....
855  *     }
856  *
857  *     notmuch_message_destroy (message);
858  *
859  * Note that there's no explicit destructor needed for the
860  * notmuch_tags_t object. (For consistency, we do provide a
861  * notmuch_tags_destroy function, but there's no good reason to call
862  * it if the message is about to be destroyed).
863  */
864 func (self *Message) GetTags() *Tags {
865         if self.message == nil {
866                 return nil
867         }
868         tags := C.notmuch_message_get_tags(self.message)
869         if tags == nil {
870                 return nil
871         }
872         return &Tags{tags: tags}
873 }
874
875 /* The longest possible tag value. */
876 const TAG_MAX = 200
877
878 /* Add a tag to the given message.
879  *
880  * Return value:
881  *
882  * NOTMUCH_STATUS_SUCCESS: Tag successfully added to message
883  *
884  * NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL
885  *
886  * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
887  *      (exceeds NOTMUCH_TAG_MAX)
888  *
889  * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
890  *      mode so message cannot be modified.
891  */
892 func (self *Message) AddTag(tag string) Status {
893         if self.message == nil {
894                 return STATUS_NULL_POINTER
895         }
896         c_tag := C.CString(tag)
897         defer C.free(unsafe.Pointer(c_tag))
898
899         return Status(C.notmuch_message_add_tag(self.message, c_tag))
900 }
901
902 /* Remove a tag from the given message.
903  *
904  * Return value:
905  *
906  * NOTMUCH_STATUS_SUCCESS: Tag successfully removed from message
907  *
908  * NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL
909  *
910  * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
911  *      (exceeds NOTMUCH_TAG_MAX)
912  *
913  * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
914  *      mode so message cannot be modified.
915  */
916 func (self *Message) RemoveTag(tag string) Status {
917         if self.message == nil {
918                 return STATUS_NULL_POINTER
919         }
920         c_tag := C.CString(tag)
921         defer C.free(unsafe.Pointer(c_tag))
922
923         return Status(C.notmuch_message_remove_tag(self.message, c_tag))
924 }
925
926 /* Remove all tags from the given message.
927  *
928  * See notmuch_message_freeze for an example showing how to safely
929  * replace tag values.
930  *
931  * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
932  *      mode so message cannot be modified.
933  */
934 func (self *Message) RemoveAllTags() Status {
935         if self.message == nil {
936                 return STATUS_NULL_POINTER
937         }
938         return Status(C.notmuch_message_remove_all_tags(self.message))
939 }
940
941 /* Freeze the current state of 'message' within the database.
942  *
943  * This means that changes to the message state, (via
944  * notmuch_message_add_tag, notmuch_message_remove_tag, and
945  * notmuch_message_remove_all_tags), will not be committed to the
946  * database until the message is thawed with notmuch_message_thaw.
947  *
948  * Multiple calls to freeze/thaw are valid and these calls will
949  * "stack". That is there must be as many calls to thaw as to freeze
950  * before a message is actually thawed.
951  *
952  * The ability to do freeze/thaw allows for safe transactions to
953  * change tag values. For example, explicitly setting a message to
954  * have a given set of tags might look like this:
955  *
956  *    notmuch_message_freeze (message);
957  *
958  *    notmuch_message_remove_all_tags (message);
959  *
960  *    for (i = 0; i < NUM_TAGS; i++)
961  *        notmuch_message_add_tag (message, tags[i]);
962  *
963  *    notmuch_message_thaw (message);
964  *
965  * With freeze/thaw used like this, the message in the database is
966  * guaranteed to have either the full set of original tag values, or
967  * the full set of new tag values, but nothing in between.
968  *
969  * Imagine the example above without freeze/thaw and the operation
970  * somehow getting interrupted. This could result in the message being
971  * left with no tags if the interruption happened after
972  * notmuch_message_remove_all_tags but before notmuch_message_add_tag.
973  *
974  * Return value:
975  *
976  * NOTMUCH_STATUS_SUCCESS: Message successfully frozen.
977  *
978  * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
979  *      mode so message cannot be modified.
980  */
981 func (self *Message) Freeze() Status {
982         if self.message == nil {
983                 return STATUS_NULL_POINTER
984         }
985         return Status(C.notmuch_message_freeze(self.message))
986 }
987
988 /* Thaw the current 'message', synchronizing any changes that may have
989  * occurred while 'message' was frozen into the notmuch database.
990  *
991  * See notmuch_message_freeze for an example of how to use this
992  * function to safely provide tag changes.
993  *
994  * Multiple calls to freeze/thaw are valid and these calls with
995  * "stack". That is there must be as many calls to thaw as to freeze
996  * before a message is actually thawed.
997  *
998  * Return value:
999  *
1000  * NOTMUCH_STATUS_SUCCESS: Message successfully thawed, (or at least
1001  *      its frozen count has successfully been reduced by 1).
1002  *
1003  * NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: An attempt was made to thaw
1004  *      an unfrozen message. That is, there have been an unbalanced
1005  *      number of calls to notmuch_message_freeze and
1006  *      notmuch_message_thaw.
1007  */
1008 func (self *Message) Thaw() Status {
1009         if self.message == nil {
1010                 return STATUS_NULL_POINTER
1011         }
1012
1013         return Status(C.notmuch_message_thaw(self.message))
1014 }
1015
1016 /* Destroy a notmuch_message_t object.
1017  *
1018  * It can be useful to call this function in the case of a single
1019  * query object with many messages in the result, (such as iterating
1020  * over the entire database). Otherwise, it's fine to never call this
1021  * function and there will still be no memory leaks. (The memory from
1022  * the messages get reclaimed when the containing query is destroyed.)
1023  */
1024 func (self *Message) Destroy() {
1025         if self.message == nil {
1026                 return
1027         }
1028         C.notmuch_message_destroy(self.message)
1029 }
1030
1031 /* Is the given 'tags' iterator pointing at a valid tag.
1032  *
1033  * When this function returns TRUE, notmuch_tags_get will return a
1034  * valid string. Whereas when this function returns FALSE,
1035  * notmuch_tags_get will return NULL.
1036  *
1037  * See the documentation of notmuch_message_get_tags for example code
1038  * showing how to iterate over a notmuch_tags_t object.
1039  */
1040 func (self *Tags) Valid() bool {
1041         if self.tags == nil {
1042                 return false
1043         }
1044         v := C.notmuch_tags_valid(self.tags)
1045         if v == 0 {
1046                 return false
1047         }
1048         return true
1049 }
1050
1051 /* Get the current tag from 'tags' as a string.
1052  *
1053  * Note: The returned string belongs to 'tags' and has a lifetime
1054  * identical to it (and the query to which it ultimately belongs).
1055  *
1056  * See the documentation of notmuch_message_get_tags for example code
1057  * showing how to iterate over a notmuch_tags_t object.
1058  */
1059 func (self *Tags) Get() string {
1060         if self.tags == nil {
1061                 return ""
1062         }
1063         s := C.notmuch_tags_get(self.tags)
1064         // we dont own 's'
1065
1066         return C.GoString(s)
1067 }
1068 func (self *Tags) String() string {
1069         return self.Get()
1070 }
1071
1072 /* Move the 'tags' iterator to the next tag.
1073  *
1074  * If 'tags' is already pointing at the last tag then the iterator
1075  * will be moved to a point just beyond that last tag, (where
1076  * notmuch_tags_valid will return FALSE and notmuch_tags_get will
1077  * return NULL).
1078  *
1079  * See the documentation of notmuch_message_get_tags for example code
1080  * showing how to iterate over a notmuch_tags_t object.
1081  */
1082 func (self *Tags) MoveToNext() {
1083         if self.tags == nil {
1084                 return
1085         }
1086         C.notmuch_tags_move_to_next(self.tags)
1087 }
1088
1089 /* Destroy a notmuch_tags_t object.
1090  *
1091  * It's not strictly necessary to call this function. All memory from
1092  * the notmuch_tags_t object will be reclaimed when the containing
1093  * message or query objects are destroyed.
1094  */
1095 func (self *Tags) Destroy() {
1096         if self.tags == nil {
1097                 return
1098         }
1099         C.notmuch_tags_destroy(self.tags)
1100 }
1101
1102 // TODO: wrap notmuch_directory_<fct>
1103
1104 /* Destroy a notmuch_directory_t object. */
1105 func (self *Directory) Destroy() {
1106         if self.dir == nil {
1107                 return
1108         }
1109         C.notmuch_directory_destroy(self.dir)
1110 }
1111
1112 // TODO: wrap notmuch_filenames_<fct>
1113
1114 /* Destroy a notmuch_filenames_t object.
1115  *
1116  * It's not strictly necessary to call this function. All memory from
1117  * the notmuch_filenames_t object will be reclaimed when the
1118  * containing directory object is destroyed.
1119  *
1120  * It is acceptable to pass NULL for 'filenames', in which case this
1121  * function will do nothing.
1122  */
1123 func (self *Filenames) Destroy() {
1124         if self.fnames == nil {
1125                 return
1126         }
1127         C.notmuch_filenames_destroy(self.fnames)
1128 }
1129
1130 /* EOF */