1pub mod ffi;
20pub mod error;
21
22pub use error::{Result, SdkError};
23
24use std::os::raw::c_char;
25use serde_json::Value;
26use libloading::Symbol;
27
28#[derive(Debug, Clone, Copy)]
30pub enum IsolationLevel {
31 ReadUncommitted = 0,
32 ReadCommitted = 1,
33 RepeatableRead = 2,
34 Serializable = 3,
35}
36
37pub struct Transaction {
39 pub id: u64,
40}
41
42#[derive(Default)]
44pub struct OpenOptions {
45 pub password: Option<String>,
46 pub engine: Option<String>,
47 pub auto_create_tables: bool,
48}
49
50impl OpenOptions {
51 pub fn new() -> Self { Self { auto_create_tables: true, ..Default::default() } }
52 pub fn password(mut self, p: &str) -> Self { self.password = Some(p.into()); self }
53 pub fn engine(mut self, e: &str) -> Self { self.engine = Some(e.into()); self }
54}
55
56fn last_error() -> String {
59 unsafe {
60 let lib = ffi::load();
61 let func: Symbol<unsafe extern "C" fn() -> *const c_char> =
62 lib.get(b"overdrive_last_error").unwrap();
63 let ptr = func();
64 ffi::read_static(ptr)
65 }
66}
67
68pub struct OverdriveDb {
78 handle: *mut std::ffi::c_void,
79}
80
81unsafe impl Send for OverdriveDb {}
82
83impl Drop for OverdriveDb {
84 fn drop(&mut self) {
85 if !self.handle.is_null() {
86 unsafe {
87 let lib = ffi::load();
88 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void)> =
89 lib.get(b"overdrive_close").unwrap();
90 func(self.handle);
91 }
92 self.handle = std::ptr::null_mut();
93 }
94 }
95}
96
97impl OverdriveDb {
98 pub fn open(path: &str) -> Result<Self> {
102 let c_path = ffi::to_cstr(path);
103 unsafe {
104 let lib = ffi::load();
105 let func: Symbol<unsafe extern "C" fn(*const c_char) -> *mut std::ffi::c_void> =
106 lib.get(b"overdrive_open").unwrap();
107 let handle = func(c_path.as_ptr());
108 if handle.is_null() {
109 return Err(SdkError(last_error()));
110 }
111 Ok(Self { handle })
112 }
113 }
114
115 pub fn open_with_options(path: &str, opts: OpenOptions) -> Result<Self> {
117 let c_path = ffi::to_cstr(path);
118 let engine = opts.engine.as_deref().unwrap_or("Disk");
119 let c_engine = ffi::to_cstr(engine);
120 let options = serde_json::json!({
121 "password": opts.password,
122 "auto_create_tables": opts.auto_create_tables,
123 });
124 let c_opts = ffi::to_cstr(&options.to_string());
125 unsafe {
126 let lib = ffi::load();
127 let func: Symbol<unsafe extern "C" fn(*const c_char, *const c_char, *const c_char) -> *mut std::ffi::c_void> =
128 lib.get(b"overdrive_open_with_engine").unwrap();
129 let handle = func(c_path.as_ptr(), c_engine.as_ptr(), c_opts.as_ptr());
130 if handle.is_null() {
131 return Err(SdkError(last_error()));
132 }
133 Ok(Self { handle })
134 }
135 }
136
137 pub fn sync(&self) -> Result<()> {
139 unsafe {
140 let lib = ffi::load();
141 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void)> =
142 lib.get(b"overdrive_sync").unwrap();
143 func(self.handle);
144 }
145 Ok(())
146 }
147
148 pub fn close(mut self) -> Result<()> {
150 unsafe {
151 let lib = ffi::load();
152 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void)> =
153 lib.get(b"overdrive_close").unwrap();
154 func(self.handle);
155 self.handle = std::ptr::null_mut();
156 }
157 Ok(())
158 }
159
160 pub fn version() -> String {
162 unsafe {
163 let lib = ffi::load();
164 let func: Symbol<unsafe extern "C" fn() -> *const c_char> =
165 lib.get(b"overdrive_version").unwrap();
166 ffi::read_static(func())
167 }
168 }
169
170 pub fn create_table(&mut self, name: &str) -> Result<()> {
174 let c_name = ffi::to_cstr(name);
175 unsafe {
176 let lib = ffi::load();
177 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void, *const c_char) -> i32> =
178 lib.get(b"overdrive_create_table").unwrap();
179 if func(self.handle, c_name.as_ptr()) != 0 {
180 return Err(SdkError(last_error()));
181 }
182 }
183 Ok(())
184 }
185
186 pub fn drop_table(&mut self, name: &str) -> Result<()> {
188 let c_name = ffi::to_cstr(name);
189 unsafe {
190 let lib = ffi::load();
191 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void, *const c_char) -> i32> =
192 lib.get(b"overdrive_drop_table").unwrap();
193 if func(self.handle, c_name.as_ptr()) != 0 {
194 return Err(SdkError(last_error()));
195 }
196 }
197 Ok(())
198 }
199
200 pub fn list_tables(&self) -> Result<Vec<String>> {
202 unsafe {
203 let lib = ffi::load();
204 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void) -> *mut c_char> =
205 lib.get(b"overdrive_list_tables").unwrap();
206 let ptr = func(self.handle);
207 if ptr.is_null() { return Err(SdkError(last_error())); }
208 let s = ffi::read_and_free(ptr);
209 Ok(serde_json::from_str(&s)?)
210 }
211 }
212
213 pub fn table_exists(&self, name: &str) -> Result<bool> {
215 let c_name = ffi::to_cstr(name);
216 unsafe {
217 let lib = ffi::load();
218 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void, *const c_char) -> i32> =
219 lib.get(b"overdrive_table_exists").unwrap();
220 Ok(func(self.handle, c_name.as_ptr()) == 1)
221 }
222 }
223
224 pub fn insert(&mut self, table: &str, doc: &Value) -> Result<String> {
228 let c_table = ffi::to_cstr(table);
229 let c_json = ffi::to_cstr(&doc.to_string());
230 unsafe {
231 let lib = ffi::load();
232 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void, *const c_char, *const c_char) -> *mut c_char> =
233 lib.get(b"overdrive_insert").unwrap();
234 let ptr = func(self.handle, c_table.as_ptr(), c_json.as_ptr());
235 if ptr.is_null() { return Err(SdkError(last_error())); }
236 Ok(ffi::read_and_free(ptr))
237 }
238 }
239
240 pub fn insert_batch(&mut self, table: &str, docs: &[Value]) -> Result<Vec<String>> {
242 docs.iter().map(|doc| self.insert(table, doc)).collect()
243 }
244
245 pub fn get(&self, table: &str, id: &str) -> Result<Option<Value>> {
247 let c_table = ffi::to_cstr(table);
248 let c_id = ffi::to_cstr(id);
249 unsafe {
250 let lib = ffi::load();
251 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void, *const c_char, *const c_char) -> *mut c_char> =
252 lib.get(b"overdrive_get").unwrap();
253 let ptr = func(self.handle, c_table.as_ptr(), c_id.as_ptr());
254 if ptr.is_null() { return Ok(None); }
255 let s = ffi::read_and_free(ptr);
256 Ok(Some(serde_json::from_str(&s)?))
257 }
258 }
259
260 pub fn update(&mut self, table: &str, id: &str, patch: &Value) -> Result<bool> {
262 let c_table = ffi::to_cstr(table);
263 let c_id = ffi::to_cstr(id);
264 let c_json = ffi::to_cstr(&patch.to_string());
265 unsafe {
266 let lib = ffi::load();
267 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void, *const c_char, *const c_char, *const c_char) -> i32> =
268 lib.get(b"overdrive_update").unwrap();
269 match func(self.handle, c_table.as_ptr(), c_id.as_ptr(), c_json.as_ptr()) {
270 1 => Ok(true),
271 0 => Ok(false),
272 _ => Err(SdkError(last_error())),
273 }
274 }
275 }
276
277 pub fn delete(&mut self, table: &str, id: &str) -> Result<bool> {
279 let c_table = ffi::to_cstr(table);
280 let c_id = ffi::to_cstr(id);
281 unsafe {
282 let lib = ffi::load();
283 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void, *const c_char, *const c_char) -> i32> =
284 lib.get(b"overdrive_delete").unwrap();
285 match func(self.handle, c_table.as_ptr(), c_id.as_ptr()) {
286 1 => Ok(true),
287 0 => Ok(false),
288 _ => Err(SdkError(last_error())),
289 }
290 }
291 }
292
293 pub fn count(&self, table: &str) -> Result<usize> {
295 let c_table = ffi::to_cstr(table);
296 unsafe {
297 let lib = ffi::load();
298 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void, *const c_char) -> i32> =
299 lib.get(b"overdrive_count").unwrap();
300 let n = func(self.handle, c_table.as_ptr());
301 if n < 0 { return Err(SdkError(last_error())); }
302 Ok(n as usize)
303 }
304 }
305
306 pub fn query(&mut self, sql: &str) -> Result<Vec<Value>> {
310 let c_sql = ffi::to_cstr(sql);
311 unsafe {
312 let lib = ffi::load();
313 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void, *const c_char) -> *mut c_char> =
314 lib.get(b"overdrive_query").unwrap();
315 let ptr = func(self.handle, c_sql.as_ptr());
316 if ptr.is_null() { return Err(SdkError(last_error())); }
317 let s = ffi::read_and_free(ptr);
318 let v: Value = serde_json::from_str(&s)?;
319 if let Some(rows) = v.get("rows").and_then(|r| r.as_array()) {
321 return Ok(rows.clone());
322 }
323 Ok(vec![v])
324 }
325 }
326
327 pub fn search(&self, table: &str, text: &str) -> Result<Vec<Value>> {
329 let c_table = ffi::to_cstr(table);
330 let c_text = ffi::to_cstr(text);
331 unsafe {
332 let lib = ffi::load();
333 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void, *const c_char, *const c_char) -> *mut c_char> =
334 lib.get(b"overdrive_search").unwrap();
335 let ptr = func(self.handle, c_table.as_ptr(), c_text.as_ptr());
336 if ptr.is_null() { return Ok(vec![]); }
337 let s = ffi::read_and_free(ptr);
338 Ok(serde_json::from_str(&s).unwrap_or_default())
339 }
340 }
341
342 pub fn begin_transaction(&mut self, iso: IsolationLevel) -> Result<Transaction> {
346 unsafe {
347 let lib = ffi::load();
348 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void, i32) -> u64> =
349 lib.get(b"overdrive_begin_transaction").unwrap();
350 let id = func(self.handle, iso as i32);
351 if id == 0 { return Err(SdkError(last_error())); }
352 Ok(Transaction { id })
353 }
354 }
355
356 pub fn commit_transaction(&mut self, txn: &Transaction) -> Result<()> {
358 unsafe {
359 let lib = ffi::load();
360 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void, u64) -> i32> =
361 lib.get(b"overdrive_commit_transaction").unwrap();
362 if func(self.handle, txn.id) != 0 {
363 return Err(SdkError(last_error()));
364 }
365 }
366 Ok(())
367 }
368
369 pub fn abort_transaction(&mut self, txn: &Transaction) -> Result<()> {
371 unsafe {
372 let lib = ffi::load();
373 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void, u64) -> i32> =
374 lib.get(b"overdrive_abort_transaction").unwrap();
375 if func(self.handle, txn.id) != 0 {
376 return Err(SdkError(last_error()));
377 }
378 }
379 Ok(())
380 }
381
382 pub fn transaction<F, T>(&mut self, iso: IsolationLevel, f: F) -> Result<T>
384 where F: FnOnce(&mut Self) -> Result<T>
385 {
386 let txn = self.begin_transaction(iso)?;
387 match f(self) {
388 Ok(v) => { self.commit_transaction(&txn)?; Ok(v) }
389 Err(e) => { let _ = self.abort_transaction(&txn); Err(e) }
390 }
391 }
392
393 pub fn verify_integrity(&self) -> Result<Value> {
397 unsafe {
398 let lib = ffi::load();
399 let func: Symbol<unsafe extern "C" fn(*mut std::ffi::c_void) -> *mut c_char> =
400 lib.get(b"overdrive_verify_integrity").unwrap();
401 let ptr = func(self.handle);
402 if ptr.is_null() { return Err(SdkError(last_error())); }
403 let s = ffi::read_and_free(ptr);
404 Ok(serde_json::from_str(&s)?)
405 }
406 }
407}