nix_bindings_expr/
primop.rs1use crate::eval_state::{EvalState, EvalStateWeak};
2use crate::value::Value;
3use anyhow::Result;
4use nix_bindings_expr_sys as raw;
5use nix_bindings_util::check_call;
6use nix_bindings_util_sys as raw_util;
7use std::ffi::{c_int, c_void, CStr, CString};
8use std::mem::ManuallyDrop;
9use std::ptr::{null, null_mut};
10
11pub struct PrimOpMeta<'a, const N: usize> {
13 pub name: &'a CStr,
17
18 pub doc: &'a CStr,
21
22 pub args: [&'a CStr; N],
26}
27
28pub struct PrimOp {
29 pub(crate) ptr: *mut raw::PrimOp,
30}
31impl Drop for PrimOp {
32 fn drop(&mut self) {
33 unsafe {
34 raw::gc_decref(null_mut(), self.ptr as *mut c_void);
35 }
36 }
37}
38impl PrimOp {
39 pub fn new<const N: usize>(
40 eval_state: &mut EvalState,
41 meta: PrimOpMeta<N>,
42 f: Box<dyn Fn(&mut EvalState, &[Value; N]) -> Result<Value>>,
43 ) -> Result<PrimOp> {
44 assert!(N != 0);
45
46 let mut args = Vec::new();
47 for arg in meta.args {
48 args.push(arg.as_ptr());
49 }
50 args.push(null());
51
52 let user_data = {
55 let user_data = ManuallyDrop::new(Box::new(PrimOpContext {
58 arity: N,
59 function: Box::new(move |eval_state, args| f(eval_state, args.try_into().unwrap())),
60 eval_state: eval_state.weak_ref(),
61 }));
62 user_data.as_ref() as *const PrimOpContext as *mut c_void
63 };
64 let op = unsafe {
65 check_call!(raw::alloc_primop(
66 &mut eval_state.context,
67 FUNCTION_ADAPTER,
68 N as c_int,
69 meta.name.as_ptr(),
70 args.as_mut_ptr(), meta.doc.as_ptr(),
72 user_data
73 ))?
74 };
75 Ok(PrimOp { ptr: op })
76 }
77}
78
79struct PrimOpContext {
81 arity: usize,
82 function: Box<dyn Fn(&mut EvalState, &[Value]) -> Result<Value>>,
83 eval_state: EvalStateWeak,
84}
85
86unsafe extern "C" fn function_adapter(
87 user_data: *mut ::std::os::raw::c_void,
88 context_out: *mut raw_util::c_context,
89 _state: *mut raw::EvalState,
90 args: *mut *mut raw::Value,
91 ret: *mut raw::Value,
92) {
93 let primop_info = (user_data as *const PrimOpContext).as_ref().unwrap();
94 let mut eval_state = primop_info.eval_state.upgrade().unwrap_or_else(|| {
95 panic!("Nix primop called after EvalState was dropped");
96 });
97 let args_raw_slice = unsafe { std::slice::from_raw_parts(args, primop_info.arity) };
98 let args_vec: Vec<Value> = args_raw_slice
99 .iter()
100 .map(|v| Value::new_borrowed(*v))
101 .collect();
102 let args_slice = args_vec.as_slice();
103
104 let r = primop_info.function.as_ref()(&mut eval_state, args_slice);
105
106 match r {
107 Ok(v) => unsafe {
108 raw::copy_value(context_out, ret, v.raw_ptr());
109 },
110 Err(e) => unsafe {
111 let cstr = CString::new(e.to_string()).unwrap_or_else(|_e| {
112 CString::new("<rust nix-expr application error message contained null byte>")
113 .unwrap()
114 });
115 raw_util::set_err_msg(context_out, raw_util::err_NIX_ERR_UNKNOWN, cstr.as_ptr());
116 },
117 }
118}
119
120static FUNCTION_ADAPTER: raw::PrimOpFun = Some(function_adapter);