updated README and a bunch of other stuff
This commit is contained in:
@@ -1,3 +1,255 @@
|
||||
//! C ABI for dynamically loaded parts (see `include/mize.h`).
|
||||
//!
|
||||
//! Names passed to `opt` / `method` / `thread` / `get_config` / `set_config`
|
||||
//! are absolute dotted paths (e.g. `"vicinae.open"`).
|
||||
//!
|
||||
//! Errors use a thread-local slot (like errno): C code calls `set_err(...)`;
|
||||
//! Rust checks via [`take_err`] after the call.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::{c_char, c_void, CStr};
|
||||
use std::ptr;
|
||||
|
||||
//pub extern "C" fn
|
||||
use crate::config::{ConfigOpt, MizeFun};
|
||||
use crate::error::MizeError;
|
||||
use crate::get_global_mize;
|
||||
use crate::item::ItemData;
|
||||
use crate::mize_err;
|
||||
use crate::write_to_mize_stdout;
|
||||
|
||||
type CFun = unsafe extern "C" fn(*mut ItemData) -> *mut ItemData;
|
||||
|
||||
thread_local! {
|
||||
/// Per-thread error set by C via `set_err`, consumed by Rust after the call.
|
||||
static CUR_ERR: RefCell<Option<MizeError>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
/// Clear the thread-local C error (call before invoking a C part entry / callback).
|
||||
pub fn clear_err() {
|
||||
CUR_ERR.with(|slot| {
|
||||
*slot.borrow_mut() = None;
|
||||
});
|
||||
}
|
||||
|
||||
/// Take the thread-local C error, if any.
|
||||
pub fn take_err() -> Option<MizeError> {
|
||||
CUR_ERR.with(|slot| slot.borrow_mut().take())
|
||||
}
|
||||
|
||||
/// Opaque pointer stored via `data_from_native` / read via `data_as_native`.
|
||||
struct NativePtr(*mut c_void);
|
||||
|
||||
// SAFETY: the C++ part is responsible for only using the pointer on the right thread.
|
||||
unsafe impl Send for NativePtr {}
|
||||
unsafe impl Sync for NativePtr {}
|
||||
|
||||
fn cstr<'a>(p: *const c_char) -> Option<&'a str> {
|
||||
if p.is_null() {
|
||||
return None;
|
||||
}
|
||||
unsafe { CStr::from_ptr(p) }.to_str().ok()
|
||||
}
|
||||
|
||||
fn wrap_c_fun(c_fun: CFun) -> MizeFun {
|
||||
Box::new(move |args| {
|
||||
clear_err();
|
||||
let mut args_box = Box::new(args);
|
||||
let ret_ptr = unsafe { c_fun(args_box.as_mut()) };
|
||||
drop(args_box);
|
||||
if let Some(err) = take_err() {
|
||||
if !ret_ptr.is_null() {
|
||||
unsafe { drop(Box::from_raw(ret_ptr)) };
|
||||
}
|
||||
return Err(err);
|
||||
}
|
||||
if ret_ptr.is_null() {
|
||||
return Ok(ItemData::new());
|
||||
}
|
||||
Ok(*unsafe { Box::from_raw(ret_ptr) })
|
||||
})
|
||||
}
|
||||
|
||||
fn register_fun(name: &str, kind: &str, signature: &str, fun: MizeFun) {
|
||||
let mut mize = match get_global_mize() {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
let _ = write_to_mize_stdout(format_args!("[c_api] get_global_mize failed: {e:?}\n"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let id = match mize.id_new_dots(name) {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
let _ = write_to_mize_stdout(format_args!("[c_api] bad id '{name}': {e:?}\n"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if mize.is_generate_mode().unwrap_or(false) {
|
||||
let short = name.rsplit('.').next().unwrap_or(name);
|
||||
let _ = mize.record_method(short, id.clone(), kind, signature);
|
||||
return;
|
||||
}
|
||||
|
||||
let opt = ConfigOpt {
|
||||
name: id.dot(),
|
||||
val: None,
|
||||
fun: None,
|
||||
deps: Vec::new(),
|
||||
};
|
||||
mize.config_opts.lock().unwrap().insert(id.clone(), opt);
|
||||
mize.fun_values.lock().unwrap().insert(id, fun);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn opt(name: *const c_char) {
|
||||
let Some(name) = cstr(name) else { return };
|
||||
let mut mize = match get_global_mize() {
|
||||
Ok(m) => m,
|
||||
Err(_) => return,
|
||||
};
|
||||
let Ok(id) = mize.id_new_dots(name) else { return };
|
||||
|
||||
if mize.is_generate_mode().unwrap_or(false) {
|
||||
let short = name.rsplit('.').next().unwrap_or(name);
|
||||
let _ = mize.record_opt(short, id.clone(), None);
|
||||
return;
|
||||
}
|
||||
|
||||
let opt = ConfigOpt {
|
||||
name: id.dot(),
|
||||
val: None,
|
||||
fun: None,
|
||||
deps: Vec::new(),
|
||||
};
|
||||
mize.config_opts.lock().unwrap().insert(id, opt);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn method(name: *const c_char, signature: *const c_char, c_fun: CFun) {
|
||||
let Some(name) = cstr(name) else { return };
|
||||
let sig = cstr(signature).unwrap_or("");
|
||||
register_fun(name, "method", sig, wrap_c_fun(c_fun));
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn effect(name: *const c_char, c_fun: CFun) {
|
||||
let Some(name) = cstr(name) else { return };
|
||||
register_fun(name, "effect", "", wrap_c_fun(c_fun));
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn apply(name: *const c_char, c_fun: CFun) {
|
||||
let Some(name) = cstr(name) else { return };
|
||||
register_fun(name, "apply", "", wrap_c_fun(c_fun));
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn thread(name: *const c_char, c_fun: CFun) {
|
||||
let Some(name) = cstr(name) else { return };
|
||||
register_fun(name, "thread", "", wrap_c_fun(c_fun));
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn set_config(name: *const c_char, value: *mut ItemData) {
|
||||
if value.is_null() {
|
||||
return;
|
||||
}
|
||||
let Some(name) = cstr(name) else {
|
||||
unsafe { drop(Box::from_raw(value)) };
|
||||
return;
|
||||
};
|
||||
let value = unsafe { *Box::from_raw(value) };
|
||||
let mut mize = match get_global_mize() {
|
||||
Ok(m) => m,
|
||||
Err(_) => return,
|
||||
};
|
||||
let Ok(id) = mize.id_new_dots(name) else { return };
|
||||
|
||||
let mut opts = mize.config_opts.lock().unwrap();
|
||||
match opts.get_mut(&id) {
|
||||
Some(opt) => {
|
||||
opt.val = Some(value);
|
||||
}
|
||||
None => {
|
||||
opts.insert(
|
||||
id.clone(),
|
||||
ConfigOpt {
|
||||
name: id.dot(),
|
||||
val: Some(value),
|
||||
fun: None,
|
||||
deps: Vec::new(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn get_config(name: *const c_char) -> *mut ItemData {
|
||||
let Some(name) = cstr(name) else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
let mut mize = match get_global_mize() {
|
||||
Ok(m) => m,
|
||||
Err(_) => return ptr::null_mut(),
|
||||
};
|
||||
match mize.get_config(name) {
|
||||
Ok(data) => Box::into_raw(Box::new(data)),
|
||||
Err(_) => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn data_new() -> *mut ItemData {
|
||||
Box::into_raw(Box::new(ItemData::new()))
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn data_from_native(ptr: *mut c_void) -> *mut ItemData {
|
||||
match ItemData::from_native(NativePtr(ptr)) {
|
||||
Ok(data) => Box::into_raw(Box::new(data)),
|
||||
Err(e) => {
|
||||
let _ = write_to_mize_stdout(format_args!("[c_api] data_from_native: {e:?}\n"));
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn data_as_native(data: *mut ItemData) -> *mut c_void {
|
||||
if data.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
let data = unsafe { &*data };
|
||||
match data.native::<NativePtr>() {
|
||||
Ok(guard) => guard.0,
|
||||
Err(e) => {
|
||||
let _ = write_to_mize_stdout(format_args!("[c_api] data_as_native: {e:?}\n"));
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn data_free(data: *mut ItemData) {
|
||||
if !data.is_null() {
|
||||
unsafe { drop(Box::from_raw(data)) };
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn mize_write_stdout(str_ptr: *const c_char) {
|
||||
let Some(s) = cstr(str_ptr) else { return };
|
||||
let _ = write_to_mize_stdout(format_args!("{s}"));
|
||||
}
|
||||
|
||||
/// Set the thread-local error (like errno). `load_part` / C-callback wrappers turn this into `Err`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn set_err(msg: *const c_char) {
|
||||
let text = cstr(msg).unwrap_or("set_err called with null/invalid message");
|
||||
CUR_ERR.with(|slot| {
|
||||
*slot.borrow_mut() = Some(mize_err!("{}", text));
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user