nix_bindings_expr/
primop.rs1use crate::eval_state::EvalState;
2use crate::value::Value;
3use anyhow::Result;
4use nix_bindings_expr_sys as raw;
5use nix_bindings_util::check_call;
6use nix_bindings_util::context::Context;
7use nix_bindings_util_sys as raw_util;
8use std::ffi::{c_int, c_void, CStr, CString};
9use std::mem::ManuallyDrop;
10use std::ptr::{null, null_mut, NonNull};
11
12#[derive(Debug)]
25pub struct RecoverableError(String);
26
27impl RecoverableError {
28 pub fn new(msg: impl Into<String>) -> Self {
29 RecoverableError(msg.into())
30 }
31}
32
33impl std::fmt::Display for RecoverableError {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 self.0.fmt(f)
36 }
37}
38
39impl std::error::Error for RecoverableError {}
40
41pub struct PrimOpMeta<'a, const N: usize> {
43 pub name: &'a CStr,
47
48 pub doc: &'a CStr,
51
52 pub args: [&'a CStr; N],
56}
57
58pub struct PrimOp {
59 ptr: *mut raw::PrimOp,
60}
61impl Drop for PrimOp {
62 fn drop(&mut self) {
63 unsafe {
64 raw::gc_decref(null_mut(), self.ptr as *mut c_void);
65 }
66 }
67}
68impl PrimOp {
69 pub fn new<const N: usize>(
78 meta: PrimOpMeta<N>,
79 f: Box<dyn Fn(&mut EvalState, &[Value; N]) -> Result<Value>>,
80 ) -> Result<PrimOp> {
81 assert!(N != 0);
82
83 let mut args = Vec::new();
84 for arg in meta.args {
85 args.push(arg.as_ptr());
86 }
87 args.push(null());
88
89 let user_data = {
92 let user_data = ManuallyDrop::new(Box::new(PrimOpContext {
95 arity: N,
96 function: Box::new(move |eval_state, args| f(eval_state, args.try_into().unwrap())),
97 }));
98 user_data.as_ref() as *const PrimOpContext as *mut c_void
99 };
100 let mut ctx = Context::new();
101 let op = unsafe {
102 check_call!(raw::alloc_primop(
103 &mut ctx,
104 FUNCTION_ADAPTER,
105 N as c_int,
106 meta.name.as_ptr(),
107 args.as_mut_ptr(), meta.doc.as_ptr(),
109 user_data
110 ))?
111 };
112 Ok(PrimOp { ptr: op })
113 }
114
115 #[doc(alias = "make_primop")]
123 #[doc(alias = "create_function")]
124 #[doc(alias = "builtin")]
125 #[doc(alias = "init_primop")]
126 pub fn into_value(self, eval_state: &mut EvalState) -> Result<Value> {
127 let value = eval_state.new_value_uninitialized()?;
128 unsafe {
129 check_call!(raw::init_primop(
130 &mut eval_state.context,
131 value.raw_ptr(),
132 self.ptr
133 ))?;
134 }
135 Ok(value)
136 }
137
138 #[doc(alias = "nix_register_primop")]
145 pub fn register_globally(self) -> Result<()> {
146 let mut ctx = Context::new();
147 unsafe {
148 check_call!(raw::register_primop(&mut ctx, self.ptr))?;
149 }
150 Ok(())
151 }
152}
153
154struct PrimOpContext {
156 arity: usize,
157 function: Box<dyn Fn(&mut EvalState, &[Value]) -> Result<Value>>,
158}
159
160unsafe extern "C" fn function_adapter(
161 user_data: *mut ::std::os::raw::c_void,
162 context_out: *mut raw_util::c_context,
163 state: *mut raw::EvalState,
164 args: *mut *mut raw::Value,
165 ret: *mut raw::Value,
166) {
167 let primop_info = (user_data as *const PrimOpContext).as_ref().unwrap();
168 let mut eval_state = EvalState::from_raw_borrowed(
169 NonNull::new(state).expect("Nix primop callback given null EvalState pointer"),
170 );
171 let args_raw_slice = unsafe { std::slice::from_raw_parts(args, primop_info.arity) };
172 let args_vec: Vec<Value> = args_raw_slice
173 .iter()
174 .map(|v| Value::new_borrowed(*v))
175 .collect();
176 let args_slice = args_vec.as_slice();
177
178 let r = primop_info.function.as_ref()(&mut eval_state, args_slice);
179
180 match r {
181 Ok(v) => unsafe {
182 raw::copy_value(context_out, ret, v.raw_ptr());
183 },
184 Err(e) => unsafe {
185 let err_code = error_code(&e);
186 let cstr = CString::new(e.to_string()).unwrap_or_else(|_e| {
187 CString::new("<rust nix-expr application error message contained null byte>")
188 .unwrap()
189 });
190 raw_util::set_err_msg(context_out, err_code, cstr.as_ptr());
191 },
192 }
193 drop(eval_state);
195}
196
197fn error_code(e: &anyhow::Error) -> raw_util::err {
198 #[cfg(nix_at_least = "2.34.0pre")]
199 if e.downcast_ref::<RecoverableError>().is_some() {
200 return raw_util::err_NIX_ERR_RECOVERABLE;
201 }
202 raw_util::err_NIX_ERR_UNKNOWN
203}
204
205static FUNCTION_ADAPTER: raw::PrimOpFun = Some(function_adapter);