c ffi
parent
b49c5a1cab
commit
03751e15fb
@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "cozo_c"
|
||||||
|
version = "0.1.1"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "staticlib"]
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cozo = { version = "0.1.1", path = ".." }
|
||||||
|
miette = { version = "=5.3.0", features = ["fancy"] }
|
||||||
|
serde_json = "1.0.81"
|
||||||
|
lazy_static = "1.4.0"
|
@ -0,0 +1,20 @@
|
|||||||
|
#include <cstdarg>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <ostream>
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
int8_t *cozo_open_db(const int8_t *path, int32_t *db_id);
|
||||||
|
|
||||||
|
bool cozo_close_db(int32_t id);
|
||||||
|
|
||||||
|
int8_t *cozo_run_query(int32_t db_id,
|
||||||
|
const int8_t *script_raw,
|
||||||
|
const int8_t *params_raw,
|
||||||
|
bool *errored);
|
||||||
|
|
||||||
|
void cozo_free_str(int8_t *s);
|
||||||
|
|
||||||
|
} // extern "C"
|
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022, The Cozo Project Authors. Licensed under MIT/Apache-2.0/BSD-3-Clause.
|
||||||
|
*/
|
||||||
|
#![warn(rust_2018_idioms, future_incompatible)]
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
use std::sync::atomic::{AtomicI32, Ordering};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
use cozo::Db;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Handles {
|
||||||
|
current: AtomicI32,
|
||||||
|
dbs: Mutex<BTreeMap<i32, Db>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref HANDLES: Handles = Handles::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn cozo_open_db(path: *const i8, db_id: &mut i32) -> *mut i8 {
|
||||||
|
let path = match CStr::from_ptr(path).to_str() {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(err) => return CString::new(format!("{}", err)).unwrap().into_raw(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match Db::new(path) {
|
||||||
|
Ok(db) => {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
Err(err) => CString::new(format!("{}", err)).unwrap().into_raw(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn cozo_close_db(id: i32) -> bool {
|
||||||
|
let db = {
|
||||||
|
let mut dbs = HANDLES.dbs.lock().unwrap();
|
||||||
|
dbs.remove(&id)
|
||||||
|
};
|
||||||
|
db.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn cozo_run_query(
|
||||||
|
db_id: i32,
|
||||||
|
script_raw: *const i8,
|
||||||
|
params_raw: *const i8,
|
||||||
|
errored: &mut bool,
|
||||||
|
) -> *mut i8 {
|
||||||
|
let script = match CStr::from_ptr(script_raw).to_str() {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(err) => {
|
||||||
|
*errored = true;
|
||||||
|
return CString::new(format!("{}", err)).unwrap().into_raw();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let db = {
|
||||||
|
let db_ref = {
|
||||||
|
let dbs = HANDLES.dbs.lock().unwrap();
|
||||||
|
dbs.get(&db_id).cloned()
|
||||||
|
};
|
||||||
|
match db_ref {
|
||||||
|
None => {
|
||||||
|
*errored = true;
|
||||||
|
return CString::new("database already closed").unwrap().into_raw();
|
||||||
|
}
|
||||||
|
Some(db) => db,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let params_str = match CStr::from_ptr(params_raw).to_str() {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(err) => {
|
||||||
|
*errored = true;
|
||||||
|
return CString::new(format!("{}", err)).unwrap().into_raw();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let params_map: serde_json::Value = match serde_json::from_str(¶ms_str) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(_) => {
|
||||||
|
*errored = true;
|
||||||
|
return CString::new("the given params argument is not valid JSON")
|
||||||
|
.unwrap()
|
||||||
|
.into_raw();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let params_arg: BTreeMap<_, _> = match params_map {
|
||||||
|
serde_json::Value::Object(m) => m.into_iter().collect(),
|
||||||
|
_ => {
|
||||||
|
*errored = true;
|
||||||
|
return CString::new("the given params argument is not a JSON map")
|
||||||
|
.unwrap()
|
||||||
|
.into_raw();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = db.run_script(script, ¶ms_arg);
|
||||||
|
match result {
|
||||||
|
Ok(json) => {
|
||||||
|
let json_str = json.to_string();
|
||||||
|
CString::new(json_str).unwrap().into_raw()
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
let err_str = format!("{:?}", err);
|
||||||
|
*errored = true;
|
||||||
|
CString::new(err_str).unwrap().into_raw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn cozo_free_str(s: *mut i8) {
|
||||||
|
let _ = CString::from_raw(s);
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linux:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: messense/maturin-action@v1
|
||||||
|
with:
|
||||||
|
manylinux: auto
|
||||||
|
command: build
|
||||||
|
args: --release --sdist -o dist --find-interpreter
|
||||||
|
- name: Upload wheels
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: wheels
|
||||||
|
path: dist
|
||||||
|
|
||||||
|
windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: messense/maturin-action@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --release -o dist --find-interpreter
|
||||||
|
- name: Upload wheels
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: wheels
|
||||||
|
path: dist
|
||||||
|
|
||||||
|
macos:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: messense/maturin-action@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --release -o dist --universal2 --find-interpreter
|
||||||
|
- name: Upload wheels
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: wheels
|
||||||
|
path: dist
|
||||||
|
|
||||||
|
release:
|
||||||
|
name: Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: "startsWith(github.ref, 'refs/tags/')"
|
||||||
|
needs: [ macos, windows, linux ]
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: wheels
|
||||||
|
- name: Publish to PyPI
|
||||||
|
uses: messense/maturin-action@v1
|
||||||
|
env:
|
||||||
|
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
||||||
|
with:
|
||||||
|
command: upload
|
||||||
|
args: --skip-existing *
|
Loading…
Reference in New Issue