ruby: Kill garbage collection related cruft.
[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 /*
24  * call-seq: Notmuch::Database.new(path, [{:create => false, :mode => notmuch::MODE_READ_ONLY}]) => DB
25  *
26  * Create or open a notmuch database using the given path.
27  * If :create is +true+, create the database instead of opening.
28  * The argument :mode specifies the open mode of the database.
29  */
30 VALUE
31 notmuch_rb_database_new(int argc, VALUE *argv, VALUE klass)
32 {
33     const char *path;
34     int create, mode;
35     notmuch_database_t *db;
36     VALUE modev;
37
38 #if !defined(RSTRING_PTR)
39 #define RSTRING_PTR(v) (RSTRING((v))->ptr)
40 #endif /* !defined(RSTRING_PTR) */
41
42     /* Check arguments */
43     if (argc < 1 || argc > 2)
44         rb_raise(rb_eTypeError, "Wrong number of arguments");
45
46     SafeStringValue(argv[0]);
47     path = RSTRING_PTR(argv[0]);
48
49     if (argc == 2) {
50         Check_Type(argv[1], T_HASH);
51         create = RTEST(rb_hash_aref(argv[1], ID2SYM(ID_db_create)));
52         modev = rb_hash_aref(argv[1], ID2SYM(ID_db_mode));
53         if (NIL_P(modev))
54             mode = NOTMUCH_DATABASE_MODE_READ_ONLY;
55         else if (!FIXNUM_P(modev))
56             rb_raise(rb_eTypeError, ":mode isn't a Fixnum");
57         else {
58             mode = FIX2INT(modev);
59             switch (mode) {
60             case NOTMUCH_DATABASE_MODE_READ_ONLY:
61             case NOTMUCH_DATABASE_MODE_READ_WRITE:
62                 break;
63             default:
64                 rb_raise(rb_eTypeError, "Invalid mode");
65             }
66         }
67     }
68     else {
69         create = 0;
70         mode = NOTMUCH_DATABASE_MODE_READ_ONLY;
71     }
72
73     db = create ? notmuch_database_create(path) : notmuch_database_open(path, mode);
74     if (!db)
75         rb_raise(notmuch_rb_eDatabaseError, "failed to open database");
76
77     return Data_Wrap_Struct(klass, NULL, NULL, db);
78 }
79
80 /*
81  * call-seq: DB.close => nil
82  *
83  * Close the notmuch database.
84  */
85 VALUE
86 notmuch_rb_database_close(VALUE self)
87 {
88     notmuch_database_t *db;
89
90     Data_Get_Struct(self, notmuch_database_t, db);
91     notmuch_database_close(db);
92
93     return Qnil;
94 }
95
96 /*
97  * call-seq: DB.path => String
98  *
99  * Return the path of the database
100  */
101 VALUE
102 notmuch_rb_database_path(VALUE self)
103 {
104     notmuch_database_t *db;
105
106     Data_Get_Struct(self, notmuch_database_t, db);
107
108     return rb_str_new2(notmuch_database_get_path(db));
109 }
110
111 /*
112  * call-seq: DB.version => Fixnum
113  *
114  * Return the version of the database
115  */
116 VALUE
117 notmuch_rb_database_version(VALUE self)
118 {
119     notmuch_database_t *db;
120
121     Data_Get_Struct(self, notmuch_database_t, db);
122
123     return INT2FIX(notmuch_database_get_version(db));
124 }
125
126 /*
127  * call-seq: DB.needs_upgrade? => true or false
128  *
129  * Return the +true+ if the database needs upgrading, +false+ otherwise
130  */
131 VALUE
132 notmuch_rb_database_needs_upgrade(VALUE self)
133 {
134     notmuch_database_t *db;
135
136     Data_Get_Struct(self, notmuch_database_t, db);
137
138     return notmuch_database_needs_upgrade(db) ? Qtrue : Qfalse;
139 }
140
141 static void
142 notmuch_rb_upgrade_notify(void *closure, double progress)
143 {
144     VALUE *block = (VALUE *)closure;
145     rb_funcall(*block, ID_call, 1, rb_float_new(progress));
146 }
147
148 /*
149  * call-seq: DB.upgrade! [{|progress| block }] => nil
150  *
151  * Upgrade the database.
152  *
153  * If a block is given the block is called with a progress indicator as a
154  * floating point value in the range of [0.0..1.0].
155  */
156 VALUE
157 notmuch_rb_database_upgrade(VALUE self)
158 {
159     notmuch_status_t ret;
160     void (*pnotify) (void *closure, double progress);
161     notmuch_database_t *db;
162     VALUE block;
163
164     Data_Get_Struct(self, notmuch_database_t, db);
165
166     if (rb_block_given_p()) {
167         pnotify = notmuch_rb_upgrade_notify;
168         block = rb_block_proc();
169     }
170     else
171         pnotify = NULL;
172
173     ret = notmuch_database_upgrade(db, pnotify, pnotify ? &block : NULL);
174     notmuch_rb_status_raise(ret);
175
176     return Qtrue;
177 }
178
179 /*
180  * call-seq: DB.get_directory(path) => DIR
181  *
182  * Retrieve a directory object from the database for 'path'
183  */
184 VALUE
185 notmuch_rb_database_get_directory(VALUE self, VALUE pathv)
186 {
187     const char *path;
188     notmuch_directory_t *dir;
189     notmuch_database_t *db;
190
191     Data_Get_Struct(self, notmuch_database_t, db);
192
193 #if !defined(RSTRING_PTR)
194 #define RSTRING_PTR(v) (RSTRING((v))->ptr)
195 #endif /* !defined(RSTRING_PTR) */
196
197     SafeStringValue(pathv);
198     path = RSTRING_PTR(pathv);
199
200     dir = notmuch_database_get_directory(db, path);
201     if (!dir)
202         rb_raise(notmuch_rb_eXapianError, "Xapian exception");
203
204     return Data_Wrap_Struct(notmuch_rb_cDirectory, NULL, NULL, dir);
205 }
206
207 /*
208  * call-seq: DB.add_message(path) => MESSAGE, isdup
209  *
210  * Add a message to the database and return it.
211  *
212  * +isdup+ is a boolean that specifies whether the added message was a
213  * duplicate.
214  */
215 VALUE
216 notmuch_rb_database_add_message(VALUE self, VALUE pathv)
217 {
218     const char *path;
219     notmuch_status_t ret;
220     notmuch_message_t *message;
221     notmuch_database_t *db;
222
223     Data_Get_Struct(self, notmuch_database_t, db);
224
225 #if !defined(RSTRING_PTR)
226 #define RSTRING_PTR(v) (RSTRING((v))->ptr)
227 #endif /* !defined(RSTRING_PTR) */
228
229     SafeStringValue(pathv);
230     path = RSTRING_PTR(pathv);
231
232     ret = notmuch_database_add_message(db, path, &message);
233     notmuch_rb_status_raise(ret);
234     return rb_assoc_new(Data_Wrap_Struct(notmuch_rb_cMessage, NULL, NULL, message),
235         (ret == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) ? Qtrue : Qfalse);
236 }
237
238 /*
239  * call-seq: DB.remove_message(path) => isdup
240  *
241  * Remove a message from the database.
242  *
243  * +isdup+ is a boolean that specifies whether the removed message was a
244  * duplicate.
245  */
246 VALUE
247 notmuch_rb_database_remove_message(VALUE self, VALUE pathv)
248 {
249     const char *path;
250     notmuch_status_t ret;
251     notmuch_database_t *db;
252
253     Data_Get_Struct(self, notmuch_database_t, db);
254
255 #if !defined(RSTRING_PTR)
256 #define RSTRING_PTR(v) (RSTRING((v))->ptr)
257 #endif /* !defined(RSTRING_PTR) */
258
259     SafeStringValue(pathv);
260     path = RSTRING_PTR(pathv);
261
262     ret = notmuch_database_remove_message(db, path);
263     notmuch_rb_status_raise(ret);
264     return (ret == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) ? Qtrue : Qfalse;
265 }
266
267 /*
268  * call-seq: DB.query(query) => QUERY
269  *
270  * Retrieve a query object for the query string 'query'
271  */
272 VALUE
273 notmuch_rb_database_query_create(VALUE self, VALUE qstrv)
274 {
275     const char *qstr;
276     notmuch_query_t *query;
277     notmuch_database_t *db;
278
279     Data_Get_Struct(self, notmuch_database_t, db);
280
281 #if !defined(RSTRING_PTR)
282 #define RSTRING_PTR(v) (RSTRING((v))->ptr)
283 #endif /* !defined(RSTRING_PTR) */
284
285     SafeStringValue(qstrv);
286     qstr = RSTRING_PTR(qstrv);
287
288     query = notmuch_query_create(db, qstr);
289     if (!query)
290         rb_raise(notmuch_rb_eMemoryError, "out of memory");
291
292     return Data_Wrap_Struct(notmuch_rb_cQuery, NULL, NULL, query);
293 }