updated README and a bunch of other stuff

This commit is contained in:
Sebastian Moser
2026-11-04 20:24:14 +01:00
parent 6eedb1826a
commit 9707286375
46 changed files with 3725 additions and 172 deletions

View File

@@ -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));
});
}