feat: nix_expr::eval_state::require_list_strict
(cherry picked from commit 647854fd767b156c3a5db2da0e5659e040a69534)
This commit is contained in:
parent
759550b47f
commit
41c99e02cf
1 changed files with 126 additions and 0 deletions
|
|
@ -11,6 +11,7 @@ use nix_util::context::Context;
|
|||
use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data};
|
||||
use nix_util::{check_call, check_call_opt_key, result_string_init};
|
||||
use std::ffi::{c_char, CString};
|
||||
use std::iter::FromIterator;
|
||||
use std::os::raw::c_uint;
|
||||
use std::ptr::{null, null_mut, NonNull};
|
||||
use std::sync::{Arc, Weak};
|
||||
|
|
@ -263,6 +264,46 @@ impl EvalState {
|
|||
unsafe { check_call!(raw::get_bool(&mut self.context, v.raw_ptr())) }
|
||||
}
|
||||
|
||||
/// Evaluate and require that the passed value is a list.
|
||||
/// Returns the contained values in the specified container type.
|
||||
/// This is strict - all list elements will be evaluated.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use nix_expr::value::Value;
|
||||
/// # use std::collections::VecDeque;
|
||||
/// # fn example(es: &mut nix_expr::eval_state::EvalState, list_value: &Value) -> anyhow::Result<()> {
|
||||
/// let vec: Vec<Value> = es.require_list_strict(&list_value)?;
|
||||
/// let deque: VecDeque<Value> = es.require_list_strict(&list_value)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn require_list_strict<C>(&mut self, value: &Value) -> Result<C>
|
||||
where
|
||||
C: FromIterator<Value>,
|
||||
{
|
||||
let t = self.value_type(value)?;
|
||||
if t != ValueType::List {
|
||||
bail!("expected a list, but got a {:?}", t);
|
||||
}
|
||||
let size = unsafe { check_call!(raw::get_list_size(&mut self.context, value.raw_ptr())) }?;
|
||||
|
||||
(0..size)
|
||||
.map(|i| {
|
||||
let element_ptr = unsafe {
|
||||
check_call!(raw::get_list_byidx(
|
||||
&mut self.context,
|
||||
value.raw_ptr(),
|
||||
self.eval_state.as_ptr(),
|
||||
i
|
||||
))
|
||||
}?;
|
||||
Ok(unsafe { Value::new(element_ptr) })
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Evaluate, and require that the value is an attrset.
|
||||
/// Returns a list of the keys in the attrset.
|
||||
///
|
||||
|
|
@ -1320,6 +1361,91 @@ mod tests {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_state_value_list_strict_empty() {
|
||||
gc_registering_current_thread(|| {
|
||||
let store = Store::open(None, HashMap::new()).unwrap();
|
||||
let mut es = EvalState::new(store, []).unwrap();
|
||||
let v = es.eval_from_string("[]", "<test>").unwrap();
|
||||
es.force(&v).unwrap();
|
||||
let list: Vec<Value> = es.require_list_strict(&v).unwrap();
|
||||
assert_eq!(list.len(), 0);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_state_value_list_strict_int() {
|
||||
gc_registering_current_thread(|| {
|
||||
let store = Store::open(None, HashMap::new()).unwrap();
|
||||
let mut es = EvalState::new(store, []).unwrap();
|
||||
let v = es.eval_from_string("[42]", "<test>").unwrap();
|
||||
es.force(&v).unwrap();
|
||||
let list: Vec<Value> = es.require_list_strict(&v).unwrap();
|
||||
assert_eq!(list.len(), 1);
|
||||
assert_eq!(es.require_int(&list[0]).unwrap(), 42);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_state_value_list_strict_int_bool() {
|
||||
gc_registering_current_thread(|| {
|
||||
let store = Store::open(None, HashMap::new()).unwrap();
|
||||
let mut es = EvalState::new(store, []).unwrap();
|
||||
let v = es.eval_from_string("[42 true]", "<test>").unwrap();
|
||||
es.force(&v).unwrap();
|
||||
let list: Vec<Value> = es.require_list_strict(&v).unwrap();
|
||||
assert_eq!(list.len(), 2);
|
||||
assert_eq!(es.require_int(&list[0]).unwrap(), 42);
|
||||
assert_eq!(es.require_bool(&list[1]).unwrap(), true);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_state_value_list_strict_error() {
|
||||
gc_registering_current_thread(|| {
|
||||
let store = Store::open(None, HashMap::new()).unwrap();
|
||||
let mut es = EvalState::new(store, []).unwrap();
|
||||
let v = es.eval_from_string(r#"[(throw "_evaluated_item_")]"#, "<test>").unwrap();
|
||||
es.force(&v).unwrap();
|
||||
// This should fail because require_list_strict evaluates all elements
|
||||
let result: Result<Vec<Value>, _> = es.require_list_strict(&v);
|
||||
assert!(result.is_err());
|
||||
match result {
|
||||
Err(error_msg) => {
|
||||
let error_str = error_msg.to_string();
|
||||
assert!(error_str.contains("_evaluated_item_"));
|
||||
}
|
||||
Ok(_) => panic!("unexpected success. The item should have been evaluated and its error propagated.")
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_state_value_list_strict_generic_container() {
|
||||
gc_registering_current_thread(|| {
|
||||
let store = Store::open(None, HashMap::new()).unwrap();
|
||||
let mut es = EvalState::new(store, []).unwrap();
|
||||
let v = es.eval_from_string("[1 2 3]", "<test>").unwrap();
|
||||
|
||||
// Test with Vec
|
||||
let vec: Vec<Value> = es.require_list_strict(&v).unwrap();
|
||||
assert_eq!(vec.len(), 3);
|
||||
|
||||
// Test with VecDeque
|
||||
let deque: std::collections::VecDeque<Value> = es.require_list_strict(&v).unwrap();
|
||||
assert_eq!(deque.len(), 3);
|
||||
|
||||
// Verify contents are the same
|
||||
assert_eq!(es.require_int(&vec[0]).unwrap(), 1);
|
||||
assert_eq!(es.require_int(&deque[0]).unwrap(), 1);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_state_realise_string() {
|
||||
gc_registering_current_thread(|| {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue