]> git.notmuchmail.org Git - notmuch/blob - bindings/ruby/database.c
Merge in ruby bindings.
[notmuch] / bindings / ruby / database.c
1 /* The Ruby interface to the notmuch mail library
2  *
3  * Copyright © 2010 Ali Polatel
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see http://www.gnu.org/licenses/ .
17  *
18  * Author: Ali Polatel <alip@exherbo.org>
19  */
20
21 #include "defs.h"
22
23 VALUE
24 notmuch_rb_database_alloc(VALUE klass)
25 {
26     return Data_Wrap_Struct(klass, NULL, NULL, NULL);
27 }
28
29 /*
30  * call-seq: Notmuch::Database.new(path [, {:create => false, :mode => Notmuch::MODE_READ_ONLY}]) => DB
31  *
32  * Create or open a notmuch database using the given path.
33  *
34  * If :create is +true+, create the database instead of opening.
35  *
36  * The argument :mode specifies the open mode of the database.
37  */
38 VALUE
39 notmuch_rb_database_initialize(int argc, VALUE *argv, VALUE self)
40 {
41     const char *path;
42     int create, mode;
43     VALUE pathv, hashv;
44     VALUE modev;
45
46 #if !defined(RSTRING_PTR)
47 #define RSTRING_PTR(v) (RSTRING((v))->ptr)
48 #endif /* !defined(RSTRING_PTR) */
49
50     /* Check arguments */
51     rb_scan_args(argc, argv, "11", &pathv, &hashv);
52
53     SafeStringValue(pathv);
54     path = RSTRING_PTR(pathv);
55
56     if (!NIL_P(hashv)) {
57         Check_Type(hashv, T_HASH);
58         create = RTEST(rb_hash_aref(hashv, ID2SYM(ID_db_create)));
59         modev = rb_hash_aref(hashv, ID2SYM(ID_db_mode));
60         if (NIL_P(modev))
61             mode = NOTMUCH_DATABASE_MODE_READ_ONLY;
62         else if (!FIXNUM_P(modev))
63             rb_raise(rb_eTypeError, ":mode isn't a Fixnum");
64         else {
65             mode = FIX2INT(modev);
66             switch (mode) {
67             case NOTMUCH_DATABASE_MODE_READ_ONLY:
68             case NOTMUCH_DATABASE_MODE_READ_WRITE:
69                 break;
70             default:
71                 rb_raise(rb_eTypeError, "Invalid mode");
72             }
73         }
74     }
75     else {
76         create = 0;
77         mode = NOTMUCH_DATABASE_MODE_READ_ONLY;
78     }
79
80     Check_Type(self, T_DATA);
81     DATA_PTR(self) = create ? notmuch_database_create(path) : notmuch_database_open(path, mode);
82     if (!DATA_PTR(self))
83         rb_raise(notmuch_rb_eDatabaseError, "Failed to open database");
84
85     return self;
86 }
87
88 /*
89  * call-seq: Notmuch::Database.open(path [, ahash]) {|db| ...}
90  *
91  * Identical to new, except that when it is called with a block, it yields with
92  * the new instance and closes it, and returns the result which is returned from
93  * the block.
94  */
95 VALUE
96 notmuch_rb_database_open(int argc, VALUE *argv, VALUE klass)
97 {
98     VALUE obj;
99
100     obj = rb_class_new_instance(argc, argv, klass);
101     if (!rb_block_given_p())
102         return obj;
103
104     return rb_ensure(rb_yield, obj, notmuch_rb_database_close, obj);
105 }
106
107 /*
108  * call-seq: DB.close => nil
109  *
110  * Close the notmuch database.
111  */
112 VALUE
113 notmuch_rb_database_close(VALUE self)
114 {
115     notmuch_database_t *db;
116
117     Data_Get_Notmuch_Database(self, db);
118     notmuch_database_close(db);
119     DATA_PTR(self) = NULL;
120
121     return Qnil;
122 }
123
124 /*
125  * call-seq: DB.path => String
126  *
127  * Return the path of the database
128  */
129 VALUE
130 notmuch_rb_database_path(VALUE self)
131 {
132     notmuch_database_t *db;
133
134     Data_Get_Notmuch_Database(self, db);
135
136     return rb_str_new2(notmuch_database_get_path(db));
137 }
138
139 /*
140  * call-seq: DB.version => Fixnum
141  *
142  * Return the version of the database
143  */
144 VALUE
145 notmuch_rb_database_version(VALUE self)
146 {
147     notmuch_database_t *db;
148
149     Data_Get_Notmuch_Database(self, db);
150
151     return INT2FIX(notmuch_database_get_version(db));
152 }
153
154 /*
155  * call-seq: DB.needs_upgrade? => true or false
156  *
157  * Return the +true+ if the database needs upgrading, +false+ otherwise
158  */
159 VALUE
160 notmuch_rb_database_needs_upgrade(VALUE self)
161 {
162     notmuch_database_t *db;
163
164     Data_Get_Notmuch_Database(self, db);
165
166     return notmuch_database_needs_upgrade(db) ? Qtrue : Qfalse;
167 }
168
169 static void
170 notmuch_rb_upgrade_notify(void *closure, double progress)
171 {
172     VALUE *block = (VALUE *)closure;
173     rb_funcall(*block, ID_call, 1, rb_float_new(progress));
174 }
175
176 /*
177  * call-seq: DB.upgrade! [{|progress| block }] => nil
178  *
179  * Upgrade the database.
180  *
181  * If a block is given the block is called with a progress indicator as a
182  * floating point value in the range of [0.0..1.0].
183  */
184 VALUE
185 notmuch_rb_database_upgrade(VALUE self)
186 {
187     notmuch_status_t ret;
188     void (*pnotify) (void *closure, double progress);
189     notmuch_database_t *db;
190     VALUE block;
191
192     Data_Get_Notmuch_Database(self, db);
193
194     if (rb_block_given_p()) {
195         pnotify = notmuch_rb_upgrade_notify;
196         block = rb_block_proc();
197     }
198     else
199         pnotify = NULL;
200
201     ret = notmuch_database_upgrade(db, pnotify, pnotify ? &block : NULL);
202     notmuch_rb_status_raise(ret);
203
204     return Qtrue;
205 }
206
207 /*
208  * call-seq: DB.get_directory(path) => DIR
209  *
210  * Retrieve a directory object from the database for 'path'
211  */
212 VALUE
213 notmuch_rb_database_get_directory(VALUE self, VALUE pathv)
214 {
215     const char *path;
216     notmuch_directory_t *dir;
217     notmuch_database_t *db;
218
219     Data_Get_Notmuch_Database(self, db);
220
221 #if !defined(RSTRING_PTR)
222 #define RSTRING_PTR(v) (RSTRING((v))->ptr)
223 #endif /* !defined(RSTRING_PTR) */
224
225     SafeStringValue(pathv);
226     path = RSTRING_PTR(pathv);
227
228     dir = notmuch_database_get_directory(db, path);
229     if (!dir)
230         rb_raise(notmuch_rb_eXapianError, "Xapian exception");
231
232     return Data_Wrap_Struct(notmuch_rb_cDirectory, NULL, NULL, dir);
233 }
234
235 /*
236  * call-seq: DB.add_message(path) => MESSAGE, isdup
237  *
238  * Add a message to the database and return it.
239  *
240  * +isdup+ is a boolean that specifies whether the added message was a
241  * duplicate.
242  */
243 VALUE
244 notmuch_rb_database_add_message(VALUE self, VALUE pathv)
245 {
246     const char *path;
247     notmuch_status_t ret;
248     notmuch_message_t *message;
249     notmuch_database_t *db;
250
251     Data_Get_Notmuch_Database(self, db);
252
253 #if !defined(RSTRING_PTR)
254 #define RSTRING_PTR(v) (RSTRING((v))->ptr)
255 #endif /* !defined(RSTRING_PTR) */
256
257     SafeStringValue(pathv);
258     path = RSTRING_PTR(pathv);
259
260     ret = notmuch_database_add_message(db, path, &message);
261     notmuch_rb_status_raise(ret);
262     return rb_assoc_new(Data_Wrap_Struct(notmuch_rb_cMessage, NULL, NULL, message),
263         (ret == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) ? Qtrue : Qfalse);
264 }
265
266 /*
267  * call-seq: DB.remove_message(path) => isdup
268  *
269  * Remove a message from the database.
270  *
271  * +isdup+ is a boolean that specifies whether the removed message was a
272  * duplicate.
273  */
274 VALUE
275 notmuch_rb_database_remove_message(VALUE self, VALUE pathv)
276 {
277     const char *path;
278     notmuch_status_t ret;
279     notmuch_database_t *db;
280
281     Data_Get_Notmuch_Database(self, db);
282
283 #if !defined(RSTRING_PTR)
284 #define RSTRING_PTR(v) (RSTRING((v))->ptr)
285 #endif /* !defined(RSTRING_PTR) */
286
287     SafeStringValue(pathv);
288     path = RSTRING_PTR(pathv);
289
290     ret = notmuch_database_remove_message(db, path);
291     notmuch_rb_status_raise(ret);
292     return (ret == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) ? Qtrue : Qfalse;
293 }
294
295 /*
296  * call-seq: DB.query(query) => QUERY
297  *
298  * Retrieve a query object for the query string 'query'
299  */
300 VALUE
301 notmuch_rb_database_query_create(VALUE self, VALUE qstrv)
302 {
303     const char *qstr;
304     notmuch_query_t *query;
305     notmuch_database_t *db;
306
307     Data_Get_Notmuch_Database(self, db);
308
309 #if !defined(RSTRING_PTR)
310 #define RSTRING_PTR(v) (RSTRING((v))->ptr)
311 #endif /* !defined(RSTRING_PTR) */
312
313     SafeStringValue(qstrv);
314     qstr = RSTRING_PTR(qstrv);
315
316     query = notmuch_query_create(db, qstr);
317     if (!query)
318         rb_raise(notmuch_rb_eMemoryError, "Out of memory");
319
320     return Data_Wrap_Struct(notmuch_rb_cQuery, NULL, NULL, query);
321 }