#![warn(missing_docs)]
#[cfg(feature = "automate-function-loading-boilerplate")]
pub use inventory;
use wolfram_expr::{Expr, Symbol};
use wolfram_serialize::{FromWXF, ToWXF};
#[derive(ToWXF, FromWXF, Debug)]
#[allow(missing_docs)]
pub struct FunctionEntry {
pub name: String,
pub kind: String,
pub params: Vec<String>,
pub ret: String,
}
pub enum ExportEntry {
Native {
name: &'static str,
signature: fn() -> Result<(Vec<Expr>, Expr), String>,
},
Wstp {
name: &'static str,
},
Wxf {
name: &'static str,
signature: fn() -> Result<(Vec<Expr>, Expr), String>,
},
}
#[cfg(feature = "automate-function-loading-boilerplate")]
inventory::collect!(ExportEntry);
#[cfg(feature = "automate-function-loading-boilerplate")]
#[no_mangle]
pub extern "C" fn __wolfram_manifest__(out_len: *mut usize) -> *const u8 {
let assoc: Expr = exported_library_functions_association(None);
let bytes: Vec<u8> =
wolfram_serialize::to_wxf(&assoc, None).expect("manifest WXF serialization");
let len = bytes.len();
let ptr = Box::leak(bytes.into_boxed_slice()).as_ptr();
unsafe {
*out_len = len;
}
ptr
}
#[cfg(feature = "automate-function-loading-boilerplate")]
#[no_mangle]
pub extern "C" fn __wolfram_manifest_data__() -> *const u8 {
let entries: Vec<FunctionEntry> = inventory::iter::<ExportEntry>()
.map(|e| match e {
ExportEntry::Native { name, signature } => {
let (params, ret) =
signature().unwrap_or_else(|_| (vec![], Expr::string("")));
FunctionEntry {
name: (*name).to_owned(),
kind: "Native".to_owned(),
params: params.iter().map(|e| e.to_string()).collect(),
ret: ret.to_string(),
}
},
ExportEntry::Wstp { name } => FunctionEntry {
name: (*name).to_owned(),
kind: "Wstp".to_owned(),
params: vec![],
ret: String::new(),
},
ExportEntry::Wxf { name, .. } => FunctionEntry {
name: (*name).to_owned(),
kind: "Wxf".to_owned(),
params: vec![],
ret: String::new(),
},
})
.collect();
let wxf =
wolfram_serialize::to_wxf(&entries, None).expect("manifest WXF serialization failed");
let mut buf = Vec::with_capacity(8 + wxf.len());
buf.extend_from_slice(&(wxf.len() as u64).to_le_bytes());
buf.extend_from_slice(&wxf);
Box::leak(buf.into_boxed_slice()).as_ptr()
}
#[cfg(feature = "automate-function-loading-boilerplate")]
pub fn exported_library_functions_association(
library: Option<std::path::PathBuf>,
) -> Expr {
let library: std::path::PathBuf = library.unwrap_or_else(|| {
process_path::get_dylib_path()
.expect("unable to automatically determine Rust LibraryLink dynamic library file path. Suggestion: pass the library name or path to exported_library_functions_association(..)")
});
use wolfram_expr::{Association, RuleEntry};
let assoc: Association = inventory::iter::<ExportEntry>()
.filter_map(|entry| {
let code = entry.loading_code(&library).ok()?;
Some(RuleEntry::rule(Expr::from(entry.name()), code))
})
.collect();
Expr::from(assoc)
}
#[cfg_attr(
not(feature = "automate-function-loading-boilerplate"),
allow(dead_code)
)]
impl ExportEntry {
fn name(&self) -> &str {
match self {
ExportEntry::Native { name, .. } => name,
ExportEntry::Wstp { name } => name,
ExportEntry::Wxf { name, .. } => name,
}
}
fn loading_code(&self, library: &std::path::PathBuf) -> Result<Expr, String> {
use wolfram_expr::expr;
let library = library
.to_str()
.expect("unable to convert library file path to str");
let code = match self {
ExportEntry::Native { name, signature } => {
let (args, ret) = signature()?;
let name = *name;
expr!(System::LibraryFunctionLoad[library, name, args, ret])
},
ExportEntry::Wstp { name } => {
let name = *name;
let load_call = expr!(
System::LibraryFunctionLoad[library, name, "LinkObject", "LinkObject"]
);
let var = expr!(RustLink::Private::wstpFunc);
let ctx = Expr::from(Symbol::new("System`$Context"));
let ctx_path = Expr::from(Symbol::new("System`$ContextPath"));
let var2 = var.clone();
let body = expr!(var[System::SlotSequence[1]]);
expr!(System::With[
System::List[System::Set[var2, load_call]],
System::Function[System::Block[
System::List[
System::Set[ctx, "RustLinkWSTPPrivateContext`"],
System::Set[ctx_path, System::List[]]
],
body
]]
])
},
ExportEntry::Wxf { name, .. } => {
let name = *name;
expr!(System::LibraryFunctionLoad[library, name, System::List["ByteArray"], "ByteArray"])
},
};
Ok(code)
}
}