From 78435d4ed78c9a31de612108df4d4a850b3565cc Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 24 May 2024 09:34:20 +0200 Subject: [PATCH] feat: EvalState::new_value_apply (cherry picked from commit f8143ae4ed7b62b86cb232f8d73e53bae30d632c) --- rust/nix-expr/src/eval_state.rs | 84 +++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/rust/nix-expr/src/eval_state.rs b/rust/nix-expr/src/eval_state.rs index 935d75a..c9d4728 100644 --- a/rust/nix-expr/src/eval_state.rs +++ b/rust/nix-expr/src/eval_state.rs @@ -293,6 +293,8 @@ impl EvalState { } /// Eagerly apply a function to an argument. + /// + /// For a lazy version, see [`new_value_apply`][`EvalState::new_value_apply`]. pub fn call(&self, f: Value, a: Value) -> Result { let v = unsafe { let value = self.new_value_uninitialized(); @@ -309,6 +311,23 @@ impl EvalState { Ok(v) } + /// Apply a function to an argument, but don't evaluate the result just yet. + /// + /// For an eager version, see [`call`][`EvalState::call`]. + pub fn new_value_apply(&self, f: &Value, a: &Value) -> Result { + let value = self.new_value_uninitialized(); + unsafe { + raw::init_apply( + self.context.ptr(), + value.raw_ptr(), + f.raw_ptr(), + a.raw_ptr(), + ); + }; + self.context.check_err()?; + Ok(value) + } + fn new_value_uninitialized(&self) -> Value { let value = unsafe { raw::alloc_value(self.context.ptr(), self.raw_ptr()) }; Value::new(value) @@ -847,6 +866,23 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_apply() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store, []).unwrap(); + // This is a function that takes two arguments. + let f = es.eval_from_string("x: x + 1", "").unwrap(); + let a = es.eval_from_string("2", "").unwrap(); + let v = es.new_value_apply(&f, &a).unwrap(); + assert!(es.value_is_thunk(&v)); + es.force(&v).unwrap(); + let i = es.require_int(&v).unwrap(); + assert!(i == 3); + }) + .unwrap(); + } + #[test] fn eval_state_call_fail_body() { gc_registering_current_thread(|| { @@ -868,6 +904,30 @@ mod tests { .unwrap(); } + #[test] + fn eval_state_apply_fail_body() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store, []).unwrap(); + let f = es.eval_from_string("x: x + 1", "").unwrap(); + let a = es.eval_from_string("true", "").unwrap(); + // Lazy => no error + let r = es.new_value_apply(&f, &a).unwrap(); + // Force it => error + let res = es.force(&r); + match res { + Ok(_) => panic!("expected an error"), + Err(e) => { + if !e.to_string().contains("cannot coerce") { + eprintln!("{}", e); + assert!(false); + } + } + } + }) + .unwrap(); + } + #[test] fn eval_state_call_fail_args() { gc_registering_current_thread(|| { @@ -888,4 +948,28 @@ mod tests { }) .unwrap(); } + + #[test] + fn eval_state_apply_fail_args() { + gc_registering_current_thread(|| { + let store = Store::open("auto").unwrap(); + let es = EvalState::new(store, []).unwrap(); + let f = es.eval_from_string("{x}: x + 1", "").unwrap(); + let a = es.eval_from_string("{}", "").unwrap(); + // Lazy => no error + let r = es.new_value_apply(&f, &a).unwrap(); + // Force it => error + let res = es.force(&r); + match res { + Ok(_) => panic!("expected an error"), + Err(e) => { + if !e.to_string().contains("called without required argument") { + eprintln!("{}", e); + assert!(false); + } + } + } + }) + .unwrap(); + } }