1use crate::primop;
132use crate::value::{Int, Value, ValueType};
133use anyhow::Context as _;
134use anyhow::{bail, Result};
135use cstr::cstr;
136use nix_bindings_bdwgc_sys as gc;
137use nix_bindings_expr_sys as raw;
138use nix_bindings_store::path::StorePath;
139use nix_bindings_store::store::{Store, StoreWeak};
140use nix_bindings_store_sys as raw_store;
141use nix_bindings_util::context::Context;
142use nix_bindings_util::string_return::{
143 callback_get_result_string, callback_get_result_string_data,
144};
145use nix_bindings_util::{check_call, check_call_opt_key, result_string_init};
146use std::ffi::{c_char, CString};
147use std::iter::FromIterator;
148use std::os::raw::c_uint;
149use std::ptr::{null, null_mut, NonNull};
150use std::sync::{Arc, LazyLock, Weak};
151
152static INIT: LazyLock<Result<()>> = LazyLock::new(|| unsafe {
153 gc::GC_allow_register_threads();
154 check_call!(raw::libexpr_init(&mut Context::new()))?;
155 Ok(())
156});
157
158pub fn init() -> Result<()> {
159 let x = INIT.as_ref();
160 match x {
161 Ok(_) => Ok(()),
162 Err(e) => {
163 Err(anyhow::format_err!("nix_bindings_expr::init error: {}", e))
165 }
166 }
167}
168
169pub struct RealisedString {
173 pub s: String,
175 pub paths: Vec<StorePath>,
177}
178
179pub struct EvalStateWeak {
181 inner: Weak<EvalStateRef>,
182 store: StoreWeak,
183}
184impl EvalStateWeak {
185 pub fn upgrade(&self) -> Option<EvalState> {
189 self.inner.upgrade().and_then(|eval_state| {
190 self.store.upgrade().map(|store| EvalState {
191 eval_state,
192 store,
193 context: Context::new(),
194 })
195 })
196 }
197}
198
199struct EvalStateRef {
200 eval_state: NonNull<raw::EvalState>,
201}
202impl EvalStateRef {
203 unsafe fn as_ptr(&self) -> *mut raw::EvalState {
209 self.eval_state.as_ptr()
210 }
211}
212impl Drop for EvalStateRef {
213 fn drop(&mut self) {
214 unsafe {
215 raw::state_free(self.eval_state.as_ptr());
216 }
217 }
218}
219#[cfg(nix_at_least = "2.26")]
247pub struct EvalStateBuilder {
248 eval_state_builder: *mut raw::eval_state_builder,
249 lookup_path: Vec<CString>,
250 load_ambient_settings: bool,
251 store: Store,
252}
253#[cfg(nix_at_least = "2.26")]
254impl Drop for EvalStateBuilder {
255 fn drop(&mut self) {
256 unsafe {
257 raw::eval_state_builder_free(self.eval_state_builder);
258 }
259 }
260}
261#[cfg(nix_at_least = "2.26")]
262impl EvalStateBuilder {
263 pub fn new(store: Store) -> Result<EvalStateBuilder> {
265 let mut context = Context::new();
266 let eval_state_builder =
267 unsafe { check_call!(raw::eval_state_builder_new(&mut context, store.raw_ptr())) }?;
268 Ok(EvalStateBuilder {
269 store,
270 eval_state_builder,
271 lookup_path: Vec::new(),
272 load_ambient_settings: true,
273 })
274 }
275 pub fn lookup_path<'a>(mut self, path: impl IntoIterator<Item = &'a str>) -> Result<Self> {
277 let lookup_path: Vec<CString> = path
278 .into_iter()
279 .map(|path| {
280 CString::new(path).with_context(|| {
281 format!("EvalStateBuilder::lookup_path: path `{path}` contains null byte")
282 })
283 })
284 .collect::<Result<_>>()?;
285 self.lookup_path = lookup_path;
286 Ok(self)
287 }
288 pub fn load_ambient_settings(mut self, load: bool) -> Self {
294 self.load_ambient_settings = load;
295 self
296 }
297 pub fn build(&self) -> Result<EvalState> {
299 init()?;
301
302 let mut context = Context::new();
303
304 if self.load_ambient_settings {
307 unsafe {
308 check_call!(raw::eval_state_builder_load(
309 &mut context,
310 self.eval_state_builder
311 ))?;
312 }
313 }
314
315 let mut lookup_path: Vec<*const c_char> = self
317 .lookup_path
318 .iter()
319 .map(|s| s.as_ptr())
320 .chain(std::iter::once(null())) .collect();
322
323 unsafe {
324 check_call!(raw::eval_state_builder_set_lookup_path(
325 &mut context,
326 self.eval_state_builder,
327 lookup_path.as_mut_ptr()
328 ))?;
329 }
330
331 let eval_state =
332 unsafe { check_call!(raw::eval_state_build(&mut context, self.eval_state_builder)) }?;
333 Ok(EvalState {
334 eval_state: Arc::new(EvalStateRef {
335 eval_state: NonNull::new(eval_state).unwrap_or_else(|| {
336 panic!("nix_state_create returned a null pointer without an error")
337 }),
338 }),
339 store: self.store.clone(),
340 context,
341 })
342 }
343 pub fn raw_ptr(&self) -> *mut raw::eval_state_builder {
350 self.eval_state_builder
351 }
352}
353
354pub struct EvalState {
355 eval_state: Arc<EvalStateRef>,
356 store: Store,
357 pub(crate) context: Context,
358}
359impl EvalState {
360 pub fn new<'a>(store: Store, lookup_path: impl IntoIterator<Item = &'a str>) -> Result<Self> {
364 EvalStateBuilder::new(store)?
365 .lookup_path(lookup_path)?
366 .build()
367 }
368
369 pub unsafe fn raw_ptr(&self) -> *mut raw::EvalState {
375 self.eval_state.as_ptr()
376 }
377
378 pub fn store(&self) -> &Store {
380 &self.store
381 }
382
383 pub fn weak_ref(&self) -> EvalStateWeak {
385 EvalStateWeak {
386 inner: Arc::downgrade(&self.eval_state),
387 store: self.store.weak_ref(),
388 }
389 }
390
391 #[doc(alias = "nix_expr_eval_from_string")]
414 #[doc(alias = "parse")]
415 #[doc(alias = "eval")]
416 #[doc(alias = "evaluate")]
417 pub fn eval_from_string(&mut self, expr: &str, path: &str) -> Result<Value> {
418 let expr_ptr =
419 CString::new(expr).with_context(|| "eval_from_string: expr contains null byte")?;
420 let path_ptr =
421 CString::new(path).with_context(|| "eval_from_string: path contains null byte")?;
422 unsafe {
423 let value = self.new_value_uninitialized()?;
424 check_call!(raw::expr_eval_from_string(
425 &mut self.context,
426 self.eval_state.as_ptr(),
427 expr_ptr.as_ptr(),
428 path_ptr.as_ptr(),
429 value.raw_ptr()
430 ))?;
431 Ok(value)
432 }
433 }
434
435 #[doc(alias = "evaluate")]
443 #[doc(alias = "strict")]
444 pub fn force(&mut self, v: &Value) -> Result<()> {
445 unsafe {
446 check_call!(raw::value_force(
447 &mut self.context,
448 self.eval_state.as_ptr(),
449 v.raw_ptr()
450 ))
451 }?;
452 Ok(())
453 }
454
455 #[doc(alias = "type_of")]
463 #[doc(alias = "value_type_lazy")]
464 #[doc(alias = "nix_get_type")]
465 #[doc(alias = "get_type")]
466 #[doc(alias = "nix_value_type")]
467 pub fn value_type_unforced(&mut self, value: &Value) -> Option<ValueType> {
468 let r = unsafe { check_call!(raw::get_type(&mut self.context, value.raw_ptr())) };
469 ValueType::from_raw(r.unwrap())
471 }
472 #[doc(alias = "type_of")]
480 #[doc(alias = "value_type_strict")]
481 #[doc(alias = "nix_get_type")]
482 #[doc(alias = "get_type")]
483 #[doc(alias = "nix_value_type_strict")]
484 pub fn value_type(&mut self, value: &Value) -> Result<ValueType> {
485 match self.value_type_unforced(value) {
486 Some(a) => Ok(a),
487 None => {
488 self.force(value)?;
489 match self.value_type_unforced(value) {
490 Some(a) => Ok(a),
491 None => {
492 panic!("Nix value must not be thunk after being forced.")
493 }
494 }
495 }
496 }
497 }
498 #[doc(alias = "integer")]
524 #[doc(alias = "number")]
525 #[doc(alias = "nix_get_int")]
526 #[doc(alias = "get_int")]
527 pub fn require_int(&mut self, v: &Value) -> Result<Int> {
528 let t = self.value_type(v)?;
529 if t != ValueType::Int {
530 bail!("expected an int, but got a {:?}", t);
531 }
532 unsafe { check_call!(raw::get_int(&mut self.context, v.raw_ptr())) }
533 }
534
535 #[doc(alias = "boolean")]
541 #[doc(alias = "nix_get_bool")]
542 #[doc(alias = "get_bool")]
543 pub fn require_bool(&mut self, v: &Value) -> Result<bool> {
544 let t = self.value_type(v)?;
545 if t != ValueType::Bool {
546 bail!("expected a bool, but got a {:?}", t);
547 }
548 unsafe { check_call!(raw::get_bool(&mut self.context, v.raw_ptr())) }
549 }
550
551 #[doc(alias = "collect")]
572 #[doc(alias = "to_vec")]
573 #[doc(alias = "all")]
574 #[doc(alias = "nix_get_list_size")]
575 #[doc(alias = "nix_get_list_byidx")]
576 pub fn require_list_strict<C>(&mut self, value: &Value) -> Result<C>
577 where
578 C: FromIterator<Value>,
579 {
580 let t = self.value_type(value)?;
581 if t != ValueType::List {
582 bail!("expected a list, but got a {:?}", t);
583 }
584 let size = unsafe { check_call!(raw::get_list_size(&mut self.context, value.raw_ptr())) }?;
585
586 (0..size)
587 .map(|i| {
588 let element_ptr = unsafe {
589 check_call!(raw::get_list_byidx(
590 &mut self.context,
591 value.raw_ptr(),
592 self.eval_state.as_ptr(),
593 i
594 ))
595 }?;
596 Ok(unsafe { Value::new(element_ptr) })
597 })
598 .collect()
599 }
600
601 #[doc(alias = "keys")]
607 #[doc(alias = "attributes")]
608 #[doc(alias = "fields")]
609 pub fn require_attrs_names(&mut self, v: &Value) -> Result<Vec<String>> {
610 self.require_attrs_names_unsorted(v).map(|mut v| {
611 v.sort();
612 v
613 })
614 }
615
616 #[doc(alias = "keys_unsorted")]
620 #[doc(alias = "attributes_unsorted")]
621 pub fn require_attrs_names_unsorted(&mut self, v: &Value) -> Result<Vec<String>> {
622 let t = self.value_type(v)?;
623 if t != ValueType::AttrSet {
624 bail!("expected an attrset, but got a {:?}", t);
625 }
626 let n = unsafe { check_call!(raw::get_attrs_size(&mut self.context, v.raw_ptr())) }?;
627 let mut attrs = Vec::with_capacity(n as usize);
628 for i in 0..n {
629 let cstr_ptr: *const c_char = unsafe {
630 check_call!(raw::get_attr_name_byidx(
631 &mut self.context,
632 v.raw_ptr(),
633 self.eval_state.as_ptr(),
634 i as c_uint
635 ))
636 }?;
637 let cstr = unsafe { std::ffi::CStr::from_ptr(cstr_ptr) };
638 let s = cstr
639 .to_str()
640 .map_err(|e| anyhow::format_err!("Nix attrset key is not valid UTF-8: {}", e))?;
641 attrs.insert(i as usize, s.to_owned());
642 }
643 Ok(attrs)
644 }
645
646 #[doc(alias = "get_attr")]
652 #[doc(alias = "attribute")]
653 #[doc(alias = "field")]
654 pub fn require_attrs_select(&mut self, v: &Value, attr_name: &str) -> Result<Value> {
655 let t = self.value_type(v)?;
656 if t != ValueType::AttrSet {
657 bail!("expected an attrset, but got a {:?}", t);
658 }
659 let attr_name = CString::new(attr_name)
660 .with_context(|| "require_attrs_select: attrName contains null byte")?;
661 unsafe {
662 let v2 = check_call!(raw::get_attr_byname(
663 &mut self.context,
664 v.raw_ptr(),
665 self.eval_state.as_ptr(),
666 attr_name.as_ptr()
667 ));
668 match v2 {
669 Ok(v2) => Ok(Value::new(v2)),
670 Err(e) => {
671 if e.to_string() == "missing attribute" {
676 bail!("attribute `{}` not found", attr_name.to_string_lossy());
677 } else {
678 Err(e)
679 }
680 }
681 }
682 }
683 }
684
685 #[doc(alias = "nix_get_attr_byname")]
695 #[doc(alias = "get_attr_byname")]
696 #[doc(alias = "get_attr_opt")]
697 #[doc(alias = "try_get")]
698 #[doc(alias = "maybe_get")]
699 pub fn require_attrs_select_opt(
700 &mut self,
701 v: &Value,
702 attr_name: &str,
703 ) -> Result<Option<Value>> {
704 let t = self.value_type(v)?;
705 if t != ValueType::AttrSet {
706 bail!("expected an attrset, but got a {:?}", t);
707 }
708 let attr_name = CString::new(attr_name)
709 .with_context(|| "require_attrs_select_opt: attrName contains null byte")?;
710 let v2 = unsafe {
711 check_call_opt_key!(raw::get_attr_byname(
712 &mut self.context,
713 v.raw_ptr(),
714 self.eval_state.as_ptr(),
715 attr_name.as_ptr()
716 ))
717 }?;
718 Ok(v2.map(|x| unsafe { Value::new(x) }))
719 }
720
721 #[doc(alias = "length")]
727 #[doc(alias = "count")]
728 #[doc(alias = "len")]
729 #[doc(alias = "nix_get_list_size")]
730 #[doc(alias = "get_list_size")]
731 pub fn require_list_size(&mut self, v: &Value) -> Result<u32> {
732 let t = self.value_type(v)?;
733 if t != ValueType::List {
734 bail!("expected a list, but got a {:?}", t);
735 }
736 let ret = unsafe { check_call!(raw::get_list_size(&mut self.context, v.raw_ptr())) }?;
737 Ok(ret)
738 }
739
740 #[doc(alias = "get")]
751 #[doc(alias = "index")]
752 #[doc(alias = "at")]
753 #[doc(alias = "nix_get_list_byidx")]
754 #[doc(alias = "get_list_byidx")]
755 pub fn require_list_select_idx_strict(&mut self, v: &Value, idx: u32) -> Result<Option<Value>> {
756 let t = self.value_type(v)?;
757 if t != ValueType::List {
758 bail!("expected a list, but got a {:?}", t);
759 }
760
761 let size = unsafe { check_call!(raw::get_list_size(&mut self.context, v.raw_ptr())) }?;
765
766 if idx >= size {
767 return Ok(None);
768 }
769
770 let v2 = unsafe {
771 check_call_opt_key!(raw::get_list_byidx(
772 &mut self.context,
773 v.raw_ptr(),
774 self.eval_state.as_ptr(),
775 idx
776 ))
777 }?;
778 Ok(v2.map(|x| unsafe { Value::new(x) }))
779 }
780
781 #[doc(alias = "make_string")]
785 #[doc(alias = "create_string")]
786 #[doc(alias = "string_value")]
787 pub fn new_value_str(&mut self, s: &str) -> Result<Value> {
788 let s = CString::new(s).with_context(|| "new_value_str: contains null byte")?;
789 let v = unsafe {
790 let value = self.new_value_uninitialized()?;
791 check_call!(raw::init_string(
792 &mut self.context,
793 value.raw_ptr(),
794 s.as_ptr()
795 ))?;
796 value
797 };
798 Ok(v)
799 }
800
801 #[doc(alias = "make_int")]
803 #[doc(alias = "create_int")]
804 #[doc(alias = "int_value")]
805 #[doc(alias = "integer_value")]
806 pub fn new_value_int(&mut self, i: Int) -> Result<Value> {
807 let v = unsafe {
808 let value = self.new_value_uninitialized()?;
809 check_call!(raw::init_int(&mut self.context, value.raw_ptr(), i))?;
810 value
811 };
812 Ok(v)
813 }
814
815 #[doc(alias = "make_thunk")]
822 #[doc(alias = "create_thunk")]
823 #[doc(alias = "lazy_value")]
824 pub fn new_value_thunk(
825 &mut self,
826 name: &str,
827 f: Box<dyn Fn(&mut EvalState) -> Result<Value>>,
828 ) -> Result<Value> {
829 let name = CString::new(name).with_context(|| "new_thunk: name contains null byte")?;
832 let primop = primop::PrimOp::new(
833 self,
834 primop::PrimOpMeta {
835 name: name.as_c_str(),
837 doc: cstr!("Performs an on demand computation, implemented outside the Nix language in native code."),
839 args: [CString::new("internal_unused").unwrap().as_c_str()],
841 },
842 Box::new(move |eval_state, _dummy: &[Value; 1]| f(eval_state)),
843 )?;
844
845 let p = self.new_value_primop(primop)?;
846 self.new_value_apply(&p, &p)
847 }
848
849 fn get_string(&mut self, value: &Value) -> Result<String> {
851 let mut r = result_string_init!();
852 unsafe {
853 check_call!(raw::get_string(
854 &mut self.context,
855 value.raw_ptr(),
856 Some(callback_get_result_string),
857 callback_get_result_string_data(&mut r)
858 ))?;
859 };
860 r
861 }
862 #[doc(alias = "str")]
869 #[doc(alias = "text")]
870 #[doc(alias = "nix_get_string")]
871 #[doc(alias = "get_string")]
872 pub fn require_string(&mut self, value: &Value) -> Result<String> {
873 let t = self.value_type(value)?;
874 if t != ValueType::String {
875 bail!("expected a string, but got a {:?}", t);
876 }
877 self.get_string(value)
878 }
879 #[doc(alias = "realize_string")]
884 #[doc(alias = "string_with_context")]
885 #[doc(alias = "build_string")]
886 pub fn realise_string(
887 &mut self,
888 value: &Value,
889 is_import_from_derivation: bool,
890 ) -> Result<RealisedString> {
891 let t = self.value_type(value)?;
892 if t != ValueType::String {
893 bail!("expected a string, but got a {:?}", t);
894 }
895
896 let rs = unsafe {
897 check_call!(raw::string_realise(
898 &mut self.context,
899 self.eval_state.as_ptr(),
900 value.raw_ptr(),
901 is_import_from_derivation
902 ))
903 }?;
904
905 let s = unsafe {
906 let start = raw::realised_string_get_buffer_start(rs) as *const u8;
907 let size = raw::realised_string_get_buffer_size(rs);
908 let slice = std::slice::from_raw_parts(start, size);
909 String::from_utf8(slice.to_vec())
910 .map_err(|e| anyhow::format_err!("Nix string is not valid UTF-8: {}", e))?
911 };
912
913 let paths = unsafe {
914 let n = raw::realised_string_get_store_path_count(rs);
915 let mut paths = Vec::with_capacity(n as usize);
916 for i in 0..n {
917 let path = raw::realised_string_get_store_path(rs, i);
918 let path = NonNull::new(path as *mut raw_store::StorePath).ok_or_else(|| {
919 anyhow::format_err!(
920 "nix_realised_string_get_store_path returned a null pointer"
921 )
922 })?;
923 paths.push(StorePath::new_raw_clone(path));
924 }
925 paths
926 };
927
928 unsafe {
930 raw::realised_string_free(rs);
931 }
932
933 Ok(RealisedString { s, paths })
934 }
935
936 #[doc(alias = "nix_value_call")]
941 #[doc(alias = "value_call")]
942 #[doc(alias = "apply")]
943 #[doc(alias = "invoke")]
944 #[doc(alias = "execute")]
945 pub fn call(&mut self, f: Value, a: Value) -> Result<Value> {
946 let value = self.new_value_uninitialized()?;
947 unsafe {
948 check_call!(raw::value_call(
949 &mut self.context,
950 self.eval_state.as_ptr(),
951 f.raw_ptr(),
952 a.raw_ptr(),
953 value.raw_ptr()
954 ))
955 }?;
956 Ok(value)
957 }
958
959 #[doc(alias = "nix_value_call_multi")]
998 #[doc(alias = "value_call_multi")]
999 #[doc(alias = "apply_multi")]
1000 #[doc(alias = "curry")]
1001 #[doc(alias = "call_with_args")]
1002 pub fn call_multi(&mut self, f: &Value, args: &[Value]) -> Result<Value> {
1003 let value = self.new_value_uninitialized()?;
1004 unsafe {
1005 let mut args_ptrs = args.iter().map(|a| a.raw_ptr()).collect::<Vec<_>>();
1006 check_call!(raw::value_call_multi(
1007 &mut self.context,
1008 self.eval_state.as_ptr(),
1009 f.raw_ptr(),
1010 args_ptrs.len(),
1011 args_ptrs.as_mut_ptr(),
1012 value.raw_ptr()
1013 ))
1014 }?;
1015 Ok(value)
1016 }
1017
1018 #[doc(alias = "lazy_apply")]
1023 #[doc(alias = "thunk_apply")]
1024 #[doc(alias = "defer_call")]
1025 pub fn new_value_apply(&mut self, f: &Value, a: &Value) -> Result<Value> {
1026 let value = self.new_value_uninitialized()?;
1027 unsafe {
1028 check_call!(raw::init_apply(
1029 &mut self.context,
1030 value.raw_ptr(),
1031 f.raw_ptr(),
1032 a.raw_ptr()
1033 ))
1034 }?;
1035 Ok(value)
1036 }
1037
1038 fn new_value_uninitialized(&mut self) -> Result<Value> {
1039 unsafe {
1040 let value = check_call!(raw::alloc_value(
1041 &mut self.context,
1042 self.eval_state.as_ptr()
1043 ))?;
1044 Ok(Value::new(value))
1045 }
1046 }
1047
1048 #[doc(alias = "make_primop")]
1054 #[doc(alias = "create_function")]
1055 #[doc(alias = "builtin")]
1056 pub fn new_value_primop(&mut self, primop: primop::PrimOp) -> Result<Value> {
1057 let value = self.new_value_uninitialized()?;
1058 unsafe {
1059 check_call!(raw::init_primop(
1060 &mut self.context,
1061 value.raw_ptr(),
1062 primop.ptr
1063 ))?;
1064 };
1065 Ok(value)
1066 }
1067
1068 #[doc(alias = "make_attrs")]
1105 #[doc(alias = "create_attrset")]
1106 #[doc(alias = "object")]
1107 #[doc(alias = "record")]
1108 pub fn new_value_attrs<I>(&mut self, attrs: I) -> Result<Value>
1109 where
1110 I: IntoIterator<Item = (String, Value)>,
1111 I::IntoIter: ExactSizeIterator,
1112 {
1113 let iter = attrs.into_iter();
1114 let size = iter.len();
1115 let bindings_builder = BindingsBuilder::new(self, size)?;
1116 for (name, value) in iter {
1117 let name =
1118 CString::new(name).with_context(|| "new_value_attrs: name contains null byte")?;
1119 unsafe {
1120 check_call!(raw::bindings_builder_insert(
1121 &mut self.context,
1122 bindings_builder.ptr,
1123 name.as_ptr(),
1124 value.raw_ptr()
1125 ))?;
1126 }
1127 }
1128 let value = self.new_value_uninitialized()?;
1129 unsafe {
1130 check_call!(raw::make_attrs(
1131 &mut self.context,
1132 value.raw_ptr(),
1133 bindings_builder.ptr
1134 ))?;
1135 }
1136 Ok(value)
1137 }
1138}
1139
1140struct BindingsBuilder {
1142 ptr: *mut raw::BindingsBuilder,
1143}
1144impl Drop for BindingsBuilder {
1145 fn drop(&mut self) {
1146 unsafe {
1147 raw::bindings_builder_free(self.ptr);
1148 }
1149 }
1150}
1151impl BindingsBuilder {
1152 fn new(eval_state: &mut EvalState, capacity: usize) -> Result<Self> {
1153 let ptr = unsafe {
1154 check_call!(raw::make_bindings_builder(
1155 &mut eval_state.context,
1156 eval_state.eval_state.as_ptr(),
1157 capacity
1158 ))
1159 }?;
1160 Ok(BindingsBuilder { ptr })
1161 }
1162}
1163
1164#[doc(alias = "garbage_collect")]
1166#[doc(alias = "collect")]
1167#[doc(alias = "gc")]
1168pub fn gc_now() {
1169 unsafe {
1170 raw::gc_now();
1171 }
1172}
1173
1174pub struct ThreadRegistrationGuard {
1178 must_unregister: bool,
1179}
1180impl Drop for ThreadRegistrationGuard {
1181 fn drop(&mut self) {
1182 if self.must_unregister {
1183 unsafe {
1184 gc::GC_unregister_my_thread();
1185 }
1186 }
1187 }
1188}
1189
1190fn gc_register_my_thread_do_it() -> Result<()> {
1191 unsafe {
1192 let mut sb: gc::GC_stack_base = gc::GC_stack_base {
1193 mem_base: null_mut(),
1194 };
1195 let r = gc::GC_get_stack_base(&mut sb);
1196 if r as u32 != gc::GC_SUCCESS {
1197 Err(anyhow::format_err!("GC_get_stack_base failed: {}", r))?;
1198 }
1199 gc::GC_register_my_thread(&sb);
1200 Ok(())
1201 }
1202}
1203
1204#[doc(alias = "register_thread")]
1205#[doc(alias = "thread_setup")]
1206#[doc(alias = "gc_register")]
1207pub fn gc_register_my_thread() -> Result<ThreadRegistrationGuard> {
1208 init()?;
1209 unsafe {
1210 let already_done = gc::GC_thread_is_registered();
1211 if already_done != 0 {
1212 return Ok(ThreadRegistrationGuard {
1213 must_unregister: false,
1214 });
1215 }
1216 gc_register_my_thread_do_it()?;
1217 Ok(ThreadRegistrationGuard {
1218 must_unregister: true,
1219 })
1220 }
1221}
1222
1223impl Clone for EvalState {
1224 fn clone(&self) -> Self {
1225 EvalState {
1226 eval_state: self.eval_state.clone(),
1227 store: self.store.clone(),
1228 context: Context::new(),
1229 }
1230 }
1231}
1232
1233#[doc(alias = "test_initialize")]
1236#[doc(alias = "test_setup")]
1237pub fn test_init() {
1238 init().unwrap();
1239
1240 nix_bindings_util::settings::set("build-hook", "").unwrap();
1245
1246 if cfg!(target_os = "linux") {
1250 nix_bindings_util::settings::set("sandbox-build-dir", "/custom-build-dir-for-test")
1251 .unwrap();
1252 }
1253 std::env::set_var("_NIX_TEST_NO_SANDBOX", "1");
1254
1255 nix_bindings_util::settings::set("substituters", "").unwrap();
1257}
1258
1259#[cfg(test)]
1260mod tests {
1261 use super::*;
1262 use cstr::cstr;
1263 use ctor::ctor;
1264 use std::collections::HashMap;
1265 use std::fs::read_dir;
1266 use std::io::Write as _;
1267 use std::sync::{Arc, Mutex};
1268
1269 #[ctor]
1270 fn setup() {
1271 test_init();
1272
1273 std::env::set_var("NIX_CONFIG", "max-call-depth = 1000");
1278 }
1279
1280 pub fn gc_registering_current_thread<F, R>(f: F) -> Result<R>
1282 where
1283 F: FnOnce() -> R,
1284 {
1285 let guard = gc_register_my_thread()?;
1286 let r = f();
1287 drop(guard);
1288 Ok(r)
1289 }
1290
1291 #[test]
1292 fn eval_state_new_and_drop() {
1293 gc_registering_current_thread(|| {
1294 let store = Store::open(None, HashMap::new()).unwrap();
1296 let _e = EvalState::new(store, []).unwrap();
1297 })
1298 .unwrap();
1299 }
1300
1301 #[test]
1302 fn weak_ref() {
1303 gc_registering_current_thread(|| {
1304 let store = Store::open(None, HashMap::new()).unwrap();
1305 let es = EvalState::new(store, []).unwrap();
1306 let weak = es.weak_ref();
1307 let _es = weak.upgrade().unwrap();
1308 })
1309 .unwrap();
1310 }
1311
1312 #[test]
1313 fn weak_ref_gone() {
1314 gc_registering_current_thread(|| {
1315 let weak = {
1316 let store = Store::open(Some("auto?foo=bar"), HashMap::new()).unwrap();
1318 let es = EvalState::new(store, []).unwrap();
1319 es.weak_ref()
1320 };
1321 assert!(weak.upgrade().is_none());
1322 assert!(weak.store.upgrade().is_none());
1323 assert!(weak.inner.upgrade().is_none());
1324 })
1325 .unwrap();
1326 }
1327
1328 #[test]
1329 fn eval_state_lookup_path() {
1330 let import_expression = "import <test_file0> + import <test_file1>";
1331 let integer0 = 83;
1332 let integer1 = 103;
1333 let mut test_file0 = tempfile::NamedTempFile::new().unwrap();
1334 let mut test_file1 = tempfile::NamedTempFile::new().unwrap();
1335 writeln!(test_file0, "{integer0}").unwrap();
1336 writeln!(test_file1, "{integer1}").unwrap();
1337 gc_registering_current_thread(|| {
1338 let mut es = EvalState::new(Store::open(None, HashMap::new()).unwrap(), []).unwrap();
1339 assert!(es.eval_from_string(import_expression, "<test>").is_err());
1340
1341 let mut es = EvalState::new(
1342 Store::open(None, HashMap::new()).unwrap(),
1343 [
1344 format!("test_file0={}", test_file0.path().to_str().unwrap()).as_str(),
1345 format!("test_file1={}", test_file1.path().to_str().unwrap()).as_str(),
1346 ],
1347 )
1348 .unwrap();
1349 let ie = &es.eval_from_string(import_expression, "<test>").unwrap();
1350 let v = es.require_int(ie).unwrap();
1351 assert_eq!(v, integer0 + integer1);
1352 })
1353 .unwrap();
1354 test_file0.close().unwrap();
1355 test_file1.close().unwrap();
1356 }
1357
1358 #[test]
1359 fn eval_state_eval_from_string() {
1360 gc_registering_current_thread(|| {
1361 let store = Store::open(None, HashMap::new()).unwrap();
1362 let mut es = EvalState::new(store, []).unwrap();
1363 let v = es.eval_from_string("1", "<test>").unwrap();
1364 let v2 = v.clone();
1365 es.force(&v).unwrap();
1366 let t = es.value_type_unforced(&v);
1367 assert!(t == Some(ValueType::Int));
1368 let t2 = es.value_type_unforced(&v2);
1369 assert!(t2 == Some(ValueType::Int));
1370 gc_now();
1371 })
1372 .unwrap();
1373 }
1374
1375 #[test]
1376 fn eval_state_value_bool() {
1377 gc_registering_current_thread(|| {
1378 let store = Store::open(None, HashMap::new()).unwrap();
1379 let mut es = EvalState::new(store, []).unwrap();
1380 let v = es.eval_from_string("true", "<test>").unwrap();
1381 es.force(&v).unwrap();
1382 let t = es.value_type_unforced(&v);
1383 assert!(t == Some(ValueType::Bool));
1384 let b = es.require_bool(&v).unwrap();
1385 assert!(b);
1386
1387 let v = es.eval_from_string("false", "<test>").unwrap();
1388 es.require_bool(&v).unwrap();
1389 let b = es.require_bool(&v).unwrap();
1390 assert!(!b);
1391 })
1392 .unwrap();
1393 }
1394
1395 #[test]
1396 fn eval_state_value_int() {
1397 gc_registering_current_thread(|| {
1398 let store = Store::open(None, HashMap::new()).unwrap();
1399 let mut es = EvalState::new(store, []).unwrap();
1400 let v = es.eval_from_string("1", "<test>").unwrap();
1401 es.force(&v).unwrap();
1402 let t = es.value_type(&v).unwrap();
1403 assert!(t == ValueType::Int);
1404 let i = es.require_int(&v).unwrap();
1405 assert!(i == 1);
1406 })
1407 .unwrap();
1408 }
1409
1410 #[test]
1411 fn eval_state_require_int_forces_thunk() {
1412 gc_registering_current_thread(|| {
1413 let store = Store::open(None, HashMap::new()).unwrap();
1414 let mut es = EvalState::new(store, []).unwrap();
1415 let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
1416 let a = es.eval_from_string("2", "<test>").unwrap();
1417 let v = es.new_value_apply(&f, &a).unwrap();
1418 let t = es.value_type_unforced(&v);
1419 assert!(t.is_none());
1420 let i = es.require_int(&v).unwrap();
1421 assert!(i == 3);
1422 })
1423 .unwrap();
1424 }
1425
1426 #[test]
1427 fn eval_state_require_bool_forces_thunk() {
1428 gc_registering_current_thread(|| {
1429 let store = Store::open(None, HashMap::new()).unwrap();
1430 let mut es = EvalState::new(store, []).unwrap();
1431 let f = es.eval_from_string("x: !x", "<test>").unwrap();
1432 let a = es.eval_from_string("true", "<test>").unwrap();
1433 let v = es.new_value_apply(&f, &a).unwrap();
1434 let t = es.value_type_unforced(&v);
1435 assert!(t.is_none());
1436 let i = es.require_bool(&v).unwrap();
1437 assert!(!i);
1438 })
1439 .unwrap();
1440 }
1441
1442 fn make_thunk(es: &mut EvalState, expr: &str) -> Value {
1444 let f = es.eval_from_string("x: x", "<test>").unwrap();
1449 let v = es.eval_from_string(expr, "<test>").unwrap();
1450 es.new_value_apply(&f, &v).unwrap()
1451 }
1452
1453 #[test]
1454 fn make_thunk_helper_works() {
1455 gc_registering_current_thread(|| {
1456 let store = Store::open(None, HashMap::new()).unwrap();
1457 let mut es = EvalState::new(store, []).unwrap();
1458 let v = make_thunk(&mut es, "1");
1459 let t = es.value_type_unforced(&v);
1460 assert!(t.is_none());
1461 })
1462 .unwrap();
1463 }
1464
1465 #[test]
1466 fn eval_state_value_attrs_names_empty() {
1467 gc_registering_current_thread(|| {
1468 let store = Store::open(None, HashMap::new()).unwrap();
1469 let mut es = EvalState::new(store, []).unwrap();
1470 let v = es.eval_from_string("{ }", "<test>").unwrap();
1471 es.force(&v).unwrap();
1472 let t = es.value_type_unforced(&v);
1473 assert!(t == Some(ValueType::AttrSet));
1474 let attrs = es.require_attrs_names_unsorted(&v).unwrap();
1475 assert_eq!(attrs.len(), 0);
1476 })
1477 .unwrap()
1478 }
1479
1480 #[test]
1481 fn eval_state_require_attrs_names_unsorted_forces_thunk() {
1482 gc_registering_current_thread(|| {
1483 let store = Store::open(None, HashMap::new()).unwrap();
1484 let mut es = EvalState::new(store, []).unwrap();
1485 let v = make_thunk(&mut es, "{ a = 1; b = 2; }");
1486 let t = es.value_type_unforced(&v);
1487 assert!(t.is_none());
1488 let attrs = es.require_attrs_names_unsorted(&v).unwrap();
1489 assert_eq!(attrs.len(), 2);
1490 })
1491 .unwrap()
1492 }
1493
1494 #[test]
1495 fn eval_state_require_attrs_names_unsorted_bad_type() {
1496 gc_registering_current_thread(|| {
1497 let store = Store::open(None, HashMap::new()).unwrap();
1498 let mut es = EvalState::new(store, []).unwrap();
1499 let v = es.eval_from_string("1", "<test>").unwrap();
1500 es.force(&v).unwrap();
1501 let r = es.require_attrs_names_unsorted(&v);
1502 assert!(r.is_err());
1503 assert_eq!(
1504 r.unwrap_err().to_string(),
1505 "expected an attrset, but got a Int"
1506 );
1507 })
1508 .unwrap()
1509 }
1510
1511 #[test]
1512 fn eval_state_value_attrs_names_example() {
1513 gc_registering_current_thread(|| {
1514 let store = Store::open(None, HashMap::new()).unwrap();
1515 let mut es = EvalState::new(store, []).unwrap();
1516 let expr = r#"{ a = throw "nope a"; b = throw "nope b"; }"#;
1517 let v = es.eval_from_string(expr, "<test>").unwrap();
1518 let attrs = es.require_attrs_names(&v).unwrap();
1519 assert_eq!(attrs.len(), 2);
1520 assert_eq!(attrs[0], "a");
1521 assert_eq!(attrs[1], "b");
1522 })
1523 .unwrap();
1524 }
1525
1526 #[test]
1527 fn eval_state_require_attrs_select() {
1528 gc_registering_current_thread(|| {
1529 let store = Store::open(None, HashMap::new()).unwrap();
1530 let mut es = EvalState::new(store, []).unwrap();
1531 let expr = r#"{ a = "aye"; b = "bee"; }"#;
1532 let v = es.eval_from_string(expr, "<test>").unwrap();
1533 let a = es.require_attrs_select(&v, "a").unwrap();
1534 let b = es.require_attrs_select(&v, "b").unwrap();
1535 assert_eq!(es.require_string(&a).unwrap(), "aye");
1536 assert_eq!(es.require_string(&b).unwrap(), "bee");
1537 let missing = es.require_attrs_select(&v, "c");
1538 match missing {
1539 Ok(_) => panic!("expected an error"),
1540 Err(e) => {
1541 let s = format!("{e:#}");
1542 if !s.contains("attribute `c` not found") {
1543 eprintln!("unexpected error message: {}", s);
1544 panic!();
1545 }
1546 }
1547 }
1548 })
1549 .unwrap()
1550 }
1551
1552 #[test]
1553 fn eval_state_require_attrs_select_forces_thunk() {
1554 gc_registering_current_thread(|| {
1555 let store = Store::open(None, HashMap::new()).unwrap();
1556 let mut es = EvalState::new(store, []).unwrap();
1557 let expr = r#"{ a = "aye"; b = "bee"; }"#;
1558 let v = make_thunk(&mut es, expr);
1559 assert!(es.value_type_unforced(&v).is_none());
1560 let r = es.require_attrs_select(&v, "a");
1561 assert!(r.is_ok());
1562 })
1563 .unwrap()
1564 }
1565
1566 #[test]
1567 fn eval_state_require_attrs_select_error() {
1568 gc_registering_current_thread(|| {
1569 let store = Store::open(None, HashMap::new()).unwrap();
1570 let mut es = EvalState::new(store, []).unwrap();
1571 let expr = r#"{ a = throw "oh no the error"; }"#;
1572 let v = es.eval_from_string(expr, "<test>").unwrap();
1573 let r = es.require_attrs_select(&v, "a");
1574 match r {
1575 Ok(_) => panic!("expected an error"),
1576 Err(e) => {
1577 if !e.to_string().contains("oh no the error") {
1578 eprintln!("unexpected error message: {}", e);
1579 panic!();
1580 }
1581 }
1582 }
1583 })
1584 .unwrap()
1585 }
1586
1587 #[test]
1588 fn eval_state_require_attrs_select_opt() {
1589 gc_registering_current_thread(|| {
1590 let store = Store::open(None, HashMap::new()).unwrap();
1591 let mut es = EvalState::new(store, []).unwrap();
1592 let expr = r#"{ a = "aye"; b = "bee"; }"#;
1593 let v = es.eval_from_string(expr, "<test>").unwrap();
1594 let a = es.require_attrs_select_opt(&v, "a").unwrap().unwrap();
1595 let b = es.require_attrs_select_opt(&v, "b").unwrap().unwrap();
1596 assert_eq!(es.require_string(&a).unwrap(), "aye");
1597 assert_eq!(es.require_string(&b).unwrap(), "bee");
1598 let c = es.require_attrs_select_opt(&v, "c").unwrap();
1599 assert!(c.is_none());
1600 })
1601 .unwrap()
1602 }
1603
1604 #[test]
1605 fn eval_state_require_attrs_select_opt_forces_thunk() {
1606 gc_registering_current_thread(|| {
1607 let store = Store::open(None, HashMap::new()).unwrap();
1608 let mut es = EvalState::new(store, []).unwrap();
1609 let expr = r#"{ a = "aye"; b = "bee"; }"#;
1610 let v = make_thunk(&mut es, expr);
1611 assert!(es.value_type_unforced(&v).is_none());
1612 let r = es.require_attrs_select_opt(&v, "a");
1613 assert!(r.is_ok());
1614 })
1615 .unwrap()
1616 }
1617
1618 #[test]
1619 fn eval_state_require_attrs_select_opt_error() {
1620 gc_registering_current_thread(|| {
1621 let store = Store::open(None, HashMap::new()).unwrap();
1622 let mut es = EvalState::new(store, []).unwrap();
1623 let expr = r#"{ a = throw "oh no the error"; }"#;
1624 let v = es.eval_from_string(expr, "<test>").unwrap();
1625 let r = es.require_attrs_select_opt(&v, "a");
1626 match r {
1627 Ok(_) => panic!("expected an error"),
1628 Err(e) => {
1629 if !e.to_string().contains("oh no the error") {
1630 eprintln!("unexpected error message: {}", e);
1631 panic!();
1632 }
1633 }
1634 }
1635 })
1636 .unwrap()
1637 }
1638
1639 #[test]
1640 fn eval_state_value_string() {
1641 gc_registering_current_thread(|| {
1642 let store = Store::open(None, HashMap::new()).unwrap();
1643 let mut es = EvalState::new(store, []).unwrap();
1644 let v = es.eval_from_string("\"hello\"", "<test>").unwrap();
1645 es.force(&v).unwrap();
1646 let t = es.value_type_unforced(&v);
1647 assert!(t == Some(ValueType::String));
1648 let s = es.require_string(&v).unwrap();
1649 assert!(s == "hello");
1650 })
1651 .unwrap();
1652 }
1653
1654 #[test]
1655 fn eval_state_value_string_forces_thunk() {
1656 gc_registering_current_thread(|| {
1657 let store = Store::open(None, HashMap::new()).unwrap();
1658 let mut es = EvalState::new(store, []).unwrap();
1659 let v = make_thunk(&mut es, "\"hello\"");
1660 assert!(es.value_type_unforced(&v).is_none());
1661 let s = es.require_string(&v).unwrap();
1662 assert!(s == "hello");
1663 })
1664 .unwrap();
1665 }
1666
1667 #[test]
1668 fn eval_state_value_string_unexpected_bool() {
1669 gc_registering_current_thread(|| {
1670 let store = Store::open(None, HashMap::new()).unwrap();
1671 let mut es = EvalState::new(store, []).unwrap();
1672 let v = es.eval_from_string("true", "<test>").unwrap();
1673 es.force(&v).unwrap();
1674 let r = es.require_string(&v);
1675 assert!(r.is_err());
1676 assert_eq!(
1678 r.unwrap_err().to_string(),
1679 "expected a string, but got a Bool"
1680 );
1681 })
1682 .unwrap()
1683 }
1684
1685 #[test]
1686 fn eval_state_value_string_unexpected_path_value() {
1687 gc_registering_current_thread(|| {
1688 let store = Store::open(None, HashMap::new()).unwrap();
1689 let mut es = EvalState::new(store, []).unwrap();
1690 let v = es.eval_from_string("/foo", "<test>").unwrap();
1691 es.force(&v).unwrap();
1692 let r = es.require_string(&v);
1693 assert!(r.is_err());
1694 assert_eq!(
1695 r.unwrap_err().to_string(),
1696 "expected a string, but got a Path"
1697 );
1698 })
1699 .unwrap()
1700 }
1701
1702 #[test]
1703 fn eval_state_value_string_bad_utf() {
1704 gc_registering_current_thread(|| {
1705 let store = Store::open(None, HashMap::new()).unwrap();
1706 let mut es = EvalState::new(store, []).unwrap();
1707 let v = es
1708 .eval_from_string("builtins.substring 0 1 \"ü\"", "<test>")
1709 .unwrap();
1710 es.force(&v).unwrap();
1711 let t = es.value_type_unforced(&v);
1712 assert!(t == Some(ValueType::String));
1713 let r = es.require_string(&v);
1714 assert!(r.is_err());
1715 assert!(r
1716 .unwrap_err()
1717 .to_string()
1718 .contains("Nix string is not valid UTF-8"));
1719 })
1720 .unwrap();
1721 }
1722
1723 #[test]
1724 fn eval_state_value_string_unexpected_context() {
1725 gc_registering_current_thread(|| {
1726 let store = Store::open(None, HashMap::new()).unwrap();
1727 let mut es = EvalState::new(store, []).unwrap();
1728 let v = es
1729 .eval_from_string("(derivation { name = \"hello\"; system = \"dummy\"; builder = \"cmd.exe\"; }).outPath", "<test>")
1730 .unwrap();
1731 es.force(&v).unwrap();
1732 let t = es.value_type_unforced(&v);
1733 assert!(t == Some(ValueType::String));
1734 })
1739 .unwrap();
1740 }
1741
1742 #[test]
1743 fn eval_state_new_string() {
1744 gc_registering_current_thread(|| {
1745 let store = Store::open(None, HashMap::new()).unwrap();
1746 let mut es = EvalState::new(store, []).unwrap();
1747 let v = es.new_value_str("hello").unwrap();
1748 es.force(&v).unwrap();
1749 let t = es.value_type_unforced(&v);
1750 assert!(t == Some(ValueType::String));
1751 let s = es.require_string(&v).unwrap();
1752 assert!(s == "hello");
1753 })
1754 .unwrap();
1755 }
1756
1757 #[test]
1758 fn eval_state_new_string_empty() {
1759 gc_registering_current_thread(|| {
1760 let store = Store::open(None, HashMap::new()).unwrap();
1761 let mut es = EvalState::new(store, []).unwrap();
1762 let v = es.new_value_str("").unwrap();
1763 es.force(&v).unwrap();
1764 let t = es.value_type_unforced(&v);
1765 assert!(t == Some(ValueType::String));
1766 let s = es.require_string(&v).unwrap();
1767 assert!(s.is_empty());
1768 })
1769 .unwrap();
1770 }
1771
1772 #[test]
1773 fn eval_state_new_string_invalid() {
1774 gc_registering_current_thread(|| {
1775 let store = Store::open(None, HashMap::new()).unwrap();
1776 let mut es = EvalState::new(store, []).unwrap();
1777 let r = es.new_value_str("hell\0no");
1778 match r {
1779 Ok(_) => panic!("expected an error"),
1780 Err(e) => {
1781 if !e.to_string().contains("contains null byte") {
1782 eprintln!("{}", e);
1783 panic!();
1784 }
1785 }
1786 }
1787 })
1788 .unwrap();
1789 }
1790
1791 #[test]
1792 fn eval_state_new_int() {
1793 gc_registering_current_thread(|| {
1794 let store = Store::open(None, HashMap::new()).unwrap();
1795 let mut es = EvalState::new(store, []).unwrap();
1796 let v = es.new_value_int(42).unwrap();
1797 es.force(&v).unwrap();
1798 let t = es.value_type_unforced(&v);
1799 assert!(t == Some(ValueType::Int));
1800 let i = es.require_int(&v).unwrap();
1801 assert!(i == 42);
1802 })
1803 .unwrap();
1804 }
1805
1806 #[test]
1807 fn eval_state_value_attrset() {
1808 gc_registering_current_thread(|| {
1809 let store = Store::open(None, HashMap::new()).unwrap();
1810 let mut es = EvalState::new(store, []).unwrap();
1811 let v = es.eval_from_string("{ }", "<test>").unwrap();
1812 es.force(&v).unwrap();
1813 let t = es.value_type_unforced(&v);
1814 assert!(t == Some(ValueType::AttrSet));
1815 })
1816 .unwrap();
1817 }
1818
1819 #[test]
1820 fn eval_state_value_list() {
1821 gc_registering_current_thread(|| {
1822 let store = Store::open(None, HashMap::new()).unwrap();
1823 let mut es = EvalState::new(store, []).unwrap();
1824 let v = es.eval_from_string("[ ]", "<test>").unwrap();
1825 es.force(&v).unwrap();
1826 let t = es.value_type_unforced(&v);
1827 assert!(t == Some(ValueType::List));
1828 })
1829 .unwrap();
1830 }
1831
1832 #[test]
1833 fn eval_state_value_list_strict_empty() {
1834 gc_registering_current_thread(|| {
1835 let store = Store::open(None, HashMap::new()).unwrap();
1836 let mut es = EvalState::new(store, []).unwrap();
1837 let v = es.eval_from_string("[]", "<test>").unwrap();
1838 es.force(&v).unwrap();
1839 let list: Vec<Value> = es.require_list_strict(&v).unwrap();
1840 assert_eq!(list.len(), 0);
1841 })
1842 .unwrap();
1843 }
1844
1845 #[test]
1846 fn eval_state_value_list_strict_int() {
1847 gc_registering_current_thread(|| {
1848 let store = Store::open(None, HashMap::new()).unwrap();
1849 let mut es = EvalState::new(store, []).unwrap();
1850 let v = es.eval_from_string("[42]", "<test>").unwrap();
1851 es.force(&v).unwrap();
1852 let list: Vec<Value> = es.require_list_strict(&v).unwrap();
1853 assert_eq!(list.len(), 1);
1854 assert_eq!(es.require_int(&list[0]).unwrap(), 42);
1855 })
1856 .unwrap();
1857 }
1858
1859 #[test]
1860 fn eval_state_value_list_strict_int_bool() {
1861 gc_registering_current_thread(|| {
1862 let store = Store::open(None, HashMap::new()).unwrap();
1863 let mut es = EvalState::new(store, []).unwrap();
1864 let v = es.eval_from_string("[42 true]", "<test>").unwrap();
1865 es.force(&v).unwrap();
1866 let list: Vec<Value> = es.require_list_strict(&v).unwrap();
1867 assert_eq!(list.len(), 2);
1868 assert_eq!(es.require_int(&list[0]).unwrap(), 42);
1869 assert!(es.require_bool(&list[1]).unwrap());
1870 })
1871 .unwrap();
1872 }
1873
1874 #[test]
1875 fn eval_state_value_list_strict_error() {
1876 gc_registering_current_thread(|| {
1877 let store = Store::open(None, HashMap::new()).unwrap();
1878 let mut es = EvalState::new(store, []).unwrap();
1879 let v = es.eval_from_string(r#"[(throw "_evaluated_item_")]"#, "<test>").unwrap();
1880 es.force(&v).unwrap();
1881 let result: Result<Vec<Value>, _> = es.require_list_strict(&v);
1883 assert!(result.is_err());
1884 match result {
1885 Err(error_msg) => {
1886 let error_str = error_msg.to_string();
1887 assert!(error_str.contains("_evaluated_item_"));
1888 }
1889 Ok(_) => panic!("unexpected success. The item should have been evaluated and its error propagated.")
1890 }
1891 })
1892 .unwrap();
1893 }
1894
1895 #[test]
1896 fn eval_state_value_list_strict_generic_container() {
1897 gc_registering_current_thread(|| {
1898 let store = Store::open(None, HashMap::new()).unwrap();
1899 let mut es = EvalState::new(store, []).unwrap();
1900 let v = es.eval_from_string("[1 2 3]", "<test>").unwrap();
1901
1902 let vec: Vec<Value> = es.require_list_strict(&v).unwrap();
1904 assert_eq!(vec.len(), 3);
1905
1906 let deque: std::collections::VecDeque<Value> = es.require_list_strict(&v).unwrap();
1908 assert_eq!(deque.len(), 3);
1909
1910 assert_eq!(es.require_int(&vec[0]).unwrap(), 1);
1912 assert_eq!(es.require_int(&deque[0]).unwrap(), 1);
1913 })
1914 .unwrap();
1915 }
1916
1917 #[test]
1918 fn eval_state_realise_string() {
1919 gc_registering_current_thread(|| {
1920 let store = Store::open(None, HashMap::new()).unwrap();
1921 let mut es = EvalState::new(store, []).unwrap();
1922 let expr = r#"
1923 ''
1924 a derivation output: ${
1925 derivation { name = "letsbuild";
1926 system = builtins.currentSystem;
1927 builder = "/bin/sh";
1928 args = [ "-c" "echo foo > $out" ];
1929 }}
1930 a path: ${builtins.toFile "just-a-file" "ooh file good"}
1931 a derivation path by itself: ${
1932 builtins.unsafeDiscardOutputDependency
1933 (derivation {
1934 name = "not-actually-built-yet";
1935 system = builtins.currentSystem;
1936 builder = "/bin/sh";
1937 args = [ "-c" "echo foo > $out" ];
1938 }).drvPath}
1939 ''
1940 "#;
1941 let v = es.eval_from_string(expr, "<test>").unwrap();
1942 es.force(&v).unwrap();
1943 let rs = es.realise_string(&v, false).unwrap();
1944
1945 assert!(rs.s.starts_with("a derivation output:"));
1946 assert!(rs.s.contains("-letsbuild\n"));
1947 assert!(!rs.s.contains("-letsbuild.drv"));
1948 assert!(rs.s.contains("a path:"));
1949 assert!(rs.s.contains("-just-a-file"));
1950 assert!(!rs.s.contains("-just-a-file.drv"));
1951 assert!(!rs.s.contains("ooh file good"));
1952 assert!(rs.s.ends_with("-not-actually-built-yet.drv\n"));
1953
1954 assert_eq!(rs.paths.len(), 3);
1955 let mut names: Vec<String> = rs.paths.iter().map(|p| p.name().unwrap()).collect();
1956 names.sort();
1957 assert_eq!(names[0], "just-a-file");
1958 assert_eq!(names[1], "letsbuild");
1959 assert_eq!(names[2], "not-actually-built-yet.drv");
1960 })
1961 .unwrap();
1962 }
1963
1964 #[test]
1965 fn eval_state_call() {
1966 gc_registering_current_thread(|| {
1967 let store = Store::open(None, HashMap::new()).unwrap();
1968 let mut es = EvalState::new(store, []).unwrap();
1969 let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
1970 let a = es.eval_from_string("2", "<test>").unwrap();
1971 let v = es.call(f, a).unwrap();
1972 es.force(&v).unwrap();
1973 let t = es.value_type_unforced(&v);
1974 assert!(t == Some(ValueType::Int));
1975 let i = es.require_int(&v).unwrap();
1976 assert!(i == 3);
1977 })
1978 .unwrap();
1979 }
1980
1981 #[test]
1982 fn eval_state_call_multi() {
1983 gc_registering_current_thread(|| {
1984 let store = Store::open(None, HashMap::new()).unwrap();
1985 let mut es = EvalState::new(store, []).unwrap();
1986 let f = es.eval_from_string("x: y: x - y", "<test>").unwrap();
1988 let a = es.eval_from_string("2", "<test>").unwrap();
1989 let b = es.eval_from_string("3", "<test>").unwrap();
1990 let v = es.call_multi(&f, &[a, b]).unwrap();
1991 let t = es.value_type_unforced(&v);
1992 assert!(t == Some(ValueType::Int));
1993 let i = es.require_int(&v).unwrap();
1994 assert!(i == -1);
1995 })
1996 .unwrap();
1997 }
1998
1999 #[test]
2000 fn eval_state_apply() {
2001 gc_registering_current_thread(|| {
2002 let store = Store::open(None, HashMap::new()).unwrap();
2003 let mut es = EvalState::new(store, []).unwrap();
2004 let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
2006 let a = es.eval_from_string("2", "<test>").unwrap();
2007 let v = es.new_value_apply(&f, &a).unwrap();
2008 assert!(es.value_type_unforced(&v).is_none());
2009 es.force(&v).unwrap();
2010 let t = es.value_type_unforced(&v);
2011 assert!(t == Some(ValueType::Int));
2012 let i = es.require_int(&v).unwrap();
2013 assert!(i == 3);
2014 })
2015 .unwrap();
2016 }
2017
2018 #[test]
2019 fn eval_state_call_fail_body() {
2020 gc_registering_current_thread(|| {
2021 let store = Store::open(None, HashMap::new()).unwrap();
2022 let mut es = EvalState::new(store, []).unwrap();
2023 let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
2024 let a = es.eval_from_string("true", "<test>").unwrap();
2025 let r = es.call(f, a);
2026 match r {
2027 Ok(_) => panic!("expected an error"),
2028 Err(e) => {
2029 if !e.to_string().contains("cannot coerce") {
2030 eprintln!("{}", e);
2031 panic!();
2032 }
2033 }
2034 }
2035 })
2036 .unwrap();
2037 }
2038
2039 #[test]
2040 fn eval_state_call_multi_fail_body() {
2041 gc_registering_current_thread(|| {
2042 let store = Store::open(None, HashMap::new()).unwrap();
2043 let mut es = EvalState::new(store, []).unwrap();
2044 let f = es.eval_from_string("x: y: x - y", "<test>").unwrap();
2046 let a = es.eval_from_string("2", "<test>").unwrap();
2047 let b = es.eval_from_string("true", "<test>").unwrap();
2048 let r = es.call_multi(&f, &[a, b]);
2049 match r {
2050 Ok(_) => panic!("expected an error"),
2051 Err(e) => {
2052 if !e.to_string().contains("expected an integer but found") {
2053 eprintln!("{}", e);
2054 panic!();
2055 }
2056 }
2057 }
2058 })
2059 .unwrap();
2060 }
2061
2062 #[test]
2063 fn eval_state_apply_fail_body() {
2064 gc_registering_current_thread(|| {
2065 let store = Store::open(None, HashMap::new()).unwrap();
2066 let mut es = EvalState::new(store, []).unwrap();
2067 let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
2068 let a = es.eval_from_string("true", "<test>").unwrap();
2069 let r = es.new_value_apply(&f, &a).unwrap();
2071 let res = es.force(&r);
2073 match res {
2074 Ok(_) => panic!("expected an error"),
2075 Err(e) => {
2076 if !e.to_string().contains("cannot coerce") {
2077 eprintln!("{}", e);
2078 panic!();
2079 }
2080 }
2081 }
2082 })
2083 .unwrap();
2084 }
2085
2086 #[test]
2088 fn eval_state_call_fail_args() {
2089 gc_registering_current_thread(|| {
2090 let store = Store::open(None, HashMap::new()).unwrap();
2091 let mut es = EvalState::new(store, []).unwrap();
2092 let f = es.eval_from_string("{x}: x + 1", "<test>").unwrap();
2093 let a = es.eval_from_string("{}", "<test>").unwrap();
2094 let r = es.call(f, a);
2095 match r {
2096 Ok(_) => panic!("expected an error"),
2097 Err(e) => {
2098 if !e.to_string().contains("called without required argument") {
2099 eprintln!("{}", e);
2100 panic!();
2101 }
2102 }
2103 }
2104 })
2105 .unwrap();
2106 }
2107
2108 #[test]
2109 fn eval_state_call_multi_fail_args() {
2110 gc_registering_current_thread(|| {
2111 let store = Store::open(None, HashMap::new()).unwrap();
2112 let mut es = EvalState::new(store, []).unwrap();
2113 let f = es.eval_from_string("{x}: {y}: x - y", "<test>").unwrap();
2115 let a = es.eval_from_string("{x = 2;}", "<test>").unwrap();
2116 let b = es.eval_from_string("{}", "<test>").unwrap();
2117 let r = es.call_multi(&f, &[a, b]);
2118 match r {
2119 Ok(_) => panic!("expected an error"),
2120 Err(e) => {
2121 if !e.to_string().contains("called without required argument") {
2122 eprintln!("{}", e);
2123 panic!();
2124 }
2125 }
2126 }
2127 })
2128 .unwrap();
2129 }
2130
2131 #[test]
2133 fn eval_state_apply_fail_args_lazy() {
2134 gc_registering_current_thread(|| {
2135 let store = Store::open(None, HashMap::new()).unwrap();
2136 let mut es = EvalState::new(store, []).unwrap();
2137 let f = es.eval_from_string("{x}: x + 1", "<test>").unwrap();
2138 let a = es.eval_from_string("{}", "<test>").unwrap();
2139 let r = es.new_value_apply(&f, &a).unwrap();
2141 let res = es.force(&r);
2143 match res {
2144 Ok(_) => panic!("expected an error"),
2145 Err(e) => {
2146 if !e.to_string().contains("called without required argument") {
2147 eprintln!("{}", e);
2148 panic!();
2149 }
2150 }
2151 }
2152 })
2153 .unwrap();
2154 }
2155
2156 #[test]
2157 fn store_open_params() {
2158 gc_registering_current_thread(|| {
2159 let store = tempfile::tempdir().unwrap();
2160 let store_path = store.path().to_str().unwrap();
2161 let state = tempfile::tempdir().unwrap();
2162 let state_path = state.path().to_str().unwrap();
2163 let log = tempfile::tempdir().unwrap();
2164 let log_path = log.path().to_str().unwrap();
2165
2166 let mut es = EvalState::new(
2167 Store::open(
2168 Some("local"),
2169 HashMap::from([
2170 ("store", store_path),
2171 ("state", state_path),
2172 ("log", log_path),
2173 ])
2174 .iter()
2175 .map(|(a, b)| (*a, *b)),
2176 )
2177 .unwrap(),
2178 [],
2179 )
2180 .unwrap();
2181
2182 let expr = r#"
2183 ''
2184 a derivation output: ${
2185 derivation { name = "letsbuild";
2186 system = builtins.currentSystem;
2187 builder = "/bin/sh";
2188 args = [ "-c" "echo foo > $out" ];
2189 }}
2190 a path: ${builtins.toFile "just-a-file" "ooh file good"}
2191 a derivation path by itself: ${
2192 builtins.unsafeDiscardOutputDependency
2193 (derivation {
2194 name = "not-actually-built-yet";
2195 system = builtins.currentSystem;
2196 builder = "/bin/sh";
2197 args = [ "-c" "echo foo > $out" ];
2198 }).drvPath}
2199 ''
2200 "#;
2201 let derivations: [&[u8]; 3] = [
2202 b"letsbuild.drv",
2203 b"just-a-file",
2204 b"not-actually-built-yet.drv",
2205 ];
2206 let _ = es.eval_from_string(expr, "<test>").unwrap();
2207
2208 let store_contents: Vec<_> = read_dir(store.path())
2210 .unwrap()
2211 .map(|dir_entry| dir_entry.unwrap().file_name())
2212 .collect();
2213 for derivation in derivations {
2214 assert!(store_contents
2215 .iter()
2216 .any(|f| f.as_encoded_bytes().ends_with(derivation)));
2217 }
2218 assert!(!empty(read_dir(state.path()).unwrap()));
2219
2220 store.close().unwrap();
2221 state.close().unwrap();
2222 log.close().unwrap();
2223 })
2224 .unwrap();
2225 }
2226
2227 fn empty(foldable: impl IntoIterator) -> bool {
2228 foldable.into_iter().all(|_| false)
2229 }
2230
2231 #[test]
2232 fn eval_state_primop_anon_call() {
2233 gc_registering_current_thread(|| {
2234 let store = Store::open(None, []).unwrap();
2235 let mut es = EvalState::new(store, []).unwrap();
2236 let bias: Arc<Mutex<Int>> = Arc::new(Mutex::new(0));
2237 let bias_control = bias.clone();
2238
2239 let primop = primop::PrimOp::new(
2240 &mut es,
2241 primop::PrimOpMeta {
2242 name: cstr!("testFunction"),
2243 args: [cstr!("a"), cstr!("b")],
2244 doc: cstr!("anonymous test function"),
2245 },
2246 Box::new(move |es, [a, b]| {
2247 let a = es.require_int(a)?;
2248 let b = es.require_int(b)?;
2249 let c = *bias.lock().unwrap();
2250 es.new_value_int(a + b + c)
2251 }),
2252 )
2253 .unwrap();
2254
2255 let f = es.new_value_primop(primop).unwrap();
2256
2257 {
2258 *bias_control.lock().unwrap() = 10;
2259 }
2260 let a = es.new_value_int(2).unwrap();
2261 let b = es.new_value_int(3).unwrap();
2262 let fa = es.call(f, a).unwrap();
2263 let v = es.call(fa, b).unwrap();
2264 es.force(&v).unwrap();
2265 let t = es.value_type(&v).unwrap();
2266 assert!(t == ValueType::Int);
2267 let i = es.require_int(&v).unwrap();
2268 assert!(i == 15);
2269 })
2270 .unwrap();
2271 }
2272
2273 #[test]
2274 fn eval_state_primop_anon_call_throw() {
2275 gc_registering_current_thread(|| {
2276 let store = Store::open(None, []).unwrap();
2277 let mut es = EvalState::new(store, []).unwrap();
2278 let f = {
2279 let es: &mut EvalState = &mut es;
2280 let prim = primop::PrimOp::new(
2281 es,
2282 primop::PrimOpMeta {
2283 name: cstr!("throwingTestFunction"),
2284 args: [cstr!("arg")],
2285 doc: cstr!("anonymous test function"),
2286 },
2287 Box::new(move |es, [a]| {
2288 let a = es.require_int(a)?;
2289 bail!("error with arg [{}]", a);
2290 }),
2291 )
2292 .unwrap();
2293
2294 es.new_value_primop(prim)
2295 }
2296 .unwrap();
2297 let a = es.new_value_int(2).unwrap();
2298 let r = es.call(f, a);
2299 match r {
2300 Ok(_) => panic!("expected an error"),
2301 Err(e) => {
2302 if !e.to_string().contains("error with arg [2]") {
2303 eprintln!("unexpected error message: {}", e);
2304 panic!();
2305 }
2306 }
2307 }
2308 })
2309 .unwrap();
2310 }
2311
2312 #[test]
2313 fn eval_state_primop_anon_call_no_args() {
2314 gc_registering_current_thread(|| {
2315 let store = Store::open(None, []).unwrap();
2316 let mut es = EvalState::new(store, []).unwrap();
2317 let v = es
2318 .new_value_thunk(
2319 "test_thunk",
2320 Box::new(move |es: &mut EvalState| es.new_value_int(42)),
2321 )
2322 .unwrap();
2323 es.force(&v).unwrap();
2324 let t = es.value_type(&v).unwrap();
2325 eprintln!("{:?}", t);
2326 assert!(t == ValueType::Int);
2327 let i = es.require_int(&v).unwrap();
2328 assert!(i == 42);
2329 })
2330 .unwrap();
2331 }
2332
2333 #[test]
2334 fn eval_state_primop_anon_call_no_args_lazy() {
2335 gc_registering_current_thread(|| {
2336 let store = Store::open(None, []).unwrap();
2337 let mut es = EvalState::new(store, []).unwrap();
2338 let v = es
2339 .new_value_thunk(
2340 "test_thunk",
2341 Box::new(move |_| {
2342 bail!("error message in test case eval_state_primop_anon_call_no_args_lazy")
2343 }),
2344 )
2345 .unwrap();
2346 let r = es.force(&v);
2347 match r {
2348 Ok(_) => panic!("expected an error"),
2349 Err(e) => {
2350 if !e.to_string().contains(
2351 "error message in test case eval_state_primop_anon_call_no_args_lazy",
2352 ) {
2353 eprintln!("unexpected error message: {}", e);
2354 panic!();
2355 }
2356 if !e.to_string().contains("test_thunk") {
2357 eprintln!("unexpected error message: {}", e);
2358 panic!();
2359 }
2360 }
2361 }
2362 })
2363 .unwrap();
2364 }
2365
2366 #[test]
2367 pub fn eval_state_primop_custom() {
2368 gc_registering_current_thread(|| {
2369 let store = Store::open(None, []).unwrap();
2370 let mut es = EvalState::new(store, []).unwrap();
2371 let primop = primop::PrimOp::new(
2372 &mut es,
2373 primop::PrimOpMeta {
2374 name: cstr!("frobnicate"),
2375 doc: cstr!("Frobnicates widgets"),
2376 args: [cstr!("x"), cstr!("y")],
2377 },
2378 Box::new(|es, args| {
2379 let a = es.require_int(&args[0])?;
2380 let b = es.require_int(&args[1])?;
2381 es.new_value_int(a + b)
2382 }),
2383 )
2384 .unwrap();
2385 let f = es.new_value_primop(primop).unwrap();
2386 let a = es.new_value_int(2).unwrap();
2387 let b = es.new_value_int(3).unwrap();
2388 let fa = es.call(f, a).unwrap();
2389 let fb = es.call(fa, b).unwrap();
2390 es.force(&fb).unwrap();
2391 let t = es.value_type(&fb).unwrap();
2392 assert!(t == ValueType::Int);
2393 let i = es.require_int(&fb).unwrap();
2394 assert!(i == 5);
2395 })
2396 .unwrap();
2397 }
2398
2399 #[test]
2400 pub fn eval_state_primop_custom_throw() {
2401 gc_registering_current_thread(|| {
2402 let store = Store::open(None, []).unwrap();
2403 let mut es = EvalState::new(store, []).unwrap();
2404 let primop = primop::PrimOp::new(
2405 &mut es,
2406 primop::PrimOpMeta {
2407 name: cstr!("frobnicate"),
2408 doc: cstr!("Frobnicates widgets"),
2409 args: [cstr!("x")],
2410 },
2411 Box::new(|_es, _args| bail!("The frob unexpectedly fizzled")),
2412 )
2413 .unwrap();
2414 let f = es.new_value_primop(primop).unwrap();
2415 let a = es.new_value_int(0).unwrap();
2416 match es.call(f, a) {
2417 Ok(_) => panic!("expected an error"),
2418 Err(e) => {
2419 if !e.to_string().contains("The frob unexpectedly fizzled") {
2420 eprintln!("unexpected error message: {}", e);
2421 panic!();
2422 }
2423 if !e.to_string().contains("frobnicate") {
2424 eprintln!("unexpected error message: {}", e);
2425 panic!();
2426 }
2427 }
2428 }
2429 })
2430 .unwrap();
2431 }
2432
2433 #[test]
2434 pub fn eval_state_new_value_attrs_from_slice_empty() {
2435 gc_registering_current_thread(|| {
2436 let store = Store::open(None, []).unwrap();
2437 let mut es = EvalState::new(store, []).unwrap();
2438 let attrs = es.new_value_attrs([]).unwrap();
2439 let t = es.value_type(&attrs).unwrap();
2440 assert!(t == ValueType::AttrSet);
2441 let names = es.require_attrs_names(&attrs).unwrap();
2442 assert!(names.is_empty());
2443 })
2444 .unwrap();
2445 }
2446
2447 #[test]
2448 pub fn eval_state_new_value_attrs_from_vec() {
2449 gc_registering_current_thread(|| {
2450 let store = Store::open(None, []).unwrap();
2451 let mut es = EvalState::new(store, []).unwrap();
2452 let attrs = {
2453 let a = es.new_value_int(1).unwrap();
2454 let b = es.new_value_int(2).unwrap();
2455 es.new_value_attrs(vec![("a".to_string(), a), ("b".to_string(), b)])
2456 .unwrap()
2457 };
2458 let t = es.value_type(&attrs).unwrap();
2459 assert!(t == ValueType::AttrSet);
2460 let names = es.require_attrs_names(&attrs).unwrap();
2461 assert_eq!(names.len(), 2);
2462 assert_eq!(names[0], "a");
2463 assert_eq!(names[1], "b");
2464 let a = es.require_attrs_select(&attrs, "a").unwrap();
2465 let b = es.require_attrs_select(&attrs, "b").unwrap();
2466 let i = es.require_int(&a).unwrap();
2467 assert_eq!(i, 1);
2468 let i = es.require_int(&b).unwrap();
2469 assert_eq!(i, 2);
2470 })
2471 .unwrap();
2472 }
2473
2474 #[test]
2475 pub fn eval_state_new_value_attrs_from_hashmap() {
2476 gc_registering_current_thread(|| {
2477 let store = Store::open(None, []).unwrap();
2478 let mut es = EvalState::new(store, []).unwrap();
2479 let attrs = {
2480 let a = es.new_value_int(1).unwrap();
2481 let b = es.new_value_int(2).unwrap();
2482 es.new_value_attrs(HashMap::from([("a".to_string(), a), ("b".to_string(), b)]))
2483 .unwrap()
2484 };
2485 let t = es.value_type(&attrs).unwrap();
2486 assert!(t == ValueType::AttrSet);
2487 let names = es.require_attrs_names(&attrs).unwrap();
2488 assert_eq!(names.len(), 2);
2489 assert_eq!(names[0], "a");
2490 assert_eq!(names[1], "b");
2491 let a = es.require_attrs_select(&attrs, "a").unwrap();
2492 let b = es.require_attrs_select(&attrs, "b").unwrap();
2493 let i = es.require_int(&a).unwrap();
2494 assert_eq!(i, 1);
2495 let i = es.require_int(&b).unwrap();
2496 assert_eq!(i, 2);
2497 })
2498 .unwrap();
2499 }
2500
2501 #[test]
2502 fn eval_state_require_list_select_idx_strict_basic() {
2503 gc_registering_current_thread(|| {
2504 let store = Store::open(None, HashMap::new()).unwrap();
2505 let mut es = EvalState::new(store, []).unwrap();
2506 let v = es.eval_from_string("[ 10 20 30 ]", "<test>").unwrap();
2507
2508 let elem0 = es.require_list_select_idx_strict(&v, 0).unwrap().unwrap();
2509 let elem1 = es.require_list_select_idx_strict(&v, 1).unwrap().unwrap();
2510 let elem2 = es.require_list_select_idx_strict(&v, 2).unwrap().unwrap();
2511
2512 assert_eq!(es.require_int(&elem0).unwrap(), 10);
2513 assert_eq!(es.require_int(&elem1).unwrap(), 20);
2514 assert_eq!(es.require_int(&elem2).unwrap(), 30);
2515 })
2516 .unwrap();
2517 }
2518
2519 #[test]
2520 fn eval_state_require_list_select_idx_strict_out_of_bounds() {
2521 gc_registering_current_thread(|| {
2522 let store = Store::open(None, HashMap::new()).unwrap();
2523 let mut es = EvalState::new(store, []).unwrap();
2524 let v = es.eval_from_string("[ 1 2 3 ]", "<test>").unwrap();
2525
2526 let out_of_bounds = es.require_list_select_idx_strict(&v, 3).unwrap();
2527 assert!(out_of_bounds.is_none());
2528
2529 let last_elem = es.require_list_select_idx_strict(&v, 2).unwrap().unwrap();
2531 assert_eq!(es.require_int(&last_elem).unwrap(), 3);
2532 })
2533 .unwrap();
2534 }
2535
2536 #[test]
2537 fn eval_state_require_list_select_idx_strict_empty_list() {
2538 gc_registering_current_thread(|| {
2539 let store = Store::open(None, HashMap::new()).unwrap();
2540 let mut es = EvalState::new(store, []).unwrap();
2541 let v = es.eval_from_string("[ ]", "<test>").unwrap();
2542
2543 let elem = es.require_list_select_idx_strict(&v, 0).unwrap();
2545 assert!(elem.is_none());
2546
2547 let size = es.require_list_size(&v).unwrap();
2549 assert_eq!(size, 0);
2550 })
2551 .unwrap();
2552 }
2553
2554 #[test]
2555 fn eval_state_require_list_select_idx_strict_forces_thunk() {
2556 gc_registering_current_thread(|| {
2557 let store = Store::open(None, HashMap::new()).unwrap();
2558 let mut es = EvalState::new(store, []).unwrap();
2559 let v = make_thunk(&mut es, "[ 42 ]");
2560 assert!(es.value_type_unforced(&v).is_none());
2561
2562 let elem = es.require_list_select_idx_strict(&v, 0).unwrap().unwrap();
2563 assert_eq!(es.require_int(&elem).unwrap(), 42);
2564 })
2565 .unwrap();
2566 }
2567
2568 #[test]
2569 fn eval_state_require_list_select_idx_strict_error_element() {
2570 gc_registering_current_thread(|| {
2571 let store = Store::open(None, HashMap::new()).unwrap();
2572 let mut es = EvalState::new(store, []).unwrap();
2573
2574 let v = es
2575 .eval_from_string("[ (1 + 1) (throw \"error\") (3 + 3) ]", "<test>")
2576 .unwrap();
2577
2578 let elem0 = es.require_list_select_idx_strict(&v, 0).unwrap().unwrap();
2579 assert_eq!(es.require_int(&elem0).unwrap(), 2);
2580
2581 let elem2 = es.require_list_select_idx_strict(&v, 2).unwrap().unwrap();
2582 assert_eq!(es.require_int(&elem2).unwrap(), 6);
2583
2584 let elem1_result = es.require_list_select_idx_strict(&v, 1);
2585 match elem1_result {
2586 Ok(_) => panic!("expected an error from throw during selection"),
2587 Err(e) => {
2588 assert!(e.to_string().contains("error"));
2589 }
2590 }
2591 })
2592 .unwrap();
2593 }
2594
2595 #[test]
2596 fn eval_state_require_list_select_idx_strict_wrong_type() {
2597 gc_registering_current_thread(|| {
2598 let store = Store::open(None, HashMap::new()).unwrap();
2599 let mut es = EvalState::new(store, []).unwrap();
2600 let v = es.eval_from_string("42", "<test>").unwrap();
2601
2602 let r = es.require_list_select_idx_strict(&v, 0);
2603 match r {
2604 Ok(_) => panic!("expected an error"),
2605 Err(e) => {
2606 let err_msg = e.to_string();
2607 assert!(err_msg.contains("expected a list, but got a"));
2608 }
2609 }
2610 })
2611 .unwrap();
2612 }
2613
2614 #[test]
2615 fn eval_state_require_list_size_basic() {
2616 gc_registering_current_thread(|| {
2617 let store = Store::open(None, HashMap::new()).unwrap();
2618 let mut es = EvalState::new(store, []).unwrap();
2619
2620 let empty = es.eval_from_string("[ ]", "<test>").unwrap();
2621 assert_eq!(es.require_list_size(&empty).unwrap(), 0);
2622
2623 let three_elem = es.eval_from_string("[ 1 2 3 ]", "<test>").unwrap();
2624 assert_eq!(es.require_list_size(&three_elem).unwrap(), 3);
2625 })
2626 .unwrap();
2627 }
2628
2629 #[test]
2630 fn eval_state_require_list_size_forces_thunk() {
2631 gc_registering_current_thread(|| {
2632 let store = Store::open(None, HashMap::new()).unwrap();
2633 let mut es = EvalState::new(store, []).unwrap();
2634 let v = make_thunk(&mut es, "[ 1 2 3 4 5 ]");
2635 assert!(es.value_type_unforced(&v).is_none());
2636
2637 let size = es.require_list_size(&v).unwrap();
2638 assert_eq!(size, 5);
2639 })
2640 .unwrap();
2641 }
2642
2643 #[test]
2644 fn eval_state_require_list_size_lazy_elements() {
2645 gc_registering_current_thread(|| {
2646 let store = Store::open(None, HashMap::new()).unwrap();
2647 let mut es = EvalState::new(store, []).unwrap();
2648
2649 let v = es
2650 .eval_from_string(
2651 "[ (throw \"error1\") (throw \"error2\") (throw \"error3\") ]",
2652 "<test>",
2653 )
2654 .unwrap();
2655
2656 let size = es.require_list_size(&v).unwrap();
2657 assert_eq!(size, 3);
2658 })
2659 .unwrap();
2660 }
2661
2662 #[test]
2663 fn eval_state_require_list_size_wrong_type() {
2664 gc_registering_current_thread(|| {
2665 let store = Store::open(None, HashMap::new()).unwrap();
2666 let mut es = EvalState::new(store, []).unwrap();
2667 let v = es.eval_from_string("\"not a list\"", "<test>").unwrap();
2668
2669 let r = es.require_list_size(&v);
2670 match r {
2671 Ok(_) => panic!("expected an error"),
2672 Err(e) => {
2673 let err_msg = e.to_string();
2674 assert!(err_msg.contains("expected a list, but got a"));
2675 }
2676 }
2677 })
2678 .unwrap();
2679 }
2680
2681 #[test]
2704 #[cfg(nix_at_least = "2.26" )]
2705 fn eval_state_builder_path_coercion() {
2706 gc_registering_current_thread(|| {
2707 let mut store = Store::open(None, HashMap::new()).unwrap();
2708 let mut es = EvalStateBuilder::new(store.clone())
2709 .unwrap()
2710 .build()
2711 .unwrap();
2712
2713 let expr = r#"builtins.toFile "test-file.txt" "test content""#;
2716
2717 let value = es.eval_from_string(expr, "<test>").unwrap();
2719
2720 let realised = es.realise_string(&value, false).unwrap();
2722
2723 assert_eq!(
2725 realised.paths.len(),
2726 1,
2727 "Expected 1 store path, got {}",
2728 realised.paths.len()
2729 );
2730
2731 let physical_path = store.real_path(&realised.paths[0]).unwrap();
2734
2735 assert!(
2737 std::path::Path::new(&physical_path).exists(),
2738 "Store path should exist: {}",
2739 physical_path
2740 );
2741
2742 let store_content = std::fs::read_to_string(&physical_path).unwrap();
2744 assert_eq!(store_content, "test content");
2745 })
2746 .unwrap();
2747 }
2748
2749 #[test]
2763 #[cfg(nix_at_least = "2.26")]
2764 fn eval_state_builder_loads_max_call_depth() {
2765 gc_registering_current_thread(|| {
2766 let store = Store::open(None, HashMap::new()).unwrap();
2767 let mut es = EvalStateBuilder::new(store).unwrap().build().unwrap();
2768
2769 let expr = r#"
2772 let
2773 recurse = n: if n == 0 then "done" else recurse (n - 1);
2774 in
2775 recurse 1100
2776 "#;
2777
2778 let result = es.eval_from_string(expr, "<test>");
2779
2780 match result {
2781 Err(e) => {
2782 let err_str = e.to_string();
2783 assert!(
2784 err_str.contains("max-call-depth"),
2785 "Expected max-call-depth error, got: {}",
2786 err_str
2787 );
2788 }
2789 Ok(_) => {
2790 panic!(
2791 "Expected recursion to fail with max-call-depth=1000, but it succeeded. \
2792 This indicates eval_state_builder_load() was not called."
2793 );
2794 }
2795 }
2796 })
2797 .unwrap();
2798 }
2799
2800 #[test]
2809 #[cfg(nix_at_least = "2.26")]
2810 fn eval_state_builder_ignores_ambient_when_disabled() {
2811 gc_registering_current_thread(|| {
2812 let store = Store::open(None, HashMap::new()).unwrap();
2813 let mut es = EvalStateBuilder::new(store)
2814 .unwrap()
2815 .load_ambient_settings(false)
2816 .build()
2817 .unwrap();
2818
2819 let expr = r#"
2823 let
2824 recurse = n: if n == 0 then "done" else recurse (n - 1);
2825 in
2826 recurse 1100
2827 "#;
2828
2829 let result = es.eval_from_string(expr, "<test>");
2830
2831 match result {
2832 Ok(value) => {
2833 let result_str = es.require_string(&value).unwrap();
2835 assert_eq!(result_str, "done");
2836 }
2837 Err(e) => {
2838 panic!(
2839 "Expected recursion to succeed with default max-call-depth=10000, \
2840 but it failed: {}. This indicates ambient settings were not ignored.",
2841 e
2842 );
2843 }
2844 }
2845 })
2846 .unwrap();
2847 }
2848}