From 2cab738e58530f1301886904c2940fc7a22ad3c2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 2 Apr 2025 12:22:48 +0200 Subject: [PATCH] feat: EvalStateBuilder This supports the more "advanced" `nix_eval_state_builder`, which has more methods that we'll want to use. (cherry picked from commit a96047000df6b7022d166a8c35bb6e3075e5eddb) --- rust/nix-expr/src/eval_state.rs | 112 +++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 38 deletions(-) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 64111b5..e383fd4 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -78,6 +78,76 @@ impl Drop for EvalStateRef { } } } +pub struct EvalStateBuilder { + eval_state_builder: *mut raw::eval_state_builder, + lookup_path: Vec, + store: Store, +} +impl Drop for EvalStateBuilder { + fn drop(&mut self) { + unsafe { + raw::eval_state_builder_free(self.eval_state_builder); + } + } +} +impl EvalStateBuilder { + pub fn new(store: Store) -> Result { + let mut context = Context::new(); + let eval_state_builder = + unsafe { check_call!(raw::eval_state_builder_new(&mut context, store.raw_ptr())) }?; + Ok(EvalStateBuilder { + store, + eval_state_builder, + lookup_path: Vec::new(), + }) + } + pub fn lookup_path<'a>(mut self, path: impl IntoIterator) -> Result { + let lookup_path: Vec = path + .into_iter() + .map(|path| { + CString::new(path).with_context(|| { + format!("EvalStateBuilder::lookup_path: path `{path}` contains null byte") + }) + }) + .collect::>()?; + self.lookup_path = lookup_path; + Ok(self) + } + pub fn build(&self) -> Result { + // Make sure the library is initialized + init()?; + + let mut context = Context::new(); + + // Note: these raw C string pointers borrow from self.lookup_path + let mut lookup_path: Vec<*const c_char> = self + .lookup_path + .iter() + .map(|s| s.as_ptr()) + .chain(std::iter::once(null())) // signal the end of the array + .collect(); + + unsafe { + check_call!(raw::eval_state_builder_set_lookup_path( + &mut context, + self.eval_state_builder, + lookup_path.as_mut_ptr() + ))?; + } + + let eval_state = + unsafe { check_call!(raw::eval_state_build(&mut context, self.eval_state_builder)) }?; + Ok(EvalState { + eval_state: Arc::new(EvalStateRef { + eval_state: NonNull::new(eval_state).unwrap_or_else(|| { + panic!("nix_state_create returned a null pointer without an error") + }), + }), + store: self.store.clone(), + context, + }) + } +} pub struct EvalState { eval_state: Arc, @@ -85,45 +155,11 @@ pub struct EvalState { pub(crate) context: Context, } impl EvalState { + /// For more options, use [EvalStateBuilder]. pub fn new<'a>(store: Store, lookup_path: impl IntoIterator) -> Result { - let mut context = Context::new(); - - // this intermediate value must be here and must not be moved - // because it owns the data the `*const c_char` pointers point to. - let lookup_path: Vec = lookup_path - .into_iter() - .map(|path| { - CString::new(path).with_context(|| { - format!("EvalState::new: lookup_path `{path}` contains null byte") - }) - }) - .collect::>()?; - - // this intermediate value owns the data the `*mut *const c_char` pointer points to. - let mut lookup_path: Vec<*const c_char> = lookup_path - .iter() - .map(|s| s.as_ptr()) - .chain(std::iter::once(null())) // signal the end of the array - .collect(); - - init()?; - - let eval_state = unsafe { - check_call!(raw::state_create( - &mut context, - lookup_path.as_mut_ptr(), - store.raw_ptr() - )) - }?; - Ok(EvalState { - eval_state: Arc::new(EvalStateRef { - eval_state: NonNull::new(eval_state).unwrap_or_else(|| { - panic!("nix_state_create returned a null pointer without an error") - }), - }), - store, - context, - }) + EvalStateBuilder::new(store)? + .lookup_path(lookup_path)? + .build() } /// # Safety