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;
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::LazyLock;
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(crate) struct EvalStateRef {
180 eval_state: NonNull<raw::EvalState>,
181 owned: bool,
183}
184impl EvalStateRef {
185 unsafe fn as_ptr(&self) -> *mut raw::EvalState {
191 self.eval_state.as_ptr()
192 }
193}
194impl Drop for EvalStateRef {
195 fn drop(&mut self) {
196 if self.owned {
197 unsafe {
198 raw::state_free(self.eval_state.as_ptr());
199 }
200 }
201 }
202}
203#[cfg(nix_at_least = "2.26")]
231pub struct EvalStateBuilder {
232 eval_state_builder: *mut raw::eval_state_builder,
233 lookup_path: Vec<CString>,
234 load_ambient_settings: bool,
235 store: Store,
236}
237#[cfg(nix_at_least = "2.26")]
238impl Drop for EvalStateBuilder {
239 fn drop(&mut self) {
240 unsafe {
241 raw::eval_state_builder_free(self.eval_state_builder);
242 }
243 }
244}
245#[cfg(nix_at_least = "2.26")]
246impl EvalStateBuilder {
247 pub fn new(store: Store) -> Result<EvalStateBuilder> {
249 let mut context = Context::new();
250 let eval_state_builder =
251 unsafe { check_call!(raw::eval_state_builder_new(&mut context, store.raw_ptr())) }?;
252 Ok(EvalStateBuilder {
253 store,
254 eval_state_builder,
255 lookup_path: Vec::new(),
256 load_ambient_settings: true,
257 })
258 }
259 pub fn lookup_path<'a>(mut self, path: impl IntoIterator<Item = &'a str>) -> Result<Self> {
261 let lookup_path: Vec<CString> = path
262 .into_iter()
263 .map(|path| {
264 CString::new(path).with_context(|| {
265 format!("EvalStateBuilder::lookup_path: path `{path}` contains null byte")
266 })
267 })
268 .collect::<Result<_>>()?;
269 self.lookup_path = lookup_path;
270 Ok(self)
271 }
272 pub fn load_ambient_settings(mut self, load: bool) -> Self {
278 self.load_ambient_settings = load;
279 self
280 }
281 pub fn build(&self) -> Result<EvalState> {
283 init()?;
285
286 let mut context = Context::new();
287
288 if self.load_ambient_settings {
291 unsafe {
292 check_call!(raw::eval_state_builder_load(
293 &mut context,
294 self.eval_state_builder
295 ))?;
296 }
297 }
298
299 let mut lookup_path: Vec<*const c_char> = self
301 .lookup_path
302 .iter()
303 .map(|s| s.as_ptr())
304 .chain(std::iter::once(null())) .collect();
306
307 unsafe {
308 check_call!(raw::eval_state_builder_set_lookup_path(
309 &mut context,
310 self.eval_state_builder,
311 lookup_path.as_mut_ptr()
312 ))?;
313 }
314
315 let eval_state =
316 unsafe { check_call!(raw::eval_state_build(&mut context, self.eval_state_builder)) }?;
317 Ok(EvalState {
318 eval_state: EvalStateRef {
319 eval_state: NonNull::new(eval_state).unwrap_or_else(|| {
320 panic!("nix_state_create returned a null pointer without an error")
321 }),
322 owned: true,
323 },
324 context,
325 })
326 }
327 pub fn raw_ptr(&self) -> *mut raw::eval_state_builder {
334 self.eval_state_builder
335 }
336}
337
338pub struct EvalState {
339 pub(crate) eval_state: EvalStateRef,
340 pub(crate) context: Context,
341}
342impl EvalState {
343 pub fn new<'a>(store: Store, lookup_path: impl IntoIterator<Item = &'a str>) -> Result<Self> {
347 EvalStateBuilder::new(store)?
348 .lookup_path(lookup_path)?
349 .build()
350 }
351
352 pub unsafe fn raw_ptr(&self) -> *mut raw::EvalState {
358 self.eval_state.as_ptr()
359 }
360
361 pub(crate) unsafe fn from_raw_borrowed(ptr: NonNull<raw::EvalState>) -> Self {
367 EvalState {
368 eval_state: EvalStateRef {
369 eval_state: ptr,
370 owned: false,
371 },
372 context: Context::new(),
373 }
374 }
375
376 #[doc(alias = "nix_expr_eval_from_string")]
399 #[doc(alias = "parse")]
400 #[doc(alias = "eval")]
401 #[doc(alias = "evaluate")]
402 pub fn eval_from_string(&mut self, expr: &str, path: &str) -> Result<Value> {
403 let expr_ptr =
404 CString::new(expr).with_context(|| "eval_from_string: expr contains null byte")?;
405 let path_ptr =
406 CString::new(path).with_context(|| "eval_from_string: path contains null byte")?;
407 unsafe {
408 let value = self.new_value_uninitialized()?;
409 check_call!(raw::expr_eval_from_string(
410 &mut self.context,
411 self.eval_state.as_ptr(),
412 expr_ptr.as_ptr(),
413 path_ptr.as_ptr(),
414 value.raw_ptr()
415 ))?;
416 Ok(value)
417 }
418 }
419
420 #[doc(alias = "evaluate")]
428 #[doc(alias = "strict")]
429 pub fn force(&mut self, v: &Value) -> Result<()> {
430 unsafe {
431 check_call!(raw::value_force(
432 &mut self.context,
433 self.eval_state.as_ptr(),
434 v.raw_ptr()
435 ))
436 }?;
437 Ok(())
438 }
439
440 #[doc(alias = "type_of")]
448 #[doc(alias = "value_type_lazy")]
449 #[doc(alias = "nix_get_type")]
450 #[doc(alias = "get_type")]
451 #[doc(alias = "nix_value_type")]
452 pub fn value_type_unforced(&mut self, value: &Value) -> Option<ValueType> {
453 let r = unsafe { check_call!(raw::get_type(&mut self.context, value.raw_ptr())) };
454 ValueType::from_raw(r.unwrap())
456 }
457 #[doc(alias = "type_of")]
465 #[doc(alias = "value_type_strict")]
466 #[doc(alias = "nix_get_type")]
467 #[doc(alias = "get_type")]
468 #[doc(alias = "nix_value_type_strict")]
469 pub fn value_type(&mut self, value: &Value) -> Result<ValueType> {
470 match self.value_type_unforced(value) {
471 Some(a) => Ok(a),
472 None => {
473 self.force(value)?;
474 match self.value_type_unforced(value) {
475 Some(a) => Ok(a),
476 None => {
477 panic!("Nix value must not be thunk after being forced.")
478 }
479 }
480 }
481 }
482 }
483 #[doc(alias = "integer")]
509 #[doc(alias = "number")]
510 #[doc(alias = "nix_get_int")]
511 #[doc(alias = "get_int")]
512 pub fn require_int(&mut self, v: &Value) -> Result<Int> {
513 let t = self.value_type(v)?;
514 if t != ValueType::Int {
515 bail!("expected an int, but got a {:?}", t);
516 }
517 unsafe { check_call!(raw::get_int(&mut self.context, v.raw_ptr())) }
518 }
519
520 #[doc(alias = "boolean")]
526 #[doc(alias = "nix_get_bool")]
527 #[doc(alias = "get_bool")]
528 pub fn require_bool(&mut self, v: &Value) -> Result<bool> {
529 let t = self.value_type(v)?;
530 if t != ValueType::Bool {
531 bail!("expected a bool, but got a {:?}", t);
532 }
533 unsafe { check_call!(raw::get_bool(&mut self.context, v.raw_ptr())) }
534 }
535
536 #[doc(alias = "collect")]
557 #[doc(alias = "to_vec")]
558 #[doc(alias = "all")]
559 #[doc(alias = "nix_get_list_size")]
560 #[doc(alias = "nix_get_list_byidx")]
561 pub fn require_list_strict<C>(&mut self, value: &Value) -> Result<C>
562 where
563 C: FromIterator<Value>,
564 {
565 let t = self.value_type(value)?;
566 if t != ValueType::List {
567 bail!("expected a list, but got a {:?}", t);
568 }
569 let size = unsafe { check_call!(raw::get_list_size(&mut self.context, value.raw_ptr())) }?;
570
571 (0..size)
572 .map(|i| {
573 let element_ptr = unsafe {
574 check_call!(raw::get_list_byidx(
575 &mut self.context,
576 value.raw_ptr(),
577 self.eval_state.as_ptr(),
578 i
579 ))
580 }?;
581 Ok(unsafe { Value::new(element_ptr) })
582 })
583 .collect()
584 }
585
586 #[doc(alias = "keys")]
592 #[doc(alias = "attributes")]
593 #[doc(alias = "fields")]
594 pub fn require_attrs_names(&mut self, v: &Value) -> Result<Vec<String>> {
595 self.require_attrs_names_unsorted(v).map(|mut v| {
596 v.sort();
597 v
598 })
599 }
600
601 #[doc(alias = "keys_unsorted")]
605 #[doc(alias = "attributes_unsorted")]
606 pub fn require_attrs_names_unsorted(&mut self, v: &Value) -> Result<Vec<String>> {
607 let t = self.value_type(v)?;
608 if t != ValueType::AttrSet {
609 bail!("expected an attrset, but got a {:?}", t);
610 }
611 let n = unsafe { check_call!(raw::get_attrs_size(&mut self.context, v.raw_ptr())) }?;
612 let mut attrs = Vec::with_capacity(n as usize);
613 for i in 0..n {
614 let cstr_ptr: *const c_char = unsafe {
615 check_call!(raw::get_attr_name_byidx(
616 &mut self.context,
617 v.raw_ptr(),
618 self.eval_state.as_ptr(),
619 i as c_uint
620 ))
621 }?;
622 let cstr = unsafe { std::ffi::CStr::from_ptr(cstr_ptr) };
623 let s = cstr
624 .to_str()
625 .map_err(|e| anyhow::format_err!("Nix attrset key is not valid UTF-8: {}", e))?;
626 attrs.insert(i as usize, s.to_owned());
627 }
628 Ok(attrs)
629 }
630
631 #[doc(alias = "get_attr")]
637 #[doc(alias = "attribute")]
638 #[doc(alias = "field")]
639 pub fn require_attrs_select(&mut self, v: &Value, attr_name: &str) -> Result<Value> {
640 let t = self.value_type(v)?;
641 if t != ValueType::AttrSet {
642 bail!("expected an attrset, but got a {:?}", t);
643 }
644 let attr_name = CString::new(attr_name)
645 .with_context(|| "require_attrs_select: attrName contains null byte")?;
646 unsafe {
647 let v2 = check_call!(raw::get_attr_byname(
648 &mut self.context,
649 v.raw_ptr(),
650 self.eval_state.as_ptr(),
651 attr_name.as_ptr()
652 ));
653 match v2 {
654 Ok(v2) => Ok(Value::new(v2)),
655 Err(e) => {
656 if e.to_string() == "missing attribute" {
661 bail!("attribute `{}` not found", attr_name.to_string_lossy());
662 } else {
663 Err(e)
664 }
665 }
666 }
667 }
668 }
669
670 #[doc(alias = "nix_get_attr_byname")]
680 #[doc(alias = "get_attr_byname")]
681 #[doc(alias = "get_attr_opt")]
682 #[doc(alias = "try_get")]
683 #[doc(alias = "maybe_get")]
684 pub fn require_attrs_select_opt(
685 &mut self,
686 v: &Value,
687 attr_name: &str,
688 ) -> Result<Option<Value>> {
689 let t = self.value_type(v)?;
690 if t != ValueType::AttrSet {
691 bail!("expected an attrset, but got a {:?}", t);
692 }
693 let attr_name = CString::new(attr_name)
694 .with_context(|| "require_attrs_select_opt: attrName contains null byte")?;
695 let v2 = unsafe {
696 check_call_opt_key!(raw::get_attr_byname(
697 &mut self.context,
698 v.raw_ptr(),
699 self.eval_state.as_ptr(),
700 attr_name.as_ptr()
701 ))
702 }?;
703 Ok(v2.map(|x| unsafe { Value::new(x) }))
704 }
705
706 #[doc(alias = "length")]
712 #[doc(alias = "count")]
713 #[doc(alias = "len")]
714 #[doc(alias = "nix_get_list_size")]
715 #[doc(alias = "get_list_size")]
716 pub fn require_list_size(&mut self, v: &Value) -> Result<u32> {
717 let t = self.value_type(v)?;
718 if t != ValueType::List {
719 bail!("expected a list, but got a {:?}", t);
720 }
721 let ret = unsafe { check_call!(raw::get_list_size(&mut self.context, v.raw_ptr())) }?;
722 Ok(ret)
723 }
724
725 #[doc(alias = "get")]
736 #[doc(alias = "index")]
737 #[doc(alias = "at")]
738 #[doc(alias = "nix_get_list_byidx")]
739 #[doc(alias = "get_list_byidx")]
740 pub fn require_list_select_idx_strict(&mut self, v: &Value, idx: u32) -> Result<Option<Value>> {
741 let t = self.value_type(v)?;
742 if t != ValueType::List {
743 bail!("expected a list, but got a {:?}", t);
744 }
745
746 let size = unsafe { check_call!(raw::get_list_size(&mut self.context, v.raw_ptr())) }?;
750
751 if idx >= size {
752 return Ok(None);
753 }
754
755 let v2 = unsafe {
756 check_call_opt_key!(raw::get_list_byidx(
757 &mut self.context,
758 v.raw_ptr(),
759 self.eval_state.as_ptr(),
760 idx
761 ))
762 }?;
763 Ok(v2.map(|x| unsafe { Value::new(x) }))
764 }
765
766 #[doc(alias = "make_string")]
770 #[doc(alias = "create_string")]
771 #[doc(alias = "string_value")]
772 pub fn new_value_str(&mut self, s: &str) -> Result<Value> {
773 let s = CString::new(s).with_context(|| "new_value_str: contains null byte")?;
774 let v = unsafe {
775 let value = self.new_value_uninitialized()?;
776 check_call!(raw::init_string(
777 &mut self.context,
778 value.raw_ptr(),
779 s.as_ptr()
780 ))?;
781 value
782 };
783 Ok(v)
784 }
785
786 #[doc(alias = "make_int")]
788 #[doc(alias = "create_int")]
789 #[doc(alias = "int_value")]
790 #[doc(alias = "integer_value")]
791 pub fn new_value_int(&mut self, i: Int) -> Result<Value> {
792 let v = unsafe {
793 let value = self.new_value_uninitialized()?;
794 check_call!(raw::init_int(&mut self.context, value.raw_ptr(), i))?;
795 value
796 };
797 Ok(v)
798 }
799
800 #[doc(alias = "make_thunk")]
807 #[doc(alias = "create_thunk")]
808 #[doc(alias = "lazy_value")]
809 pub fn new_value_thunk(
810 &mut self,
811 name: &str,
812 f: Box<dyn Fn(&mut EvalState) -> Result<Value>>,
813 ) -> Result<Value> {
814 let name = CString::new(name).with_context(|| "new_thunk: name contains null byte")?;
817 let primop = primop::PrimOp::new(
818 primop::PrimOpMeta {
819 name: name.as_c_str(),
821 doc: cstr!("Performs an on demand computation, implemented outside the Nix language in native code."),
823 args: [CString::new("internal_unused").unwrap().as_c_str()],
825 },
826 Box::new(move |eval_state, _dummy: &[Value; 1]| f(eval_state)),
827 )?;
828
829 let p = primop.into_value(self)?;
830 self.new_value_apply(&p, &p)
831 }
832
833 fn get_string(&mut self, value: &Value) -> Result<String> {
835 let mut r = result_string_init!();
836 unsafe {
837 check_call!(raw::get_string(
838 &mut self.context,
839 value.raw_ptr(),
840 Some(callback_get_result_string),
841 callback_get_result_string_data(&mut r)
842 ))?;
843 };
844 r
845 }
846 #[doc(alias = "str")]
853 #[doc(alias = "text")]
854 #[doc(alias = "nix_get_string")]
855 #[doc(alias = "get_string")]
856 pub fn require_string(&mut self, value: &Value) -> Result<String> {
857 let t = self.value_type(value)?;
858 if t != ValueType::String {
859 bail!("expected a string, but got a {:?}", t);
860 }
861 self.get_string(value)
862 }
863 #[doc(alias = "realize_string")]
868 #[doc(alias = "string_with_context")]
869 #[doc(alias = "build_string")]
870 pub fn realise_string(
871 &mut self,
872 value: &Value,
873 is_import_from_derivation: bool,
874 ) -> Result<RealisedString> {
875 let t = self.value_type(value)?;
876 if t != ValueType::String {
877 bail!("expected a string, but got a {:?}", t);
878 }
879
880 let rs = unsafe {
881 check_call!(raw::string_realise(
882 &mut self.context,
883 self.eval_state.as_ptr(),
884 value.raw_ptr(),
885 is_import_from_derivation
886 ))
887 }?;
888
889 let s = unsafe {
890 let start = raw::realised_string_get_buffer_start(rs) as *const std::os::raw::c_char;
891 let size = raw::realised_string_get_buffer_size(rs);
892 let slice = std::slice::from_raw_parts(start.cast::<u8>(), size);
893 String::from_utf8(slice.to_vec())
894 .map_err(|e| anyhow::format_err!("Nix string is not valid UTF-8: {}", e))?
895 };
896
897 let paths = unsafe {
898 let n = raw::realised_string_get_store_path_count(rs);
899 let mut paths = Vec::with_capacity(n as usize);
900 for i in 0..n {
901 let path = raw::realised_string_get_store_path(rs, i);
902 let path = NonNull::new(path as *mut raw_store::StorePath).ok_or_else(|| {
903 anyhow::format_err!(
904 "nix_realised_string_get_store_path returned a null pointer"
905 )
906 })?;
907 paths.push(StorePath::new_raw_clone(path));
908 }
909 paths
910 };
911
912 unsafe {
914 raw::realised_string_free(rs);
915 }
916
917 Ok(RealisedString { s, paths })
918 }
919
920 #[doc(alias = "nix_value_call")]
925 #[doc(alias = "value_call")]
926 #[doc(alias = "apply")]
927 #[doc(alias = "invoke")]
928 #[doc(alias = "execute")]
929 pub fn call(&mut self, f: Value, a: Value) -> Result<Value> {
930 let value = self.new_value_uninitialized()?;
931 unsafe {
932 check_call!(raw::value_call(
933 &mut self.context,
934 self.eval_state.as_ptr(),
935 f.raw_ptr(),
936 a.raw_ptr(),
937 value.raw_ptr()
938 ))
939 }?;
940 Ok(value)
941 }
942
943 #[doc(alias = "nix_value_call_multi")]
982 #[doc(alias = "value_call_multi")]
983 #[doc(alias = "apply_multi")]
984 #[doc(alias = "curry")]
985 #[doc(alias = "call_with_args")]
986 pub fn call_multi(&mut self, f: &Value, args: &[Value]) -> Result<Value> {
987 let value = self.new_value_uninitialized()?;
988 unsafe {
989 let mut args_ptrs = args.iter().map(|a| a.raw_ptr()).collect::<Vec<_>>();
990 check_call!(raw::value_call_multi(
991 &mut self.context,
992 self.eval_state.as_ptr(),
993 f.raw_ptr(),
994 args_ptrs.len(),
995 args_ptrs.as_mut_ptr(),
996 value.raw_ptr()
997 ))
998 }?;
999 Ok(value)
1000 }
1001
1002 #[doc(alias = "lazy_apply")]
1007 #[doc(alias = "thunk_apply")]
1008 #[doc(alias = "defer_call")]
1009 pub fn new_value_apply(&mut self, f: &Value, a: &Value) -> Result<Value> {
1010 let value = self.new_value_uninitialized()?;
1011 unsafe {
1012 check_call!(raw::init_apply(
1013 &mut self.context,
1014 value.raw_ptr(),
1015 f.raw_ptr(),
1016 a.raw_ptr()
1017 ))
1018 }?;
1019 Ok(value)
1020 }
1021
1022 pub(crate) fn new_value_uninitialized(&mut self) -> Result<Value> {
1023 unsafe {
1024 let value = check_call!(raw::alloc_value(
1025 &mut self.context,
1026 self.eval_state.as_ptr()
1027 ))?;
1028 Ok(Value::new(value))
1029 }
1030 }
1031
1032 #[deprecated(since = "0.3.0", note = "use PrimOp::into_value")]
1033 pub fn new_value_primop(&mut self, primop: primop::PrimOp) -> Result<Value> {
1034 primop.into_value(self)
1035 }
1036
1037 #[doc(alias = "make_attrs")]
1074 #[doc(alias = "create_attrset")]
1075 #[doc(alias = "object")]
1076 #[doc(alias = "record")]
1077 pub fn new_value_attrs<I>(&mut self, attrs: I) -> Result<Value>
1078 where
1079 I: IntoIterator<Item = (String, Value)>,
1080 I::IntoIter: ExactSizeIterator,
1081 {
1082 let iter = attrs.into_iter();
1083 let size = iter.len();
1084 let bindings_builder = BindingsBuilder::new(self, size)?;
1085 for (name, value) in iter {
1086 let name =
1087 CString::new(name).with_context(|| "new_value_attrs: name contains null byte")?;
1088 unsafe {
1089 check_call!(raw::bindings_builder_insert(
1090 &mut self.context,
1091 bindings_builder.ptr,
1092 name.as_ptr(),
1093 value.raw_ptr()
1094 ))?;
1095 }
1096 }
1097 let value = self.new_value_uninitialized()?;
1098 unsafe {
1099 check_call!(raw::make_attrs(
1100 &mut self.context,
1101 value.raw_ptr(),
1102 bindings_builder.ptr
1103 ))?;
1104 }
1105 Ok(value)
1106 }
1107}
1108
1109struct BindingsBuilder {
1111 ptr: *mut raw::BindingsBuilder,
1112}
1113impl Drop for BindingsBuilder {
1114 fn drop(&mut self) {
1115 unsafe {
1116 raw::bindings_builder_free(self.ptr);
1117 }
1118 }
1119}
1120impl BindingsBuilder {
1121 fn new(eval_state: &mut EvalState, capacity: usize) -> Result<Self> {
1122 let ptr = unsafe {
1123 check_call!(raw::make_bindings_builder(
1124 &mut eval_state.context,
1125 eval_state.eval_state.as_ptr(),
1126 capacity
1127 ))
1128 }?;
1129 Ok(BindingsBuilder { ptr })
1130 }
1131}
1132
1133#[doc(alias = "garbage_collect")]
1135#[doc(alias = "collect")]
1136#[doc(alias = "gc")]
1137pub fn gc_now() {
1138 unsafe {
1139 raw::gc_now();
1140 }
1141}
1142
1143pub struct ThreadRegistrationGuard {
1147 must_unregister: bool,
1148}
1149impl Drop for ThreadRegistrationGuard {
1150 fn drop(&mut self) {
1151 if self.must_unregister {
1152 unsafe {
1153 gc::GC_unregister_my_thread();
1154 }
1155 }
1156 }
1157}
1158
1159fn gc_register_my_thread_do_it() -> Result<()> {
1160 unsafe {
1161 let mut sb: gc::GC_stack_base = gc::GC_stack_base {
1162 mem_base: null_mut(),
1163 };
1164 let r = gc::GC_get_stack_base(&mut sb);
1165 if r as u32 != gc::GC_SUCCESS {
1166 Err(anyhow::format_err!("GC_get_stack_base failed: {}", r))?;
1167 }
1168 gc::GC_register_my_thread(&sb);
1169 Ok(())
1170 }
1171}
1172
1173#[doc(alias = "register_thread")]
1174#[doc(alias = "thread_setup")]
1175#[doc(alias = "gc_register")]
1176pub fn gc_register_my_thread() -> Result<ThreadRegistrationGuard> {
1177 init()?;
1178 unsafe {
1179 let already_done = gc::GC_thread_is_registered();
1180 if already_done != 0 {
1181 return Ok(ThreadRegistrationGuard {
1182 must_unregister: false,
1183 });
1184 }
1185 gc_register_my_thread_do_it()?;
1186 Ok(ThreadRegistrationGuard {
1187 must_unregister: true,
1188 })
1189 }
1190}
1191
1192#[doc(alias = "test_initialize")]
1195#[doc(alias = "test_setup")]
1196pub fn test_init() {
1197 init().unwrap();
1198
1199 nix_bindings_util::settings::set("build-hook", "").unwrap();
1204
1205 if cfg!(target_os = "linux") {
1209 nix_bindings_util::settings::set("sandbox-build-dir", "/custom-build-dir-for-test")
1210 .unwrap();
1211 }
1212 std::env::set_var("_NIX_TEST_NO_SANDBOX", "1");
1213
1214 nix_bindings_util::settings::set("substituters", "").unwrap();
1216}
1217
1218#[cfg(test)]
1219mod tests {
1220 use super::*;
1221 use cstr::cstr;
1222 use ctor::ctor;
1223 use std::collections::HashMap;
1224 use std::fs::read_dir;
1225 use std::io::Write as _;
1226 use std::sync::{Arc, Mutex};
1227
1228 #[ctor]
1229 fn setup() {
1230 test_init();
1231
1232 std::env::set_var("NIX_CONFIG", "max-call-depth = 1000");
1237 }
1238
1239 pub fn gc_registering_current_thread<F, R>(f: F) -> Result<R>
1241 where
1242 F: FnOnce() -> R,
1243 {
1244 let guard = gc_register_my_thread()?;
1245 let r = f();
1246 drop(guard);
1247 Ok(r)
1248 }
1249
1250 #[test]
1251 fn eval_state_new_and_drop() {
1252 gc_registering_current_thread(|| {
1253 let store = Store::open(None, HashMap::new()).unwrap();
1255 let _e = EvalState::new(store, []).unwrap();
1256 })
1257 .unwrap();
1258 }
1259
1260 static_assertions::assert_not_impl_any!(EvalState: Send, Sync);
1265
1266 #[test]
1267 fn eval_state_lookup_path() {
1268 let import_expression = "import <test_file0> + import <test_file1>";
1269 let integer0 = 83;
1270 let integer1 = 103;
1271 let mut test_file0 = tempfile::NamedTempFile::new().unwrap();
1272 let mut test_file1 = tempfile::NamedTempFile::new().unwrap();
1273 writeln!(test_file0, "{integer0}").unwrap();
1274 writeln!(test_file1, "{integer1}").unwrap();
1275 gc_registering_current_thread(|| {
1276 let mut es = EvalState::new(Store::open(None, HashMap::new()).unwrap(), []).unwrap();
1277 assert!(es.eval_from_string(import_expression, "<test>").is_err());
1278
1279 let mut es = EvalState::new(
1280 Store::open(None, HashMap::new()).unwrap(),
1281 [
1282 format!("test_file0={}", test_file0.path().to_str().unwrap()).as_str(),
1283 format!("test_file1={}", test_file1.path().to_str().unwrap()).as_str(),
1284 ],
1285 )
1286 .unwrap();
1287 let ie = &es.eval_from_string(import_expression, "<test>").unwrap();
1288 let v = es.require_int(ie).unwrap();
1289 assert_eq!(v, integer0 + integer1);
1290 })
1291 .unwrap();
1292 test_file0.close().unwrap();
1293 test_file1.close().unwrap();
1294 }
1295
1296 #[test]
1297 fn eval_state_eval_from_string() {
1298 gc_registering_current_thread(|| {
1299 let store = Store::open(None, HashMap::new()).unwrap();
1300 let mut es = EvalState::new(store, []).unwrap();
1301 let v = es.eval_from_string("1", "<test>").unwrap();
1302 let v2 = v.clone();
1303 es.force(&v).unwrap();
1304 let t = es.value_type_unforced(&v);
1305 assert!(t == Some(ValueType::Int));
1306 let t2 = es.value_type_unforced(&v2);
1307 assert!(t2 == Some(ValueType::Int));
1308 gc_now();
1309 })
1310 .unwrap();
1311 }
1312
1313 #[test]
1314 fn eval_state_value_bool() {
1315 gc_registering_current_thread(|| {
1316 let store = Store::open(None, HashMap::new()).unwrap();
1317 let mut es = EvalState::new(store, []).unwrap();
1318 let v = es.eval_from_string("true", "<test>").unwrap();
1319 es.force(&v).unwrap();
1320 let t = es.value_type_unforced(&v);
1321 assert!(t == Some(ValueType::Bool));
1322 let b = es.require_bool(&v).unwrap();
1323 assert!(b);
1324
1325 let v = es.eval_from_string("false", "<test>").unwrap();
1326 es.require_bool(&v).unwrap();
1327 let b = es.require_bool(&v).unwrap();
1328 assert!(!b);
1329 })
1330 .unwrap();
1331 }
1332
1333 #[test]
1334 fn eval_state_value_int() {
1335 gc_registering_current_thread(|| {
1336 let store = Store::open(None, HashMap::new()).unwrap();
1337 let mut es = EvalState::new(store, []).unwrap();
1338 let v = es.eval_from_string("1", "<test>").unwrap();
1339 es.force(&v).unwrap();
1340 let t = es.value_type(&v).unwrap();
1341 assert!(t == ValueType::Int);
1342 let i = es.require_int(&v).unwrap();
1343 assert!(i == 1);
1344 })
1345 .unwrap();
1346 }
1347
1348 #[test]
1349 fn eval_state_require_int_forces_thunk() {
1350 gc_registering_current_thread(|| {
1351 let store = Store::open(None, HashMap::new()).unwrap();
1352 let mut es = EvalState::new(store, []).unwrap();
1353 let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
1354 let a = es.eval_from_string("2", "<test>").unwrap();
1355 let v = es.new_value_apply(&f, &a).unwrap();
1356 let t = es.value_type_unforced(&v);
1357 assert!(t.is_none());
1358 let i = es.require_int(&v).unwrap();
1359 assert!(i == 3);
1360 })
1361 .unwrap();
1362 }
1363
1364 #[test]
1365 fn eval_state_require_bool_forces_thunk() {
1366 gc_registering_current_thread(|| {
1367 let store = Store::open(None, HashMap::new()).unwrap();
1368 let mut es = EvalState::new(store, []).unwrap();
1369 let f = es.eval_from_string("x: !x", "<test>").unwrap();
1370 let a = es.eval_from_string("true", "<test>").unwrap();
1371 let v = es.new_value_apply(&f, &a).unwrap();
1372 let t = es.value_type_unforced(&v);
1373 assert!(t.is_none());
1374 let i = es.require_bool(&v).unwrap();
1375 assert!(!i);
1376 })
1377 .unwrap();
1378 }
1379
1380 fn make_thunk(es: &mut EvalState, expr: &str) -> Value {
1382 let f = es.eval_from_string("x: x", "<test>").unwrap();
1387 let v = es.eval_from_string(expr, "<test>").unwrap();
1388 es.new_value_apply(&f, &v).unwrap()
1389 }
1390
1391 #[test]
1392 fn make_thunk_helper_works() {
1393 gc_registering_current_thread(|| {
1394 let store = Store::open(None, HashMap::new()).unwrap();
1395 let mut es = EvalState::new(store, []).unwrap();
1396 let v = make_thunk(&mut es, "1");
1397 let t = es.value_type_unforced(&v);
1398 assert!(t.is_none());
1399 })
1400 .unwrap();
1401 }
1402
1403 #[test]
1404 fn eval_state_value_attrs_names_empty() {
1405 gc_registering_current_thread(|| {
1406 let store = Store::open(None, HashMap::new()).unwrap();
1407 let mut es = EvalState::new(store, []).unwrap();
1408 let v = es.eval_from_string("{ }", "<test>").unwrap();
1409 es.force(&v).unwrap();
1410 let t = es.value_type_unforced(&v);
1411 assert!(t == Some(ValueType::AttrSet));
1412 let attrs = es.require_attrs_names_unsorted(&v).unwrap();
1413 assert_eq!(attrs.len(), 0);
1414 })
1415 .unwrap()
1416 }
1417
1418 #[test]
1419 fn eval_state_require_attrs_names_unsorted_forces_thunk() {
1420 gc_registering_current_thread(|| {
1421 let store = Store::open(None, HashMap::new()).unwrap();
1422 let mut es = EvalState::new(store, []).unwrap();
1423 let v = make_thunk(&mut es, "{ a = 1; b = 2; }");
1424 let t = es.value_type_unforced(&v);
1425 assert!(t.is_none());
1426 let attrs = es.require_attrs_names_unsorted(&v).unwrap();
1427 assert_eq!(attrs.len(), 2);
1428 })
1429 .unwrap()
1430 }
1431
1432 #[test]
1433 fn eval_state_require_attrs_names_unsorted_bad_type() {
1434 gc_registering_current_thread(|| {
1435 let store = Store::open(None, HashMap::new()).unwrap();
1436 let mut es = EvalState::new(store, []).unwrap();
1437 let v = es.eval_from_string("1", "<test>").unwrap();
1438 es.force(&v).unwrap();
1439 let r = es.require_attrs_names_unsorted(&v);
1440 assert!(r.is_err());
1441 assert_eq!(
1442 r.unwrap_err().to_string(),
1443 "expected an attrset, but got a Int"
1444 );
1445 })
1446 .unwrap()
1447 }
1448
1449 #[test]
1450 fn eval_state_value_attrs_names_example() {
1451 gc_registering_current_thread(|| {
1452 let store = Store::open(None, HashMap::new()).unwrap();
1453 let mut es = EvalState::new(store, []).unwrap();
1454 let expr = r#"{ a = throw "nope a"; b = throw "nope b"; }"#;
1455 let v = es.eval_from_string(expr, "<test>").unwrap();
1456 let attrs = es.require_attrs_names(&v).unwrap();
1457 assert_eq!(attrs.len(), 2);
1458 assert_eq!(attrs[0], "a");
1459 assert_eq!(attrs[1], "b");
1460 })
1461 .unwrap();
1462 }
1463
1464 #[test]
1465 fn eval_state_require_attrs_select() {
1466 gc_registering_current_thread(|| {
1467 let store = Store::open(None, HashMap::new()).unwrap();
1468 let mut es = EvalState::new(store, []).unwrap();
1469 let expr = r#"{ a = "aye"; b = "bee"; }"#;
1470 let v = es.eval_from_string(expr, "<test>").unwrap();
1471 let a = es.require_attrs_select(&v, "a").unwrap();
1472 let b = es.require_attrs_select(&v, "b").unwrap();
1473 assert_eq!(es.require_string(&a).unwrap(), "aye");
1474 assert_eq!(es.require_string(&b).unwrap(), "bee");
1475 let missing = es.require_attrs_select(&v, "c");
1476 match missing {
1477 Ok(_) => panic!("expected an error"),
1478 Err(e) => {
1479 let s = format!("{e:#}");
1480 if !s.contains("attribute `c` not found") {
1481 eprintln!("unexpected error message: {}", s);
1482 panic!();
1483 }
1484 }
1485 }
1486 })
1487 .unwrap()
1488 }
1489
1490 #[test]
1491 fn eval_state_require_attrs_select_forces_thunk() {
1492 gc_registering_current_thread(|| {
1493 let store = Store::open(None, HashMap::new()).unwrap();
1494 let mut es = EvalState::new(store, []).unwrap();
1495 let expr = r#"{ a = "aye"; b = "bee"; }"#;
1496 let v = make_thunk(&mut es, expr);
1497 assert!(es.value_type_unforced(&v).is_none());
1498 let r = es.require_attrs_select(&v, "a");
1499 assert!(r.is_ok());
1500 })
1501 .unwrap()
1502 }
1503
1504 #[test]
1505 fn eval_state_require_attrs_select_error() {
1506 gc_registering_current_thread(|| {
1507 let store = Store::open(None, HashMap::new()).unwrap();
1508 let mut es = EvalState::new(store, []).unwrap();
1509 let expr = r#"{ a = throw "oh no the error"; }"#;
1510 let v = es.eval_from_string(expr, "<test>").unwrap();
1511 let r = es.require_attrs_select(&v, "a");
1512 match r {
1513 Ok(_) => panic!("expected an error"),
1514 Err(e) => {
1515 if !e.to_string().contains("oh no the error") {
1516 eprintln!("unexpected error message: {}", e);
1517 panic!();
1518 }
1519 }
1520 }
1521 })
1522 .unwrap()
1523 }
1524
1525 #[test]
1526 fn eval_state_require_attrs_select_opt() {
1527 gc_registering_current_thread(|| {
1528 let store = Store::open(None, HashMap::new()).unwrap();
1529 let mut es = EvalState::new(store, []).unwrap();
1530 let expr = r#"{ a = "aye"; b = "bee"; }"#;
1531 let v = es.eval_from_string(expr, "<test>").unwrap();
1532 let a = es.require_attrs_select_opt(&v, "a").unwrap().unwrap();
1533 let b = es.require_attrs_select_opt(&v, "b").unwrap().unwrap();
1534 assert_eq!(es.require_string(&a).unwrap(), "aye");
1535 assert_eq!(es.require_string(&b).unwrap(), "bee");
1536 let c = es.require_attrs_select_opt(&v, "c").unwrap();
1537 assert!(c.is_none());
1538 })
1539 .unwrap()
1540 }
1541
1542 #[test]
1543 fn eval_state_require_attrs_select_opt_forces_thunk() {
1544 gc_registering_current_thread(|| {
1545 let store = Store::open(None, HashMap::new()).unwrap();
1546 let mut es = EvalState::new(store, []).unwrap();
1547 let expr = r#"{ a = "aye"; b = "bee"; }"#;
1548 let v = make_thunk(&mut es, expr);
1549 assert!(es.value_type_unforced(&v).is_none());
1550 let r = es.require_attrs_select_opt(&v, "a");
1551 assert!(r.is_ok());
1552 })
1553 .unwrap()
1554 }
1555
1556 #[test]
1557 fn eval_state_require_attrs_select_opt_error() {
1558 gc_registering_current_thread(|| {
1559 let store = Store::open(None, HashMap::new()).unwrap();
1560 let mut es = EvalState::new(store, []).unwrap();
1561 let expr = r#"{ a = throw "oh no the error"; }"#;
1562 let v = es.eval_from_string(expr, "<test>").unwrap();
1563 let r = es.require_attrs_select_opt(&v, "a");
1564 match r {
1565 Ok(_) => panic!("expected an error"),
1566 Err(e) => {
1567 if !e.to_string().contains("oh no the error") {
1568 eprintln!("unexpected error message: {}", e);
1569 panic!();
1570 }
1571 }
1572 }
1573 })
1574 .unwrap()
1575 }
1576
1577 #[test]
1578 fn eval_state_value_string() {
1579 gc_registering_current_thread(|| {
1580 let store = Store::open(None, HashMap::new()).unwrap();
1581 let mut es = EvalState::new(store, []).unwrap();
1582 let v = es.eval_from_string("\"hello\"", "<test>").unwrap();
1583 es.force(&v).unwrap();
1584 let t = es.value_type_unforced(&v);
1585 assert!(t == Some(ValueType::String));
1586 let s = es.require_string(&v).unwrap();
1587 assert!(s == "hello");
1588 })
1589 .unwrap();
1590 }
1591
1592 #[test]
1593 fn eval_state_value_string_forces_thunk() {
1594 gc_registering_current_thread(|| {
1595 let store = Store::open(None, HashMap::new()).unwrap();
1596 let mut es = EvalState::new(store, []).unwrap();
1597 let v = make_thunk(&mut es, "\"hello\"");
1598 assert!(es.value_type_unforced(&v).is_none());
1599 let s = es.require_string(&v).unwrap();
1600 assert!(s == "hello");
1601 })
1602 .unwrap();
1603 }
1604
1605 #[test]
1606 fn eval_state_value_string_unexpected_bool() {
1607 gc_registering_current_thread(|| {
1608 let store = Store::open(None, HashMap::new()).unwrap();
1609 let mut es = EvalState::new(store, []).unwrap();
1610 let v = es.eval_from_string("true", "<test>").unwrap();
1611 es.force(&v).unwrap();
1612 let r = es.require_string(&v);
1613 assert!(r.is_err());
1614 assert_eq!(
1616 r.unwrap_err().to_string(),
1617 "expected a string, but got a Bool"
1618 );
1619 })
1620 .unwrap()
1621 }
1622
1623 #[test]
1624 fn eval_state_value_string_unexpected_path_value() {
1625 gc_registering_current_thread(|| {
1626 let store = Store::open(None, HashMap::new()).unwrap();
1627 let mut es = EvalState::new(store, []).unwrap();
1628 let v = es.eval_from_string("/foo", "<test>").unwrap();
1629 es.force(&v).unwrap();
1630 let r = es.require_string(&v);
1631 assert!(r.is_err());
1632 assert_eq!(
1633 r.unwrap_err().to_string(),
1634 "expected a string, but got a Path"
1635 );
1636 })
1637 .unwrap()
1638 }
1639
1640 #[test]
1641 fn eval_state_value_string_bad_utf() {
1642 gc_registering_current_thread(|| {
1643 let store = Store::open(None, HashMap::new()).unwrap();
1644 let mut es = EvalState::new(store, []).unwrap();
1645 let v = es
1646 .eval_from_string("builtins.substring 0 1 \"ü\"", "<test>")
1647 .unwrap();
1648 es.force(&v).unwrap();
1649 let t = es.value_type_unforced(&v);
1650 assert!(t == Some(ValueType::String));
1651 let r = es.require_string(&v);
1652 assert!(r.is_err());
1653 assert!(r
1654 .unwrap_err()
1655 .to_string()
1656 .contains("Nix string is not valid UTF-8"));
1657 })
1658 .unwrap();
1659 }
1660
1661 #[test]
1662 fn eval_state_value_string_unexpected_context() {
1663 gc_registering_current_thread(|| {
1664 let store = Store::open(None, HashMap::new()).unwrap();
1665 let mut es = EvalState::new(store, []).unwrap();
1666 let v = es
1667 .eval_from_string("(derivation { name = \"hello\"; system = \"dummy\"; builder = \"cmd.exe\"; }).outPath", "<test>")
1668 .unwrap();
1669 es.force(&v).unwrap();
1670 let t = es.value_type_unforced(&v);
1671 assert!(t == Some(ValueType::String));
1672 })
1677 .unwrap();
1678 }
1679
1680 #[test]
1681 fn eval_state_new_string() {
1682 gc_registering_current_thread(|| {
1683 let store = Store::open(None, HashMap::new()).unwrap();
1684 let mut es = EvalState::new(store, []).unwrap();
1685 let v = es.new_value_str("hello").unwrap();
1686 es.force(&v).unwrap();
1687 let t = es.value_type_unforced(&v);
1688 assert!(t == Some(ValueType::String));
1689 let s = es.require_string(&v).unwrap();
1690 assert!(s == "hello");
1691 })
1692 .unwrap();
1693 }
1694
1695 #[test]
1696 fn eval_state_new_string_empty() {
1697 gc_registering_current_thread(|| {
1698 let store = Store::open(None, HashMap::new()).unwrap();
1699 let mut es = EvalState::new(store, []).unwrap();
1700 let v = es.new_value_str("").unwrap();
1701 es.force(&v).unwrap();
1702 let t = es.value_type_unforced(&v);
1703 assert!(t == Some(ValueType::String));
1704 let s = es.require_string(&v).unwrap();
1705 assert!(s.is_empty());
1706 })
1707 .unwrap();
1708 }
1709
1710 #[test]
1711 fn eval_state_new_string_invalid() {
1712 gc_registering_current_thread(|| {
1713 let store = Store::open(None, HashMap::new()).unwrap();
1714 let mut es = EvalState::new(store, []).unwrap();
1715 let r = es.new_value_str("hell\0no");
1716 match r {
1717 Ok(_) => panic!("expected an error"),
1718 Err(e) => {
1719 if !e.to_string().contains("contains null byte") {
1720 eprintln!("{}", e);
1721 panic!();
1722 }
1723 }
1724 }
1725 })
1726 .unwrap();
1727 }
1728
1729 #[test]
1730 fn eval_state_new_int() {
1731 gc_registering_current_thread(|| {
1732 let store = Store::open(None, HashMap::new()).unwrap();
1733 let mut es = EvalState::new(store, []).unwrap();
1734 let v = es.new_value_int(42).unwrap();
1735 es.force(&v).unwrap();
1736 let t = es.value_type_unforced(&v);
1737 assert!(t == Some(ValueType::Int));
1738 let i = es.require_int(&v).unwrap();
1739 assert!(i == 42);
1740 })
1741 .unwrap();
1742 }
1743
1744 #[test]
1745 fn eval_state_value_attrset() {
1746 gc_registering_current_thread(|| {
1747 let store = Store::open(None, HashMap::new()).unwrap();
1748 let mut es = EvalState::new(store, []).unwrap();
1749 let v = es.eval_from_string("{ }", "<test>").unwrap();
1750 es.force(&v).unwrap();
1751 let t = es.value_type_unforced(&v);
1752 assert!(t == Some(ValueType::AttrSet));
1753 })
1754 .unwrap();
1755 }
1756
1757 #[test]
1758 fn eval_state_value_list() {
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.eval_from_string("[ ]", "<test>").unwrap();
1763 es.force(&v).unwrap();
1764 let t = es.value_type_unforced(&v);
1765 assert!(t == Some(ValueType::List));
1766 })
1767 .unwrap();
1768 }
1769
1770 #[test]
1771 fn eval_state_value_list_strict_empty() {
1772 gc_registering_current_thread(|| {
1773 let store = Store::open(None, HashMap::new()).unwrap();
1774 let mut es = EvalState::new(store, []).unwrap();
1775 let v = es.eval_from_string("[]", "<test>").unwrap();
1776 es.force(&v).unwrap();
1777 let list: Vec<Value> = es.require_list_strict(&v).unwrap();
1778 assert_eq!(list.len(), 0);
1779 })
1780 .unwrap();
1781 }
1782
1783 #[test]
1784 fn eval_state_value_list_strict_int() {
1785 gc_registering_current_thread(|| {
1786 let store = Store::open(None, HashMap::new()).unwrap();
1787 let mut es = EvalState::new(store, []).unwrap();
1788 let v = es.eval_from_string("[42]", "<test>").unwrap();
1789 es.force(&v).unwrap();
1790 let list: Vec<Value> = es.require_list_strict(&v).unwrap();
1791 assert_eq!(list.len(), 1);
1792 assert_eq!(es.require_int(&list[0]).unwrap(), 42);
1793 })
1794 .unwrap();
1795 }
1796
1797 #[test]
1798 fn eval_state_value_list_strict_int_bool() {
1799 gc_registering_current_thread(|| {
1800 let store = Store::open(None, HashMap::new()).unwrap();
1801 let mut es = EvalState::new(store, []).unwrap();
1802 let v = es.eval_from_string("[42 true]", "<test>").unwrap();
1803 es.force(&v).unwrap();
1804 let list: Vec<Value> = es.require_list_strict(&v).unwrap();
1805 assert_eq!(list.len(), 2);
1806 assert_eq!(es.require_int(&list[0]).unwrap(), 42);
1807 assert!(es.require_bool(&list[1]).unwrap());
1808 })
1809 .unwrap();
1810 }
1811
1812 #[test]
1813 fn eval_state_value_list_strict_error() {
1814 gc_registering_current_thread(|| {
1815 let store = Store::open(None, HashMap::new()).unwrap();
1816 let mut es = EvalState::new(store, []).unwrap();
1817 let v = es.eval_from_string(r#"[(throw "_evaluated_item_")]"#, "<test>").unwrap();
1818 es.force(&v).unwrap();
1819 let result: Result<Vec<Value>, _> = es.require_list_strict(&v);
1821 assert!(result.is_err());
1822 match result {
1823 Err(error_msg) => {
1824 let error_str = error_msg.to_string();
1825 assert!(error_str.contains("_evaluated_item_"));
1826 }
1827 Ok(_) => panic!("unexpected success. The item should have been evaluated and its error propagated.")
1828 }
1829 })
1830 .unwrap();
1831 }
1832
1833 #[test]
1834 fn eval_state_value_list_strict_generic_container() {
1835 gc_registering_current_thread(|| {
1836 let store = Store::open(None, HashMap::new()).unwrap();
1837 let mut es = EvalState::new(store, []).unwrap();
1838 let v = es.eval_from_string("[1 2 3]", "<test>").unwrap();
1839
1840 let vec: Vec<Value> = es.require_list_strict(&v).unwrap();
1842 assert_eq!(vec.len(), 3);
1843
1844 let deque: std::collections::VecDeque<Value> = es.require_list_strict(&v).unwrap();
1846 assert_eq!(deque.len(), 3);
1847
1848 assert_eq!(es.require_int(&vec[0]).unwrap(), 1);
1850 assert_eq!(es.require_int(&deque[0]).unwrap(), 1);
1851 })
1852 .unwrap();
1853 }
1854
1855 #[test]
1856 fn eval_state_realise_string() {
1857 gc_registering_current_thread(|| {
1858 let store = Store::open(None, HashMap::new()).unwrap();
1859 let mut es = EvalState::new(store, []).unwrap();
1860 let expr = r#"
1861 ''
1862 a derivation output: ${
1863 derivation { name = "letsbuild";
1864 system = builtins.currentSystem;
1865 builder = "/bin/sh";
1866 args = [ "-c" "echo foo > $out" ];
1867 }}
1868 a path: ${builtins.toFile "just-a-file" "ooh file good"}
1869 a derivation path by itself: ${
1870 builtins.unsafeDiscardOutputDependency
1871 (derivation {
1872 name = "not-actually-built-yet";
1873 system = builtins.currentSystem;
1874 builder = "/bin/sh";
1875 args = [ "-c" "echo foo > $out" ];
1876 }).drvPath}
1877 ''
1878 "#;
1879 let v = es.eval_from_string(expr, "<test>").unwrap();
1880 es.force(&v).unwrap();
1881 let rs = es.realise_string(&v, false).unwrap();
1882
1883 assert!(rs.s.starts_with("a derivation output:"));
1884 assert!(rs.s.contains("-letsbuild\n"));
1885 assert!(!rs.s.contains("-letsbuild.drv"));
1886 assert!(rs.s.contains("a path:"));
1887 assert!(rs.s.contains("-just-a-file"));
1888 assert!(!rs.s.contains("-just-a-file.drv"));
1889 assert!(!rs.s.contains("ooh file good"));
1890 assert!(rs.s.ends_with("-not-actually-built-yet.drv\n"));
1891
1892 assert_eq!(rs.paths.len(), 3);
1893 let mut names: Vec<String> = rs.paths.iter().map(|p| p.name().unwrap()).collect();
1894 names.sort();
1895 assert_eq!(names[0], "just-a-file");
1896 assert_eq!(names[1], "letsbuild");
1897 assert_eq!(names[2], "not-actually-built-yet.drv");
1898 })
1899 .unwrap();
1900 }
1901
1902 #[test]
1903 fn eval_state_call() {
1904 gc_registering_current_thread(|| {
1905 let store = Store::open(None, HashMap::new()).unwrap();
1906 let mut es = EvalState::new(store, []).unwrap();
1907 let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
1908 let a = es.eval_from_string("2", "<test>").unwrap();
1909 let v = es.call(f, a).unwrap();
1910 es.force(&v).unwrap();
1911 let t = es.value_type_unforced(&v);
1912 assert!(t == Some(ValueType::Int));
1913 let i = es.require_int(&v).unwrap();
1914 assert!(i == 3);
1915 })
1916 .unwrap();
1917 }
1918
1919 #[test]
1920 fn eval_state_call_multi() {
1921 gc_registering_current_thread(|| {
1922 let store = Store::open(None, HashMap::new()).unwrap();
1923 let mut es = EvalState::new(store, []).unwrap();
1924 let f = es.eval_from_string("x: y: x - y", "<test>").unwrap();
1926 let a = es.eval_from_string("2", "<test>").unwrap();
1927 let b = es.eval_from_string("3", "<test>").unwrap();
1928 let v = es.call_multi(&f, &[a, b]).unwrap();
1929 let t = es.value_type_unforced(&v);
1930 assert!(t == Some(ValueType::Int));
1931 let i = es.require_int(&v).unwrap();
1932 assert!(i == -1);
1933 })
1934 .unwrap();
1935 }
1936
1937 #[test]
1938 fn eval_state_apply() {
1939 gc_registering_current_thread(|| {
1940 let store = Store::open(None, HashMap::new()).unwrap();
1941 let mut es = EvalState::new(store, []).unwrap();
1942 let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
1944 let a = es.eval_from_string("2", "<test>").unwrap();
1945 let v = es.new_value_apply(&f, &a).unwrap();
1946 assert!(es.value_type_unforced(&v).is_none());
1947 es.force(&v).unwrap();
1948 let t = es.value_type_unforced(&v);
1949 assert!(t == Some(ValueType::Int));
1950 let i = es.require_int(&v).unwrap();
1951 assert!(i == 3);
1952 })
1953 .unwrap();
1954 }
1955
1956 #[test]
1957 fn eval_state_call_fail_body() {
1958 gc_registering_current_thread(|| {
1959 let store = Store::open(None, HashMap::new()).unwrap();
1960 let mut es = EvalState::new(store, []).unwrap();
1961 let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
1962 let a = es.eval_from_string("true", "<test>").unwrap();
1963 let r = es.call(f, a);
1964 match r {
1965 Ok(_) => panic!("expected an error"),
1966 Err(e) => {
1967 if !e.to_string().contains("cannot coerce") {
1968 eprintln!("{}", e);
1969 panic!();
1970 }
1971 }
1972 }
1973 })
1974 .unwrap();
1975 }
1976
1977 #[test]
1978 fn eval_state_call_multi_fail_body() {
1979 gc_registering_current_thread(|| {
1980 let store = Store::open(None, HashMap::new()).unwrap();
1981 let mut es = EvalState::new(store, []).unwrap();
1982 let f = es.eval_from_string("x: y: x - y", "<test>").unwrap();
1984 let a = es.eval_from_string("2", "<test>").unwrap();
1985 let b = es.eval_from_string("true", "<test>").unwrap();
1986 let r = es.call_multi(&f, &[a, b]);
1987 match r {
1988 Ok(_) => panic!("expected an error"),
1989 Err(e) => {
1990 if !e.to_string().contains("expected an integer but found") {
1991 eprintln!("{}", e);
1992 panic!();
1993 }
1994 }
1995 }
1996 })
1997 .unwrap();
1998 }
1999
2000 #[test]
2001 fn eval_state_apply_fail_body() {
2002 gc_registering_current_thread(|| {
2003 let store = Store::open(None, HashMap::new()).unwrap();
2004 let mut es = EvalState::new(store, []).unwrap();
2005 let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
2006 let a = es.eval_from_string("true", "<test>").unwrap();
2007 let r = es.new_value_apply(&f, &a).unwrap();
2009 let res = es.force(&r);
2011 match res {
2012 Ok(_) => panic!("expected an error"),
2013 Err(e) => {
2014 if !e.to_string().contains("cannot coerce") {
2015 eprintln!("{}", e);
2016 panic!();
2017 }
2018 }
2019 }
2020 })
2021 .unwrap();
2022 }
2023
2024 #[test]
2026 fn eval_state_call_fail_args() {
2027 gc_registering_current_thread(|| {
2028 let store = Store::open(None, HashMap::new()).unwrap();
2029 let mut es = EvalState::new(store, []).unwrap();
2030 let f = es.eval_from_string("{x}: x + 1", "<test>").unwrap();
2031 let a = es.eval_from_string("{}", "<test>").unwrap();
2032 let r = es.call(f, a);
2033 match r {
2034 Ok(_) => panic!("expected an error"),
2035 Err(e) => {
2036 if !e.to_string().contains("called without required argument") {
2037 eprintln!("{}", e);
2038 panic!();
2039 }
2040 }
2041 }
2042 })
2043 .unwrap();
2044 }
2045
2046 #[test]
2047 fn eval_state_call_multi_fail_args() {
2048 gc_registering_current_thread(|| {
2049 let store = Store::open(None, HashMap::new()).unwrap();
2050 let mut es = EvalState::new(store, []).unwrap();
2051 let f = es.eval_from_string("{x}: {y}: x - y", "<test>").unwrap();
2053 let a = es.eval_from_string("{x = 2;}", "<test>").unwrap();
2054 let b = es.eval_from_string("{}", "<test>").unwrap();
2055 let r = es.call_multi(&f, &[a, b]);
2056 match r {
2057 Ok(_) => panic!("expected an error"),
2058 Err(e) => {
2059 if !e.to_string().contains("called without required argument") {
2060 eprintln!("{}", e);
2061 panic!();
2062 }
2063 }
2064 }
2065 })
2066 .unwrap();
2067 }
2068
2069 #[test]
2071 fn eval_state_apply_fail_args_lazy() {
2072 gc_registering_current_thread(|| {
2073 let store = Store::open(None, HashMap::new()).unwrap();
2074 let mut es = EvalState::new(store, []).unwrap();
2075 let f = es.eval_from_string("{x}: x + 1", "<test>").unwrap();
2076 let a = es.eval_from_string("{}", "<test>").unwrap();
2077 let r = es.new_value_apply(&f, &a).unwrap();
2079 let res = es.force(&r);
2081 match res {
2082 Ok(_) => panic!("expected an error"),
2083 Err(e) => {
2084 if !e.to_string().contains("called without required argument") {
2085 eprintln!("{}", e);
2086 panic!();
2087 }
2088 }
2089 }
2090 })
2091 .unwrap();
2092 }
2093
2094 #[test]
2095 fn store_open_params() {
2096 gc_registering_current_thread(|| {
2097 let store = tempfile::tempdir().unwrap();
2098 let store_path = store.path().to_str().unwrap();
2099 let state = tempfile::tempdir().unwrap();
2100 let state_path = state.path().to_str().unwrap();
2101 let log = tempfile::tempdir().unwrap();
2102 let log_path = log.path().to_str().unwrap();
2103
2104 let mut es = EvalState::new(
2105 Store::open(
2106 Some("local"),
2107 HashMap::from([
2108 ("store", store_path),
2109 ("state", state_path),
2110 ("log", log_path),
2111 ])
2112 .iter()
2113 .map(|(a, b)| (*a, *b)),
2114 )
2115 .unwrap(),
2116 [],
2117 )
2118 .unwrap();
2119
2120 let expr = r#"
2121 ''
2122 a derivation output: ${
2123 derivation { name = "letsbuild";
2124 system = builtins.currentSystem;
2125 builder = "/bin/sh";
2126 args = [ "-c" "echo foo > $out" ];
2127 }}
2128 a path: ${builtins.toFile "just-a-file" "ooh file good"}
2129 a derivation path by itself: ${
2130 builtins.unsafeDiscardOutputDependency
2131 (derivation {
2132 name = "not-actually-built-yet";
2133 system = builtins.currentSystem;
2134 builder = "/bin/sh";
2135 args = [ "-c" "echo foo > $out" ];
2136 }).drvPath}
2137 ''
2138 "#;
2139 let derivations: [&[u8]; 3] = [
2140 b"letsbuild.drv",
2141 b"just-a-file",
2142 b"not-actually-built-yet.drv",
2143 ];
2144 let _ = es.eval_from_string(expr, "<test>").unwrap();
2145
2146 let store_contents: Vec<_> = read_dir(store.path())
2148 .unwrap()
2149 .map(|dir_entry| dir_entry.unwrap().file_name())
2150 .collect();
2151 for derivation in derivations {
2152 assert!(store_contents
2153 .iter()
2154 .any(|f| f.as_encoded_bytes().ends_with(derivation)));
2155 }
2156 assert!(!empty(read_dir(state.path()).unwrap()));
2157
2158 store.close().unwrap();
2159 state.close().unwrap();
2160 log.close().unwrap();
2161 })
2162 .unwrap();
2163 }
2164
2165 fn empty(foldable: impl IntoIterator) -> bool {
2166 foldable.into_iter().all(|_| false)
2167 }
2168
2169 #[test]
2170 fn eval_state_primop_anon_call() {
2171 gc_registering_current_thread(|| {
2172 let store = Store::open(None, []).unwrap();
2173 let mut es = EvalState::new(store, []).unwrap();
2174 let bias: Arc<Mutex<Int>> = Arc::new(Mutex::new(0));
2175 let bias_control = bias.clone();
2176
2177 let primop = primop::PrimOp::new(
2178 primop::PrimOpMeta {
2179 name: cstr!("testFunction"),
2180 args: [cstr!("a"), cstr!("b")],
2181 doc: cstr!("anonymous test function"),
2182 },
2183 Box::new(move |es, [a, b]| {
2184 let a = es.require_int(a)?;
2185 let b = es.require_int(b)?;
2186 let c = *bias.lock().unwrap();
2187 es.new_value_int(a + b + c)
2188 }),
2189 )
2190 .unwrap();
2191
2192 let f = primop.into_value(&mut es).unwrap();
2193
2194 {
2195 *bias_control.lock().unwrap() = 10;
2196 }
2197 let a = es.new_value_int(2).unwrap();
2198 let b = es.new_value_int(3).unwrap();
2199 let fa = es.call(f, a).unwrap();
2200 let v = es.call(fa, b).unwrap();
2201 es.force(&v).unwrap();
2202 let t = es.value_type(&v).unwrap();
2203 assert!(t == ValueType::Int);
2204 let i = es.require_int(&v).unwrap();
2205 assert!(i == 15);
2206 })
2207 .unwrap();
2208 }
2209
2210 #[test]
2211 fn eval_state_primop_anon_call_throw() {
2212 gc_registering_current_thread(|| {
2213 let store = Store::open(None, []).unwrap();
2214 let mut es = EvalState::new(store, []).unwrap();
2215 let f = {
2216 let prim = primop::PrimOp::new(
2217 primop::PrimOpMeta {
2218 name: cstr!("throwingTestFunction"),
2219 args: [cstr!("arg")],
2220 doc: cstr!("anonymous test function"),
2221 },
2222 Box::new(move |es, [a]| {
2223 let a = es.require_int(a)?;
2224 bail!("error with arg [{}]", a);
2225 }),
2226 )
2227 .unwrap();
2228
2229 prim.into_value(&mut es)
2230 }
2231 .unwrap();
2232 let a = es.new_value_int(2).unwrap();
2233 let r = es.call(f, a);
2234 match r {
2235 Ok(_) => panic!("expected an error"),
2236 Err(e) => {
2237 if !e.to_string().contains("error with arg [2]") {
2238 eprintln!("unexpected error message: {}", e);
2239 panic!();
2240 }
2241 }
2242 }
2243 })
2244 .unwrap();
2245 }
2246
2247 #[test]
2248 fn eval_state_primop_anon_call_no_args() {
2249 gc_registering_current_thread(|| {
2250 let store = Store::open(None, []).unwrap();
2251 let mut es = EvalState::new(store, []).unwrap();
2252 let v = es
2253 .new_value_thunk(
2254 "test_thunk",
2255 Box::new(move |es: &mut EvalState| es.new_value_int(42)),
2256 )
2257 .unwrap();
2258 es.force(&v).unwrap();
2259 let t = es.value_type(&v).unwrap();
2260 eprintln!("{:?}", t);
2261 assert!(t == ValueType::Int);
2262 let i = es.require_int(&v).unwrap();
2263 assert!(i == 42);
2264 })
2265 .unwrap();
2266 }
2267
2268 #[test]
2269 fn eval_state_primop_anon_call_no_args_lazy() {
2270 gc_registering_current_thread(|| {
2271 let store = Store::open(None, []).unwrap();
2272 let mut es = EvalState::new(store, []).unwrap();
2273 let v = es
2274 .new_value_thunk(
2275 "test_thunk",
2276 Box::new(move |_| {
2277 bail!("error message in test case eval_state_primop_anon_call_no_args_lazy")
2278 }),
2279 )
2280 .unwrap();
2281 let r = es.force(&v);
2282 match r {
2283 Ok(_) => panic!("expected an error"),
2284 Err(e) => {
2285 if !e.to_string().contains(
2286 "error message in test case eval_state_primop_anon_call_no_args_lazy",
2287 ) {
2288 eprintln!("unexpected error message: {}", e);
2289 panic!();
2290 }
2291 if !e.to_string().contains("test_thunk") {
2292 eprintln!("unexpected error message: {}", e);
2293 panic!();
2294 }
2295 }
2296 }
2297 })
2298 .unwrap();
2299 }
2300
2301 #[test]
2302 pub fn eval_state_primop_custom() {
2303 gc_registering_current_thread(|| {
2304 let store = Store::open(None, []).unwrap();
2305 let mut es = EvalState::new(store, []).unwrap();
2306 let primop = primop::PrimOp::new(
2307 primop::PrimOpMeta {
2308 name: cstr!("frobnicate"),
2309 doc: cstr!("Frobnicates widgets"),
2310 args: [cstr!("x"), cstr!("y")],
2311 },
2312 Box::new(|es, args| {
2313 let a = es.require_int(&args[0])?;
2314 let b = es.require_int(&args[1])?;
2315 es.new_value_int(a + b)
2316 }),
2317 )
2318 .unwrap();
2319 let f = primop.into_value(&mut es).unwrap();
2320 let a = es.new_value_int(2).unwrap();
2321 let b = es.new_value_int(3).unwrap();
2322 let fa = es.call(f, a).unwrap();
2323 let fb = es.call(fa, b).unwrap();
2324 es.force(&fb).unwrap();
2325 let t = es.value_type(&fb).unwrap();
2326 assert!(t == ValueType::Int);
2327 let i = es.require_int(&fb).unwrap();
2328 assert!(i == 5);
2329 })
2330 .unwrap();
2331 }
2332
2333 #[test]
2334 pub fn eval_state_primop_custom_throw() {
2335 gc_registering_current_thread(|| {
2336 let store = Store::open(None, []).unwrap();
2337 let mut es = EvalState::new(store, []).unwrap();
2338 let primop = primop::PrimOp::new(
2339 primop::PrimOpMeta {
2340 name: cstr!("frobnicate"),
2341 doc: cstr!("Frobnicates widgets"),
2342 args: [cstr!("x")],
2343 },
2344 Box::new(|_es, _args| bail!("The frob unexpectedly fizzled")),
2345 )
2346 .unwrap();
2347 let f = primop.into_value(&mut es).unwrap();
2348 let a = es.new_value_int(0).unwrap();
2349 match es.call(f, a) {
2350 Ok(_) => panic!("expected an error"),
2351 Err(e) => {
2352 if !e.to_string().contains("The frob unexpectedly fizzled") {
2353 eprintln!("unexpected error message: {}", e);
2354 panic!();
2355 }
2356 if !e.to_string().contains("frobnicate") {
2357 eprintln!("unexpected error message: {}", e);
2358 panic!();
2359 }
2360 }
2361 }
2362 })
2363 .unwrap();
2364 }
2365
2366 #[test]
2367 pub fn eval_state_new_value_attrs_from_slice_empty() {
2368 gc_registering_current_thread(|| {
2369 let store = Store::open(None, []).unwrap();
2370 let mut es = EvalState::new(store, []).unwrap();
2371 let attrs = es.new_value_attrs([]).unwrap();
2372 let t = es.value_type(&attrs).unwrap();
2373 assert!(t == ValueType::AttrSet);
2374 let names = es.require_attrs_names(&attrs).unwrap();
2375 assert!(names.is_empty());
2376 })
2377 .unwrap();
2378 }
2379
2380 #[test]
2381 pub fn eval_state_new_value_attrs_from_vec() {
2382 gc_registering_current_thread(|| {
2383 let store = Store::open(None, []).unwrap();
2384 let mut es = EvalState::new(store, []).unwrap();
2385 let attrs = {
2386 let a = es.new_value_int(1).unwrap();
2387 let b = es.new_value_int(2).unwrap();
2388 es.new_value_attrs(vec![("a".to_string(), a), ("b".to_string(), b)])
2389 .unwrap()
2390 };
2391 let t = es.value_type(&attrs).unwrap();
2392 assert!(t == ValueType::AttrSet);
2393 let names = es.require_attrs_names(&attrs).unwrap();
2394 assert_eq!(names.len(), 2);
2395 assert_eq!(names[0], "a");
2396 assert_eq!(names[1], "b");
2397 let a = es.require_attrs_select(&attrs, "a").unwrap();
2398 let b = es.require_attrs_select(&attrs, "b").unwrap();
2399 let i = es.require_int(&a).unwrap();
2400 assert_eq!(i, 1);
2401 let i = es.require_int(&b).unwrap();
2402 assert_eq!(i, 2);
2403 })
2404 .unwrap();
2405 }
2406
2407 #[test]
2408 pub fn eval_state_new_value_attrs_from_hashmap() {
2409 gc_registering_current_thread(|| {
2410 let store = Store::open(None, []).unwrap();
2411 let mut es = EvalState::new(store, []).unwrap();
2412 let attrs = {
2413 let a = es.new_value_int(1).unwrap();
2414 let b = es.new_value_int(2).unwrap();
2415 es.new_value_attrs(HashMap::from([("a".to_string(), a), ("b".to_string(), b)]))
2416 .unwrap()
2417 };
2418 let t = es.value_type(&attrs).unwrap();
2419 assert!(t == ValueType::AttrSet);
2420 let names = es.require_attrs_names(&attrs).unwrap();
2421 assert_eq!(names.len(), 2);
2422 assert_eq!(names[0], "a");
2423 assert_eq!(names[1], "b");
2424 let a = es.require_attrs_select(&attrs, "a").unwrap();
2425 let b = es.require_attrs_select(&attrs, "b").unwrap();
2426 let i = es.require_int(&a).unwrap();
2427 assert_eq!(i, 1);
2428 let i = es.require_int(&b).unwrap();
2429 assert_eq!(i, 2);
2430 })
2431 .unwrap();
2432 }
2433
2434 #[test]
2435 fn eval_state_require_list_select_idx_strict_basic() {
2436 gc_registering_current_thread(|| {
2437 let store = Store::open(None, HashMap::new()).unwrap();
2438 let mut es = EvalState::new(store, []).unwrap();
2439 let v = es.eval_from_string("[ 10 20 30 ]", "<test>").unwrap();
2440
2441 let elem0 = es.require_list_select_idx_strict(&v, 0).unwrap().unwrap();
2442 let elem1 = es.require_list_select_idx_strict(&v, 1).unwrap().unwrap();
2443 let elem2 = es.require_list_select_idx_strict(&v, 2).unwrap().unwrap();
2444
2445 assert_eq!(es.require_int(&elem0).unwrap(), 10);
2446 assert_eq!(es.require_int(&elem1).unwrap(), 20);
2447 assert_eq!(es.require_int(&elem2).unwrap(), 30);
2448 })
2449 .unwrap();
2450 }
2451
2452 #[test]
2453 fn eval_state_require_list_select_idx_strict_out_of_bounds() {
2454 gc_registering_current_thread(|| {
2455 let store = Store::open(None, HashMap::new()).unwrap();
2456 let mut es = EvalState::new(store, []).unwrap();
2457 let v = es.eval_from_string("[ 1 2 3 ]", "<test>").unwrap();
2458
2459 let out_of_bounds = es.require_list_select_idx_strict(&v, 3).unwrap();
2460 assert!(out_of_bounds.is_none());
2461
2462 let last_elem = es.require_list_select_idx_strict(&v, 2).unwrap().unwrap();
2464 assert_eq!(es.require_int(&last_elem).unwrap(), 3);
2465 })
2466 .unwrap();
2467 }
2468
2469 #[test]
2470 fn eval_state_require_list_select_idx_strict_empty_list() {
2471 gc_registering_current_thread(|| {
2472 let store = Store::open(None, HashMap::new()).unwrap();
2473 let mut es = EvalState::new(store, []).unwrap();
2474 let v = es.eval_from_string("[ ]", "<test>").unwrap();
2475
2476 let elem = es.require_list_select_idx_strict(&v, 0).unwrap();
2478 assert!(elem.is_none());
2479
2480 let size = es.require_list_size(&v).unwrap();
2482 assert_eq!(size, 0);
2483 })
2484 .unwrap();
2485 }
2486
2487 #[test]
2488 fn eval_state_require_list_select_idx_strict_forces_thunk() {
2489 gc_registering_current_thread(|| {
2490 let store = Store::open(None, HashMap::new()).unwrap();
2491 let mut es = EvalState::new(store, []).unwrap();
2492 let v = make_thunk(&mut es, "[ 42 ]");
2493 assert!(es.value_type_unforced(&v).is_none());
2494
2495 let elem = es.require_list_select_idx_strict(&v, 0).unwrap().unwrap();
2496 assert_eq!(es.require_int(&elem).unwrap(), 42);
2497 })
2498 .unwrap();
2499 }
2500
2501 #[test]
2502 fn eval_state_require_list_select_idx_strict_error_element() {
2503 gc_registering_current_thread(|| {
2504 let store = Store::open(None, HashMap::new()).unwrap();
2505 let mut es = EvalState::new(store, []).unwrap();
2506
2507 let v = es
2508 .eval_from_string("[ (1 + 1) (throw \"error\") (3 + 3) ]", "<test>")
2509 .unwrap();
2510
2511 let elem0 = es.require_list_select_idx_strict(&v, 0).unwrap().unwrap();
2512 assert_eq!(es.require_int(&elem0).unwrap(), 2);
2513
2514 let elem2 = es.require_list_select_idx_strict(&v, 2).unwrap().unwrap();
2515 assert_eq!(es.require_int(&elem2).unwrap(), 6);
2516
2517 let elem1_result = es.require_list_select_idx_strict(&v, 1);
2518 match elem1_result {
2519 Ok(_) => panic!("expected an error from throw during selection"),
2520 Err(e) => {
2521 assert!(e.to_string().contains("error"));
2522 }
2523 }
2524 })
2525 .unwrap();
2526 }
2527
2528 #[test]
2529 fn eval_state_require_list_select_idx_strict_wrong_type() {
2530 gc_registering_current_thread(|| {
2531 let store = Store::open(None, HashMap::new()).unwrap();
2532 let mut es = EvalState::new(store, []).unwrap();
2533 let v = es.eval_from_string("42", "<test>").unwrap();
2534
2535 let r = es.require_list_select_idx_strict(&v, 0);
2536 match r {
2537 Ok(_) => panic!("expected an error"),
2538 Err(e) => {
2539 let err_msg = e.to_string();
2540 assert!(err_msg.contains("expected a list, but got a"));
2541 }
2542 }
2543 })
2544 .unwrap();
2545 }
2546
2547 #[test]
2548 fn eval_state_require_list_size_basic() {
2549 gc_registering_current_thread(|| {
2550 let store = Store::open(None, HashMap::new()).unwrap();
2551 let mut es = EvalState::new(store, []).unwrap();
2552
2553 let empty = es.eval_from_string("[ ]", "<test>").unwrap();
2554 assert_eq!(es.require_list_size(&empty).unwrap(), 0);
2555
2556 let three_elem = es.eval_from_string("[ 1 2 3 ]", "<test>").unwrap();
2557 assert_eq!(es.require_list_size(&three_elem).unwrap(), 3);
2558 })
2559 .unwrap();
2560 }
2561
2562 #[test]
2563 fn eval_state_require_list_size_forces_thunk() {
2564 gc_registering_current_thread(|| {
2565 let store = Store::open(None, HashMap::new()).unwrap();
2566 let mut es = EvalState::new(store, []).unwrap();
2567 let v = make_thunk(&mut es, "[ 1 2 3 4 5 ]");
2568 assert!(es.value_type_unforced(&v).is_none());
2569
2570 let size = es.require_list_size(&v).unwrap();
2571 assert_eq!(size, 5);
2572 })
2573 .unwrap();
2574 }
2575
2576 #[test]
2577 fn eval_state_require_list_size_lazy_elements() {
2578 gc_registering_current_thread(|| {
2579 let store = Store::open(None, HashMap::new()).unwrap();
2580 let mut es = EvalState::new(store, []).unwrap();
2581
2582 let v = es
2583 .eval_from_string(
2584 "[ (throw \"error1\") (throw \"error2\") (throw \"error3\") ]",
2585 "<test>",
2586 )
2587 .unwrap();
2588
2589 let size = es.require_list_size(&v).unwrap();
2590 assert_eq!(size, 3);
2591 })
2592 .unwrap();
2593 }
2594
2595 #[test]
2596 fn eval_state_require_list_size_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("\"not a list\"", "<test>").unwrap();
2601
2602 let r = es.require_list_size(&v);
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]
2637 #[cfg(nix_at_least = "2.26" )]
2638 fn eval_state_builder_path_coercion() {
2639 gc_registering_current_thread(|| {
2640 let mut store = Store::open(None, HashMap::new()).unwrap();
2641 let mut es = EvalStateBuilder::new(store.clone())
2642 .unwrap()
2643 .build()
2644 .unwrap();
2645
2646 let expr = r#"builtins.toFile "test-file.txt" "test content""#;
2649
2650 let value = es.eval_from_string(expr, "<test>").unwrap();
2652
2653 let realised = es.realise_string(&value, false).unwrap();
2655
2656 assert_eq!(
2658 realised.paths.len(),
2659 1,
2660 "Expected 1 store path, got {}",
2661 realised.paths.len()
2662 );
2663
2664 let physical_path = store.real_path(&realised.paths[0]).unwrap();
2667
2668 assert!(
2670 std::path::Path::new(&physical_path).exists(),
2671 "Store path should exist: {}",
2672 physical_path
2673 );
2674
2675 let store_content = std::fs::read_to_string(&physical_path).unwrap();
2677 assert_eq!(store_content, "test content");
2678 })
2679 .unwrap();
2680 }
2681
2682 #[test]
2696 #[cfg(nix_at_least = "2.26")]
2697 fn eval_state_builder_loads_max_call_depth() {
2698 gc_registering_current_thread(|| {
2699 let store = Store::open(None, HashMap::new()).unwrap();
2700 let mut es = EvalStateBuilder::new(store).unwrap().build().unwrap();
2701
2702 let expr = r#"
2705 let
2706 recurse = n: if n == 0 then "done" else recurse (n - 1);
2707 in
2708 recurse 1100
2709 "#;
2710
2711 let result = es.eval_from_string(expr, "<test>");
2712
2713 match result {
2714 Err(e) => {
2715 let err_str = e.to_string();
2716 assert!(
2717 err_str.contains("max-call-depth"),
2718 "Expected max-call-depth error, got: {}",
2719 err_str
2720 );
2721 }
2722 Ok(_) => {
2723 panic!(
2724 "Expected recursion to fail with max-call-depth=1000, but it succeeded. \
2725 This indicates eval_state_builder_load() was not called."
2726 );
2727 }
2728 }
2729 })
2730 .unwrap();
2731 }
2732
2733 #[test]
2742 #[cfg(nix_at_least = "2.26")]
2743 fn eval_state_builder_ignores_ambient_when_disabled() {
2744 gc_registering_current_thread(|| {
2745 let store = Store::open(None, HashMap::new()).unwrap();
2746 let mut es = EvalStateBuilder::new(store)
2747 .unwrap()
2748 .load_ambient_settings(false)
2749 .build()
2750 .unwrap();
2751
2752 let expr = r#"
2756 let
2757 recurse = n: if n == 0 then "done" else recurse (n - 1);
2758 in
2759 recurse 1100
2760 "#;
2761
2762 let result = es.eval_from_string(expr, "<test>");
2763
2764 match result {
2765 Ok(value) => {
2766 let result_str = es.require_string(&value).unwrap();
2768 assert_eq!(result_str, "done");
2769 }
2770 Err(e) => {
2771 panic!(
2772 "Expected recursion to succeed with default max-call-depth=10000, \
2773 but it failed: {}. This indicates ambient settings were not ignored.",
2774 e
2775 );
2776 }
2777 }
2778 })
2779 .unwrap();
2780 }
2781
2782 #[test]
2783 #[cfg(nix_at_least = "2.34.0pre")]
2784 fn eval_state_primop_recoverable_error() {
2785 gc_registering_current_thread(|| {
2786 let store = Store::open(None, []).unwrap();
2787 let mut es = EvalState::new(store, []).unwrap();
2788
2789 let call_count = std::cell::Cell::new(0u32);
2790 let v = es
2791 .new_value_thunk(
2792 "recoverable_test",
2793 Box::new(move |es: &mut EvalState| {
2794 let count = call_count.get();
2795 call_count.set(count + 1);
2796 if count == 0 {
2797 Err(primop::RecoverableError::new("transient failure").into())
2798 } else {
2799 es.new_value_int(42)
2800 }
2801 }),
2802 )
2803 .unwrap();
2804
2805 let r = es.force(&v);
2807 assert!(r.is_err());
2808 assert!(
2809 r.unwrap_err().to_string().contains("transient failure"),
2810 "Error message should contain 'transient failure'"
2811 );
2812
2813 es.force(&v).unwrap();
2815 let i = es.require_int(&v).unwrap();
2816 assert_eq!(i, 42);
2817 })
2818 .unwrap();
2819 }
2820
2821 #[test]
2822 #[cfg(nix_at_least = "2.34.0pre")]
2823 fn eval_state_primop_recoverable_error_in_chain() {
2824 gc_registering_current_thread(|| {
2825 let store = Store::open(None, []).unwrap();
2826 let mut es = EvalState::new(store, []).unwrap();
2827
2828 let call_count = std::cell::Cell::new(0u32);
2829 let v = es
2830 .new_value_thunk(
2831 "recoverable_chain_test",
2832 Box::new(move |es: &mut EvalState| {
2833 let count = call_count.get();
2834 call_count.set(count + 1);
2835 if count == 0 {
2836 use anyhow::Context;
2838 Err(primop::RecoverableError::new("transient failure"))
2839 .context("wrapper context")?
2840 } else {
2841 es.new_value_int(42)
2842 }
2843 }),
2844 )
2845 .unwrap();
2846
2847 let r = es.force(&v);
2849 assert!(r.is_err());
2850
2851 es.force(&v).unwrap();
2853 let i = es.require_int(&v).unwrap();
2854 assert_eq!(i, 42);
2855 })
2856 .unwrap();
2857 }
2858
2859 #[test]
2862 fn eval_state_primop_register_builtin() {
2863 gc_registering_current_thread(|| {
2864 let primop = primop::PrimOp::new(
2865 primop::PrimOpMeta {
2866 name: cstr!("__test_answer"),
2867 doc: cstr!("Returns the answer to life, the universe, and everything."),
2868 args: [cstr!("_ignored")],
2869 },
2870 Box::new(|es, _args| es.new_value_int(42)),
2871 )
2872 .unwrap();
2873 primop.register_globally().unwrap();
2874
2875 let store = Store::open(None, HashMap::new()).unwrap();
2878 let mut es = EvalState::new(store, []).unwrap();
2879 let v = es.eval_from_string("__test_answer null", "<test>").unwrap();
2881 assert_eq!(es.require_int(&v).unwrap(), 42);
2882 let v = es
2884 .eval_from_string("builtins.test_answer null", "<test>")
2885 .unwrap();
2886 assert_eq!(es.require_int(&v).unwrap(), 42);
2887 })
2888 .unwrap();
2889 }
2890}