#![warn(rust_2018_idioms, future_incompatible)]
#![allow(clippy::missing_safety_doc)]
use std::collections::BTreeMap;
use std::ffi::{c_char, CStr, CString};
use std::ptr::null_mut;
use std::sync::atomic::{AtomicI32, Ordering};
use std::sync::Mutex;
use lazy_static::lazy_static;
use cozo::*;
struct Handles {
current: AtomicI32,
dbs: Mutex<BTreeMap<i32, DbInstance>>,
}
lazy_static! {
static ref HANDLES: Handles = Handles {
current: Default::default(),
dbs: Mutex::new(Default::default())
};
}
#[no_mangle]
pub unsafe extern "C" fn cozo_open_db(
engine: *const c_char,
path: *const c_char,
options: *const c_char,
db_id: &mut i32,
) -> *mut c_char {
let engine = match CStr::from_ptr(engine).to_str() {
Ok(p) => p,
Err(err) => return CString::new(format!("{err}")).unwrap().into_raw(),
};
let path = match CStr::from_ptr(path).to_str() {
Ok(p) => p,
Err(err) => return CString::new(format!("{err}")).unwrap().into_raw(),
};
let options = match CStr::from_ptr(options).to_str() {
Ok(p) => p,
Err(err) => return CString::new(format!("{err}")).unwrap().into_raw(),
};
let db = match DbInstance::new_with_str(engine, path, options) {
Ok(db) => db,
Err(err) => return CString::new(err).unwrap().into_raw(),
};
let id = HANDLES.current.fetch_add(1, Ordering::AcqRel);
let mut dbs = HANDLES.dbs.lock().unwrap();
dbs.insert(id, db);
*db_id = id;
null_mut()
}
#[no_mangle]
pub unsafe extern "C" fn cozo_close_db(db_id: i32) -> bool {
let db = {
let mut dbs = HANDLES.dbs.lock().unwrap();
dbs.remove(&db_id)
};
db.is_some()
}
#[no_mangle]
pub unsafe extern "C" fn cozo_run_query(
db_id: i32,
script_raw: *const c_char,
params_raw: *const c_char,
immutable_query: bool,
) -> *mut c_char {
let script = match CStr::from_ptr(script_raw).to_str() {
Ok(p) => p,
Err(_) => {
return CString::new(r##"{"ok":false,"message":"script is not UTF-8 encoded"}"##)
.unwrap()
.into_raw();
}
};
let db = {
let db_ref = {
let dbs = HANDLES.dbs.lock().unwrap();
dbs.get(&db_id).cloned()
};
match db_ref {
None => {
return CString::new(r##"{"ok":false,"message":"database closed"}"##)
.unwrap()
.into_raw();
}
Some(db) => db,
}
};
let params_str = match CStr::from_ptr(params_raw).to_str() {
Ok(p) => p,
Err(_) => {
return CString::new(
r##"{"ok":false,"message":"params argument is not UTF-8 encoded"}"##,
)
.unwrap()
.into_raw();
}
};
let result = db.run_script_str(script, params_str, immutable_query);
CString::new(result).unwrap().into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn cozo_import_relations(
db_id: i32,
json_payload: *const c_char,
) -> *mut c_char {
let db = {
let db_ref = {
let dbs = HANDLES.dbs.lock().unwrap();
dbs.get(&db_id).cloned()
};
match db_ref {
None => {
return CString::new(r##"{"ok":false,"message":"database closed"}"##)
.unwrap()
.into_raw();
}
Some(db) => db,
}
};
let data = match CStr::from_ptr(json_payload).to_str() {
Ok(p) => p,
Err(err) => return CString::new(format!("{err}")).unwrap().into_raw(),
};
CString::new(db.import_relations_str(data))
.unwrap()
.into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn cozo_export_relations(
db_id: i32,
json_payload: *const c_char,
) -> *mut c_char {
let db = {
let db_ref = {
let dbs = HANDLES.dbs.lock().unwrap();
dbs.get(&db_id).cloned()
};
match db_ref {
None => {
return CString::new(r##"{"ok":false,"message":"database closed"}"##)
.unwrap()
.into_raw();
}
Some(db) => db,
}
};
let data = match CStr::from_ptr(json_payload).to_str() {
Ok(p) => p,
Err(err) => return CString::new(format!("{err}")).unwrap().into_raw(),
};
CString::new(db.export_relations_str(data))
.unwrap()
.into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn cozo_backup(db_id: i32, out_path: *const c_char) -> *mut c_char {
let db = {
let db_ref = {
let dbs = HANDLES.dbs.lock().unwrap();
dbs.get(&db_id).cloned()
};
match db_ref {
None => {
return CString::new(r##"{"ok":false,"message":"database closed"}"##)
.unwrap()
.into_raw();
}
Some(db) => db,
}
};
let data = match CStr::from_ptr(out_path).to_str() {
Ok(p) => p,
Err(err) => return CString::new(format!("{err}")).unwrap().into_raw(),
};
CString::new(db.backup_db_str(data)).unwrap().into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn cozo_restore(db_id: i32, in_path: *const c_char) -> *mut c_char {
let db = {
let db_ref = {
let dbs = HANDLES.dbs.lock().unwrap();
dbs.get(&db_id).cloned()
};
match db_ref {
None => {
return CString::new(r##"{"ok":false,"message":"database closed"}"##)
.unwrap()
.into_raw();
}
Some(db) => db,
}
};
let data = match CStr::from_ptr(in_path).to_str() {
Ok(p) => p,
Err(err) => return CString::new(format!("{err}")).unwrap().into_raw(),
};
CString::new(db.restore_backup_str(data))
.unwrap()
.into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn cozo_import_from_backup(
db_id: i32,
json_payload: *const c_char,
) -> *mut c_char {
let db = {
let db_ref = {
let dbs = HANDLES.dbs.lock().unwrap();
dbs.get(&db_id).cloned()
};
match db_ref {
None => {
return CString::new(r##"{"ok":false,"message":"database closed"}"##)
.unwrap()
.into_raw();
}
Some(db) => db,
}
};
let data = match CStr::from_ptr(json_payload).to_str() {
Ok(p) => p,
Err(err) => return CString::new(format!("{err}")).unwrap().into_raw(),
};
CString::new(db.import_from_backup_str(data))
.unwrap()
.into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn cozo_free_str(s: *mut c_char) {
let _ = CString::from_raw(s);
}