1#![warn(missing_docs)]
2#![cfg_attr(test, allow(clippy::arc_with_non_send_sync))]
9use std::fmt;
119#[cfg(any(
120 feature = "store",
121 feature = "expr",
122 feature = "flake",
123 feature = "external",
124 feature = "primop"
125))]
126use std::{
127 ffi::{CStr, CString},
128 ptr::NonNull,
129};
130#[cfg(any(
131 feature = "expr",
132 feature = "flake",
133 feature = "external",
134 feature = "primop"
135))]
136use std::{path::Path, sync::Arc};
137
138#[cfg(feature = "expr")] mod attrs;
139#[cfg(feature = "expr")] mod lists;
140
141#[cfg(feature = "external")] pub mod external;
142#[cfg(feature = "flake")] pub mod flake;
143#[cfg(feature = "primop")] pub mod primop;
144
145#[cfg(all(test, any(feature = "store", feature = "expr")))]
146use serial_test::serial;
147
148#[doc(hidden)]
155pub mod sys {
156 pub use nix_bindings_sys::*;
157}
158
159pub type Result<T> = std::result::Result<T, Error>;
161
162#[derive(Debug)]
164pub enum Error {
165 Unknown(String),
167
168 Overflow,
170
171 KeyNotFound(String),
173
174 IndexOutOfBounds {
176 index: usize,
178 length: usize,
180 },
181
182 EvalError(String),
184
185 InvalidType {
187 expected: &'static str,
189 actual: String,
191 },
192 NullPointer,
194
195 StringConversion(std::ffi::NulError),
197}
198
199impl fmt::Display for Error {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 match self {
202 Error::Unknown(msg) => write!(f, "Unknown error: {msg}"),
203 Error::Overflow => write!(f, "Overflow error"),
204 Error::KeyNotFound(key) => write!(f, "Key not found: {key}"),
205 Error::IndexOutOfBounds { index, length } => {
206 write!(f, "Index out of bounds: index {index}, length {length}")
207 },
208 Error::EvalError(msg) => write!(f, "Evaluation error: {msg}"),
209 Error::InvalidType { expected, actual } => {
210 write!(f, "Invalid type: expected {expected}, got {actual}")
211 },
212 Error::NullPointer => write!(f, "Null pointer error"),
213 Error::StringConversion(e) => write!(f, "String conversion error: {e}"),
214 }
215 }
216}
217
218impl std::error::Error for Error {}
219
220impl From<std::ffi::NulError> for Error {
221 fn from(e: std::ffi::NulError) -> Self {
222 Error::StringConversion(e)
223 }
224}
225
226#[cfg(feature = "store")] mod store;
227#[cfg(feature = "store")]
228pub use store::{Derivation, Store, StorePath};
229
230#[cfg(feature = "store")]
239unsafe fn string_from_callback<F>(call: F) -> Option<String>
240where
241 F: FnOnce(sys::nix_get_string_callback, *mut std::os::raw::c_void),
242{
243 unsafe extern "C" fn collect(
244 start: *const std::os::raw::c_char,
245 n: std::os::raw::c_uint,
246 user_data: *mut std::os::raw::c_void,
247 ) {
248 let result = unsafe { &mut *(user_data as *mut Option<String>) };
249 if !start.is_null() {
250 let bytes =
251 unsafe { std::slice::from_raw_parts(start.cast::<u8>(), n as usize) };
252 *result = std::str::from_utf8(bytes).ok().map(|s| s.to_owned());
253 }
254 }
255
256 let mut result: Option<String> = None;
257 let user_data = &mut result as *mut _ as *mut std::os::raw::c_void;
258 call(Some(collect), user_data);
259 result
260}
261
262#[cfg(feature = "store")]
275pub fn is_pure_eval() -> bool {
276 unsafe {
279 let val = string_from_callback(|cb, ud| {
280 sys::nix_setting_get(std::ptr::null_mut(), c"pure-eval".as_ptr(), cb, ud);
281 });
282 val.as_deref() == Some("true")
283 }
284}
285
286#[cfg(feature = "store")]
289fn check_err(ctx: *mut sys::nix_c_context, err: sys::nix_err) -> Result<()> {
290 if err == sys::nix_err_NIX_OK {
291 return Ok(());
292 }
293
294 let msg = unsafe {
298 let ptr = sys::nix_err_msg(std::ptr::null_mut(), ctx, std::ptr::null_mut());
299 if ptr.is_null() {
300 None
301 } else {
302 Some(CStr::from_ptr(ptr).to_string_lossy().into_owned())
303 }
304 };
305
306 let detail = if err == sys::nix_err_NIX_ERR_NIX_ERROR {
308 unsafe {
309 string_from_callback(|cb, ud| {
310 sys::nix_err_info_msg(std::ptr::null_mut(), ctx, cb, ud);
311 })
312 }
313 } else {
314 None
315 };
316
317 let message = detail
318 .or(msg)
319 .unwrap_or_else(|| format!("Nix error code: {err}"));
320
321 match err {
322 sys::nix_err_NIX_ERR_UNKNOWN => Err(Error::Unknown(message)),
323 sys::nix_err_NIX_ERR_OVERFLOW => Err(Error::Overflow),
324 sys::nix_err_NIX_ERR_KEY => Err(Error::KeyNotFound(message)),
325 sys::nix_err_NIX_ERR_NIX_ERROR => Err(Error::EvalError(message)),
326 _ => Err(Error::Unknown(message)),
327 }
328}
329
330#[cfg(feature = "store")]
334#[must_use]
335pub fn nix_version() -> &'static str {
336 unsafe {
338 let ptr = sys::nix_version_get();
339 if ptr.is_null() {
340 "<unknown>"
341 } else {
342 CStr::from_ptr(ptr).to_str().unwrap_or("<unknown>")
343 }
344 }
345}
346
347#[cfg(feature = "store")]
349#[derive(Debug, Clone, Copy, PartialEq, Eq)]
350pub enum Verbosity {
351 Error,
353 Warn,
355 Notice,
357 Info,
359 Talkative,
361 Chatty,
363 Debug,
365 Vomit,
367}
368
369#[cfg(feature = "store")]
370impl Verbosity {
371 fn to_c(self) -> sys::nix_verbosity {
372 match self {
373 Verbosity::Error => sys::nix_verbosity_NIX_LVL_ERROR,
374 Verbosity::Warn => sys::nix_verbosity_NIX_LVL_WARN,
375 Verbosity::Notice => sys::nix_verbosity_NIX_LVL_NOTICE,
376 Verbosity::Info => sys::nix_verbosity_NIX_LVL_INFO,
377 Verbosity::Talkative => sys::nix_verbosity_NIX_LVL_TALKATIVE,
378 Verbosity::Chatty => sys::nix_verbosity_NIX_LVL_CHATTY,
379 Verbosity::Debug => sys::nix_verbosity_NIX_LVL_DEBUG,
380 Verbosity::Vomit => sys::nix_verbosity_NIX_LVL_VOMIT,
381 }
382 }
383}
384
385#[cfg(feature = "store")]
390pub struct Context {
391 inner: NonNull<sys::nix_c_context>,
392}
393
394#[cfg(feature = "store")]
395impl Context {
396 pub fn new() -> Result<Self> {
404 let ctx_ptr = unsafe { sys::nix_c_context_create() };
406 let inner = NonNull::new(ctx_ptr).ok_or(Error::NullPointer)?;
407
408 let ctx = Context { inner };
409
410 static INIT: std::sync::Once = std::sync::Once::new();
414 let mut init_err: Result<()> = Ok(());
415 INIT.call_once(|| {
416 let r = (|| unsafe {
418 check_err(
419 ctx.inner.as_ptr(),
420 sys::nix_libutil_init(ctx.inner.as_ptr()),
421 )?;
422 check_err(
423 ctx.inner.as_ptr(),
424 sys::nix_libstore_init(ctx.inner.as_ptr()),
425 )?;
426 check_err(
427 ctx.inner.as_ptr(),
428 sys::nix_libexpr_init(ctx.inner.as_ptr()),
429 )
430 })();
431 init_err = r;
432 });
433 init_err?;
434
435 Ok(ctx)
436 }
437
438 pub fn set_setting(&self, key: &str, value: &str) -> Result<()> {
447 let key_c = CString::new(key)?;
448 let value_c = CString::new(value)?;
449 unsafe {
451 check_err(
452 self.inner.as_ptr(),
453 sys::nix_setting_set(
454 self.inner.as_ptr(),
455 key_c.as_ptr(),
456 value_c.as_ptr(),
457 ),
458 )
459 }
460 }
461
462 pub fn get_setting(&self, key: &str) -> Result<String> {
468 let key_c = CString::new(key)?;
469 let mut err_code = sys::nix_err_NIX_OK;
470 let result = unsafe {
472 string_from_callback(|cb, ud| {
473 err_code =
474 sys::nix_setting_get(self.inner.as_ptr(), key_c.as_ptr(), cb, ud);
475 })
476 };
477 check_err(self.inner.as_ptr(), err_code)?;
478 result.ok_or_else(|| Error::KeyNotFound(key.to_string()))
479 }
480
481 pub fn set_verbosity(&self, level: Verbosity) -> Result<()> {
487 unsafe {
489 check_err(
490 self.inner.as_ptr(),
491 sys::nix_set_verbosity(self.inner.as_ptr(), level.to_c()),
492 )
493 }
494 }
495
496 pub(crate) unsafe fn as_ptr(&self) -> *mut sys::nix_c_context {
502 self.inner.as_ptr()
503 }
504}
505
506#[cfg(feature = "store")]
507impl Drop for Context {
508 fn drop(&mut self) {
509 unsafe {
511 sys::nix_c_context_free(self.inner.as_ptr());
512 }
513 }
514}
515
516#[cfg(feature = "store")]
523unsafe impl Send for Context {}
524
525#[cfg(feature = "expr")]
530pub struct EvalStateBuilder {
531 inner: NonNull<sys::nix_eval_state_builder>,
532 store: Arc<Store>,
533 context: Arc<Context>,
534 skip_load: bool,
535}
536
537#[cfg(feature = "expr")]
538impl EvalStateBuilder {
539 pub fn new(store: &Arc<Store>) -> Result<Self> {
549 let builder_ptr = unsafe {
551 sys::nix_eval_state_builder_new(store._context.as_ptr(), store.as_ptr())
552 };
553
554 let inner = NonNull::new(builder_ptr).ok_or(Error::NullPointer)?;
555
556 Ok(EvalStateBuilder {
557 inner,
558 store: Arc::clone(store),
559 context: Arc::clone(&store._context),
560 skip_load: false,
561 })
562 }
563
564 pub fn set_lookup_path(self, paths: &[impl AsRef<str>]) -> Result<Self> {
573 let c_strings: Vec<CString> = paths
575 .iter()
576 .map(|s| CString::new(s.as_ref()))
577 .collect::<std::result::Result<_, _>>()?;
578
579 let mut ptrs: Vec<*const std::os::raw::c_char> =
580 c_strings.iter().map(|cs| cs.as_ptr()).collect();
581 ptrs.push(std::ptr::null()); unsafe {
585 check_err(
586 self.context.as_ptr(),
587 sys::nix_eval_state_builder_set_lookup_path(
588 self.context.as_ptr(),
589 self.inner.as_ptr(),
590 ptrs.as_mut_ptr(),
591 ),
592 )?;
593 }
594
595 Ok(self)
596 }
597
598 #[cfg(feature = "flake")]
607 pub fn with_flake_settings(
608 self,
609 settings: &flake::FlakeSettings,
610 ) -> Result<Self> {
611 unsafe {
613 check_err(
614 self.context.as_ptr(),
615 sys::nix_flake_settings_add_to_eval_state_builder(
616 self.context.as_ptr(),
617 settings.as_ptr(),
618 self.inner.as_ptr(),
619 ),
620 )?;
621 }
622
623 Ok(self)
624 }
625
626 #[must_use]
633 pub fn no_load_config(mut self) -> Self {
634 self.skip_load = true;
635 self
636 }
637
638 pub fn build(self) -> Result<EvalState> {
644 if !self.skip_load {
647 unsafe {
648 check_err(
649 self.context.as_ptr(),
650 sys::nix_eval_state_builder_load(
651 self.context.as_ptr(),
652 self.inner.as_ptr(),
653 ),
654 )?;
655 }
656 }
657
658 let state_ptr = unsafe {
661 sys::nix_eval_state_build(self.context.as_ptr(), self.inner.as_ptr())
662 };
663
664 let inner = NonNull::new(state_ptr).ok_or(Error::NullPointer)?;
665
666 Ok(EvalState {
667 inner,
668 store: self.store.clone(),
669 context: self.context.clone(),
670 })
671 }
672}
673
674#[cfg(feature = "expr")]
675impl Drop for EvalStateBuilder {
676 fn drop(&mut self) {
677 unsafe {
679 sys::nix_eval_state_builder_free(self.inner.as_ptr());
680 }
681 }
682}
683
684#[cfg(feature = "expr")]
689pub struct EvalState {
690 pub(crate) inner: NonNull<sys::EvalState>,
691 #[expect(dead_code, reason = "keeps the Arc<Store> alive Drop side-effects")]
692 store: Arc<Store>,
693 pub(crate) context: Arc<Context>,
694}
695
696#[cfg(feature = "expr")]
697impl EvalState {
698 pub fn eval_from_string(&self, expr: &str, path: &str) -> Result<Value<'_>> {
709 let expr_c = CString::new(expr)?;
710 let path_c = CString::new(path)?;
711
712 let value_ptr = unsafe {
715 sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr())
716 };
717 if value_ptr.is_null() {
718 return Err(Error::NullPointer);
719 }
720
721 unsafe {
724 check_err(
725 self.context.as_ptr(),
726 sys::nix_expr_eval_from_string(
727 self.context.as_ptr(),
728 self.inner.as_ptr(),
729 expr_c.as_ptr(),
730 path_c.as_ptr(),
731 value_ptr,
732 ),
733 )?;
734 }
735
736 let inner = NonNull::new(value_ptr).ok_or(Error::NullPointer)?;
737
738 Ok(Value { inner, state: self })
739 }
740
741 pub fn eval_from_file(&self, path: impl AsRef<Path>) -> Result<Value<'_>> {
750 let path = path.as_ref();
751 let expr = std::fs::read_to_string(path).map_err(|e| {
752 Error::Unknown(format!("Failed to read file {}: {e}", path.display()))
753 })?;
754 let base_path = path.parent().unwrap_or_else(|| Path::new("."));
755 #[cfg(unix)]
758 let base_cstring = {
759 use std::os::unix::ffi::OsStrExt as _;
760 CString::new(base_path.as_os_str().as_bytes())?
761 };
762 #[cfg(not(unix))]
763 let base_cstring = {
764 let base = base_path.to_str().ok_or_else(|| {
765 Error::Unknown("File path is not valid UTF-8".to_string())
766 })?;
767 CString::new(base)?
768 };
769 let base_str = base_cstring.to_string_lossy();
773 self.eval_from_string(&expr, &base_str)
774 }
775
776 pub fn alloc_value(&self) -> Result<Value<'_>> {
782 let value_ptr = unsafe {
784 sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr())
785 };
786 let inner = NonNull::new(value_ptr).ok_or(Error::NullPointer)?;
787
788 Ok(Value { inner, state: self })
789 }
790
791 pub fn make_int(&self, i: i64) -> Result<Value<'_>> {
797 let v = self.alloc_value()?;
798 unsafe {
800 check_err(
801 self.context.as_ptr(),
802 sys::nix_init_int(self.context.as_ptr(), v.inner.as_ptr(), i),
803 )?;
804 }
805 Ok(v)
806 }
807
808 pub fn make_float(&self, f: f64) -> Result<Value<'_>> {
814 let v = self.alloc_value()?;
815 unsafe {
817 check_err(
818 self.context.as_ptr(),
819 sys::nix_init_float(self.context.as_ptr(), v.inner.as_ptr(), f),
820 )?;
821 }
822 Ok(v)
823 }
824
825 pub fn make_bool(&self, b: bool) -> Result<Value<'_>> {
831 let v = self.alloc_value()?;
832 unsafe {
834 check_err(
835 self.context.as_ptr(),
836 sys::nix_init_bool(self.context.as_ptr(), v.inner.as_ptr(), b),
837 )?;
838 }
839 Ok(v)
840 }
841
842 pub fn make_null(&self) -> Result<Value<'_>> {
848 let v = self.alloc_value()?;
849 unsafe {
851 check_err(
852 self.context.as_ptr(),
853 sys::nix_init_null(self.context.as_ptr(), v.inner.as_ptr()),
854 )?;
855 }
856 Ok(v)
857 }
858
859 pub fn make_string(&self, s: &str) -> Result<Value<'_>> {
866 let v = self.alloc_value()?;
867 let s_c = CString::new(s)?;
868 unsafe {
870 check_err(
871 self.context.as_ptr(),
872 sys::nix_init_string(
873 self.context.as_ptr(),
874 v.inner.as_ptr(),
875 s_c.as_ptr(),
876 ),
877 )?;
878 }
879 Ok(v)
880 }
881
882 pub fn make_path(&self, path: impl AsRef<Path>) -> Result<Value<'_>> {
899 let v = self.alloc_value()?;
900 let path_str = path
901 .as_ref()
902 .to_str()
903 .ok_or_else(|| Error::Unknown("Path is not valid UTF-8".to_string()))?;
904 let path_c = CString::new(path_str)?;
905
906 #[cfg(feature = "shim")]
913 if path.as_ref().is_absolute()
914 && path_str.starts_with("/nix/store/")
915 && is_pure_eval()
916 {
917 unsafe {
919 check_err(
920 self.context.as_ptr(),
921 sys::nix_eval_state_allow_path(
922 self.context.as_ptr(),
923 self.inner.as_ptr(),
924 path_c.as_ptr(),
925 ),
926 )?;
927 }
928 }
929
930 unsafe {
932 check_err(
933 self.context.as_ptr(),
934 sys::nix_init_path_string(
935 self.context.as_ptr(),
936 self.inner.as_ptr(),
937 v.inner.as_ptr(),
938 path_c.as_ptr(),
939 ),
940 )?;
941 }
942 Ok(v)
943 }
944
945 pub fn make_list(&self, items: &[&Value<'_>]) -> Result<Value<'_>> {
951 let builder = unsafe {
953 sys::nix_make_list_builder(
954 self.context.as_ptr(),
955 self.inner.as_ptr(),
956 items.len(),
957 )
958 };
959 if builder.is_null() {
960 return Err(Error::NullPointer);
961 }
962
963 struct ListBuilderGuard(*mut sys::ListBuilder);
965 impl Drop for ListBuilderGuard {
966 fn drop(&mut self) {
967 unsafe { sys::nix_list_builder_free(self.0) };
968 }
969 }
970 let _guard = ListBuilderGuard(builder);
971
972 for (i, item) in items.iter().enumerate() {
974 unsafe {
976 check_err(
977 self.context.as_ptr(),
978 sys::nix_list_builder_insert(
979 self.context.as_ptr(),
980 builder,
981 i as std::os::raw::c_uint,
982 item.inner.as_ptr(),
983 ),
984 )?;
985 }
986 }
987
988 let result = self.alloc_value()?;
989 unsafe {
991 check_err(
992 self.context.as_ptr(),
993 sys::nix_make_list(
994 self.context.as_ptr(),
995 builder,
996 result.inner.as_ptr(),
997 ),
998 )?;
999 }
1000
1001 Ok(result)
1002 }
1003
1004 pub fn make_attrs<'s>(
1010 &'s self,
1011 pairs: &[(&str, &Value<'_>)],
1012 ) -> Result<Value<'s>> {
1013 let builder = unsafe {
1015 sys::nix_make_bindings_builder(
1016 self.context.as_ptr(),
1017 self.inner.as_ptr(),
1018 pairs.len(),
1019 )
1020 };
1021 if builder.is_null() {
1022 return Err(Error::NullPointer);
1023 }
1024
1025 struct BindingsBuilderGuard(*mut sys::BindingsBuilder);
1027 impl Drop for BindingsBuilderGuard {
1028 fn drop(&mut self) {
1029 unsafe { sys::nix_bindings_builder_free(self.0) };
1030 }
1031 }
1032 let _guard = BindingsBuilderGuard(builder);
1033
1034 for (key, value) in pairs {
1036 let key_c = CString::new(*key)?;
1037 unsafe {
1039 check_err(
1040 self.context.as_ptr(),
1041 sys::nix_bindings_builder_insert(
1042 self.context.as_ptr(),
1043 builder,
1044 key_c.as_ptr(),
1045 value.inner.as_ptr(),
1046 ),
1047 )?;
1048 }
1049 }
1050
1051 let result = self.alloc_value()?;
1052 unsafe {
1054 check_err(
1055 self.context.as_ptr(),
1056 sys::nix_make_attrs(
1057 self.context.as_ptr(),
1058 result.inner.as_ptr(),
1059 builder,
1060 ),
1061 )?;
1062 }
1063
1064 Ok(result)
1065 }
1066
1067 pub(crate) unsafe fn as_ptr(&self) -> *mut sys::EvalState {
1073 self.inner.as_ptr()
1074 }
1075}
1076
1077#[cfg(feature = "expr")]
1078impl Drop for EvalState {
1079 fn drop(&mut self) {
1080 unsafe {
1082 sys::nix_state_free(self.inner.as_ptr());
1083 }
1084 }
1085}
1086
1087#[cfg(feature = "expr")]
1099unsafe impl Send for EvalState {}
1100
1101#[cfg(feature = "expr")]
1103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1104pub enum ValueType {
1105 Thunk,
1107 Int,
1109 Float,
1111 Bool,
1113 String,
1115 Path,
1117 Null,
1119 Attrs,
1121 List,
1123 Function,
1125 External,
1127}
1128
1129#[cfg(feature = "expr")]
1130impl ValueType {
1131 fn from_c(value_type: sys::ValueType) -> Self {
1132 match value_type {
1133 sys::ValueType_NIX_TYPE_THUNK => ValueType::Thunk,
1134 sys::ValueType_NIX_TYPE_INT => ValueType::Int,
1135 sys::ValueType_NIX_TYPE_FLOAT => ValueType::Float,
1136 sys::ValueType_NIX_TYPE_BOOL => ValueType::Bool,
1137 sys::ValueType_NIX_TYPE_STRING => ValueType::String,
1138 sys::ValueType_NIX_TYPE_PATH => ValueType::Path,
1139 sys::ValueType_NIX_TYPE_NULL => ValueType::Null,
1140 sys::ValueType_NIX_TYPE_ATTRS => ValueType::Attrs,
1141 sys::ValueType_NIX_TYPE_LIST => ValueType::List,
1142 sys::ValueType_NIX_TYPE_FUNCTION => ValueType::Function,
1143 sys::ValueType_NIX_TYPE_EXTERNAL => ValueType::External,
1144 _ => ValueType::Thunk, }
1146 }
1147}
1148
1149#[cfg(feature = "expr")]
1150impl fmt::Display for ValueType {
1151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1152 let name = match self {
1153 ValueType::Thunk => "thunk",
1154 ValueType::Int => "int",
1155 ValueType::Float => "float",
1156 ValueType::Bool => "bool",
1157 ValueType::String => "string",
1158 ValueType::Path => "path",
1159 ValueType::Null => "null",
1160 ValueType::Attrs => "attrs",
1161 ValueType::List => "list",
1162 ValueType::Function => "function",
1163 ValueType::External => "external",
1164 };
1165 write!(f, "{name}")
1166 }
1167}
1168
1169#[cfg(feature = "expr")]
1175pub struct Value<'a> {
1176 pub(crate) inner: NonNull<sys::nix_value>,
1177 pub(crate) state: &'a EvalState,
1178}
1179
1180#[cfg(feature = "expr")]
1181impl Value<'_> {
1182 pub fn force(&mut self) -> Result<()> {
1190 unsafe {
1192 check_err(
1193 self.state.context.as_ptr(),
1194 sys::nix_value_force(
1195 self.state.context.as_ptr(),
1196 self.state.as_ptr(),
1197 self.inner.as_ptr(),
1198 ),
1199 )
1200 }
1201 }
1202
1203 pub fn force_deep(&mut self) -> Result<()> {
1211 unsafe {
1213 check_err(
1214 self.state.context.as_ptr(),
1215 sys::nix_value_force_deep(
1216 self.state.context.as_ptr(),
1217 self.state.as_ptr(),
1218 self.inner.as_ptr(),
1219 ),
1220 )
1221 }
1222 }
1223
1224 #[must_use]
1226 pub fn value_type(&self) -> ValueType {
1227 let c_type = unsafe {
1229 sys::nix_get_type(self.state.context.as_ptr(), self.inner.as_ptr())
1230 };
1231 ValueType::from_c(c_type)
1232 }
1233
1234 pub fn as_int(&self) -> Result<i64> {
1240 if self.value_type() != ValueType::Int {
1241 return Err(Error::InvalidType {
1242 expected: "int",
1243 actual: self.value_type().to_string(),
1244 });
1245 }
1246
1247 let result = unsafe {
1249 sys::nix_get_int(self.state.context.as_ptr(), self.inner.as_ptr())
1250 };
1251
1252 Ok(result)
1253 }
1254
1255 pub fn as_float(&self) -> Result<f64> {
1261 if self.value_type() != ValueType::Float {
1262 return Err(Error::InvalidType {
1263 expected: "float",
1264 actual: self.value_type().to_string(),
1265 });
1266 }
1267
1268 let result = unsafe {
1270 sys::nix_get_float(self.state.context.as_ptr(), self.inner.as_ptr())
1271 };
1272
1273 Ok(result)
1274 }
1275
1276 pub fn as_bool(&self) -> Result<bool> {
1282 if self.value_type() != ValueType::Bool {
1283 return Err(Error::InvalidType {
1284 expected: "bool",
1285 actual: self.value_type().to_string(),
1286 });
1287 }
1288
1289 let result = unsafe {
1291 sys::nix_get_bool(self.state.context.as_ptr(), self.inner.as_ptr())
1292 };
1293
1294 Ok(result)
1295 }
1296
1297 pub fn as_string(&self) -> Result<String> {
1306 if self.value_type() != ValueType::String {
1307 return Err(Error::InvalidType {
1308 expected: "string",
1309 actual: self.value_type().to_string(),
1310 });
1311 }
1312
1313 let realised_str = unsafe {
1316 sys::nix_string_realise(
1317 self.state.context.as_ptr(),
1318 self.state.as_ptr(),
1319 self.inner.as_ptr(),
1320 false,
1321 )
1322 };
1323
1324 if realised_str.is_null() {
1325 return Err(Error::NullPointer);
1326 }
1327
1328 let buffer_start =
1330 unsafe { sys::nix_realised_string_get_buffer_start(realised_str) };
1331
1332 let buffer_size =
1333 unsafe { sys::nix_realised_string_get_buffer_size(realised_str) };
1334
1335 if buffer_start.is_null() {
1336 unsafe { sys::nix_realised_string_free(realised_str) };
1337 return Err(Error::NullPointer);
1338 }
1339
1340 let bytes = unsafe {
1342 std::slice::from_raw_parts(buffer_start.cast::<u8>(), buffer_size)
1343 };
1344 let string = std::str::from_utf8(bytes)
1345 .map_err(|_| Error::Unknown("Invalid UTF-8 in string".to_string()))?
1346 .to_owned();
1347
1348 unsafe { sys::nix_realised_string_free(realised_str) };
1349
1350 Ok(string)
1351 }
1352
1353 pub fn as_string_with_context(
1363 &self,
1364 ) -> Result<(String, Vec<store::StorePath>)> {
1365 if self.value_type() != ValueType::String {
1366 return Err(Error::InvalidType {
1367 expected: "string",
1368 actual: self.value_type().to_string(),
1369 });
1370 }
1371
1372 let realised_str = unsafe {
1374 sys::nix_string_realise(
1375 self.state.context.as_ptr(),
1376 self.state.as_ptr(),
1377 self.inner.as_ptr(),
1378 false,
1379 )
1380 };
1381
1382 if realised_str.is_null() {
1383 return Err(Error::NullPointer);
1384 }
1385
1386 let buffer_start =
1388 unsafe { sys::nix_realised_string_get_buffer_start(realised_str) };
1389 let buffer_size =
1390 unsafe { sys::nix_realised_string_get_buffer_size(realised_str) };
1391
1392 if buffer_start.is_null() {
1393 unsafe { sys::nix_realised_string_free(realised_str) };
1394 return Err(Error::NullPointer);
1395 }
1396
1397 let bytes = unsafe {
1398 std::slice::from_raw_parts(buffer_start.cast::<u8>(), buffer_size)
1399 };
1400 let string = match std::str::from_utf8(bytes) {
1401 Ok(s) => s.to_owned(),
1402 Err(_) => {
1403 unsafe { sys::nix_realised_string_free(realised_str) };
1404 return Err(Error::Unknown("Invalid UTF-8 in string".to_string()));
1405 },
1406 };
1407
1408 let count =
1410 unsafe { sys::nix_realised_string_get_store_path_count(realised_str) };
1411 let mut paths = Vec::with_capacity(count);
1412 for i in 0..count {
1413 let raw =
1415 unsafe { sys::nix_realised_string_get_store_path(realised_str, i) };
1416 if raw.is_null() {
1417 continue;
1418 }
1419 let cloned =
1421 unsafe { sys::nix_store_path_clone(raw as *mut sys::StorePath) };
1422 if let Some(inner) = std::ptr::NonNull::new(cloned) {
1423 paths.push(store::StorePath {
1424 inner,
1425 _context: Arc::clone(&self.state.context),
1426 });
1427 }
1428 }
1429
1430 unsafe { sys::nix_realised_string_free(realised_str) };
1431
1432 Ok((string, paths))
1433 }
1434
1435 pub fn as_path(&self) -> Result<std::path::PathBuf> {
1441 if self.value_type() != ValueType::Path {
1442 return Err(Error::InvalidType {
1443 expected: "path",
1444 actual: self.value_type().to_string(),
1445 });
1446 }
1447
1448 let path_ptr = unsafe {
1450 sys::nix_get_path_string(self.state.context.as_ptr(), self.inner.as_ptr())
1451 };
1452
1453 if path_ptr.is_null() {
1454 return Err(Error::NullPointer);
1455 }
1456
1457 #[cfg(unix)]
1461 {
1462 use std::os::unix::ffi::OsStrExt as _;
1463 let bytes = unsafe { CStr::from_ptr(path_ptr) }.to_bytes();
1464 Ok(std::path::PathBuf::from(std::ffi::OsStr::from_bytes(bytes)))
1465 }
1466 #[cfg(not(unix))]
1467 {
1468 let path_str =
1469 unsafe { CStr::from_ptr(path_ptr).to_string_lossy().into_owned() };
1470 Ok(std::path::PathBuf::from(path_str))
1471 }
1472 }
1473
1474 pub fn call(&self, arg: &Value<'_>) -> Result<Value<'_>> {
1480 let result = self.state.alloc_value()?;
1481 unsafe {
1483 check_err(
1484 self.state.context.as_ptr(),
1485 sys::nix_value_call(
1486 self.state.context.as_ptr(),
1487 self.state.as_ptr(),
1488 self.inner.as_ptr(),
1489 arg.inner.as_ptr(),
1490 result.inner.as_ptr(),
1491 ),
1492 )?;
1493 }
1494 Ok(result)
1495 }
1496
1497 pub fn call_multi(&self, args: &[&Value<'_>]) -> Result<Value<'_>> {
1503 let result = self.state.alloc_value()?;
1504 let mut arg_ptrs: Vec<*mut sys::nix_value> =
1505 args.iter().map(|a| a.inner.as_ptr()).collect();
1506 unsafe {
1508 check_err(
1509 self.state.context.as_ptr(),
1510 sys::nix_value_call_multi(
1511 self.state.context.as_ptr(),
1512 self.state.as_ptr(),
1513 self.inner.as_ptr(),
1514 arg_ptrs.len(),
1515 arg_ptrs.as_mut_ptr(),
1516 result.inner.as_ptr(),
1517 ),
1518 )?;
1519 }
1520 Ok(result)
1521 }
1522
1523 pub fn make_thunk<'a>(
1533 fn_val: &'a Value<'a>,
1534 arg: &'a Value<'a>,
1535 ) -> Result<Value<'a>> {
1536 let result = fn_val.state.alloc_value()?;
1537 unsafe {
1539 check_err(
1540 fn_val.state.context.as_ptr(),
1541 sys::nix_init_apply(
1542 fn_val.state.context.as_ptr(),
1543 result.inner.as_ptr(),
1544 fn_val.inner.as_ptr(),
1545 arg.inner.as_ptr(),
1546 ),
1547 )?;
1548 }
1549 Ok(result)
1550 }
1551
1552 pub fn copy(&self) -> Result<Value<'_>> {
1558 let result = self.state.alloc_value()?;
1559 unsafe {
1561 check_err(
1562 self.state.context.as_ptr(),
1563 sys::nix_copy_value(
1564 self.state.context.as_ptr(),
1565 result.inner.as_ptr(),
1566 self.inner.as_ptr(),
1567 ),
1568 )?;
1569 }
1570 Ok(result)
1571 }
1572
1573 pub fn to_nix_string(&self) -> Result<String> {
1585 match self.value_type() {
1586 ValueType::Int => Ok(self.as_int()?.to_string()),
1587 ValueType::Float => Ok(self.as_float()?.to_string()),
1588 ValueType::Bool => {
1589 Ok(if self.as_bool()? {
1590 "true".to_string()
1591 } else {
1592 "false".to_string()
1593 })
1594 },
1595 ValueType::String => {
1596 Ok(format!("\"{}\"", self.as_string()?.replace('"', "\\\"")))
1597 },
1598 ValueType::Null => Ok("null".to_string()),
1599 ValueType::Attrs => Ok("{ <attrs> }".to_string()),
1600 ValueType::List => Ok("[ <list> ]".to_string()),
1601 ValueType::Function => Ok("<function>".to_string()),
1602 ValueType::Path => {
1603 Ok(
1604 self
1605 .as_path()
1606 .map(|p| p.display().to_string())
1607 .unwrap_or_else(|_| "<path>".to_string()),
1608 )
1609 },
1610 ValueType::Thunk => Ok("<thunk>".to_string()),
1611 ValueType::External => Ok("<external>".to_string()),
1612 }
1613 }
1614}
1615
1616#[cfg(feature = "expr")]
1617impl Drop for Value<'_> {
1618 fn drop(&mut self) {
1619 unsafe {
1622 sys::nix_value_decref(self.state.context.as_ptr(), self.inner.as_ptr());
1623 }
1624 }
1625}
1626
1627#[cfg(feature = "expr")]
1628impl<'a> Clone for Value<'a> {
1629 fn clone(&self) -> Self {
1638 let err = unsafe {
1640 sys::nix_value_incref(self.state.context.as_ptr(), self.inner.as_ptr())
1641 };
1642 assert!(
1643 err == sys::nix_err_NIX_OK,
1644 "nix_value_incref failed with code {err}"
1645 );
1646 Value {
1647 inner: self.inner,
1648 state: self.state,
1649 }
1650 }
1651}
1652
1653#[cfg(feature = "expr")]
1654impl fmt::Display for Value<'_> {
1655 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1656 match self.value_type() {
1657 ValueType::Int => {
1658 if let Ok(val) = self.as_int() {
1659 write!(f, "{val}")
1660 } else {
1661 write!(f, "<int error>")
1662 }
1663 },
1664 ValueType::Float => {
1665 if let Ok(val) = self.as_float() {
1666 write!(f, "{val}")
1667 } else {
1668 write!(f, "<float error>")
1669 }
1670 },
1671 ValueType::Bool => {
1672 if let Ok(val) = self.as_bool() {
1673 write!(f, "{val}")
1674 } else {
1675 write!(f, "<bool error>")
1676 }
1677 },
1678 ValueType::String => {
1679 if let Ok(val) = self.as_string() {
1680 write!(f, "{val}")
1681 } else {
1682 write!(f, "<string error>")
1683 }
1684 },
1685 ValueType::Null => write!(f, "null"),
1686 ValueType::Attrs => write!(f, "{{ <attrs> }}"),
1687 ValueType::List => write!(f, "[ <list> ]"),
1688 ValueType::Function => write!(f, "<function>"),
1689 ValueType::Path => {
1690 if let Ok(p) = self.as_path() {
1691 write!(f, "{}", p.display())
1692 } else {
1693 write!(f, "<path>")
1694 }
1695 },
1696 ValueType::Thunk => write!(f, "<thunk>"),
1697 ValueType::External => write!(f, "<external>"),
1698 }
1699 }
1700}
1701
1702#[cfg(feature = "expr")]
1703impl fmt::Debug for Value<'_> {
1704 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1705 let value_type = self.value_type();
1706 match value_type {
1707 ValueType::Int => {
1708 if let Ok(val) = self.as_int() {
1709 write!(f, "Value::Int({val})")
1710 } else {
1711 write!(f, "Value::Int(<error>)")
1712 }
1713 },
1714 ValueType::Float => {
1715 if let Ok(val) = self.as_float() {
1716 write!(f, "Value::Float({val})")
1717 } else {
1718 write!(f, "Value::Float(<error>)")
1719 }
1720 },
1721 ValueType::Bool => {
1722 if let Ok(val) = self.as_bool() {
1723 write!(f, "Value::Bool({val})")
1724 } else {
1725 write!(f, "Value::Bool(<error>)")
1726 }
1727 },
1728 ValueType::String => {
1729 if let Ok(val) = self.as_string() {
1730 write!(f, "Value::String({val:?})")
1731 } else {
1732 write!(f, "Value::String(<error>)")
1733 }
1734 },
1735 ValueType::Null => write!(f, "Value::Null"),
1736 ValueType::Attrs => write!(f, "Value::Attrs({{ <attrs> }})"),
1737 ValueType::List => write!(f, "Value::List([ <list> ])"),
1738 ValueType::Function => write!(f, "Value::Function(<function>)"),
1739 ValueType::Path => {
1740 if let Ok(p) = self.as_path() {
1741 write!(f, "Value::Path({})", p.display())
1742 } else {
1743 write!(f, "Value::Path(<path>)")
1744 }
1745 },
1746 ValueType::Thunk => write!(f, "Value::Thunk(<thunk>)"),
1747 ValueType::External => write!(f, "Value::External(<external>)"),
1748 }
1749 }
1750}
1751
1752#[cfg(all(test, any(feature = "store", feature = "expr")))]
1753mod tests {
1754 use super::*;
1755
1756 #[cfg(feature = "store")]
1757 #[test]
1758 #[serial]
1759 fn test_context_creation() {
1760 let _ctx = Context::new().expect("Failed to create context");
1761 }
1763
1764 #[cfg(feature = "store")]
1765 #[test]
1766 #[serial]
1767 fn test_nix_version() {
1768 let version = nix_version();
1769 assert!(!version.is_empty(), "Version should not be empty");
1770 }
1771
1772 #[cfg(feature = "expr")]
1773 #[test]
1774 #[serial]
1775 fn test_eval_state_builder() {
1776 let ctx = Arc::new(Context::new().expect("Failed to create context"));
1777 let store =
1778 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
1779 let _state = EvalStateBuilder::new(&store)
1780 .expect("Failed to create builder")
1781 .build()
1782 .expect("Failed to build state");
1783 }
1785
1786 #[cfg(feature = "expr")]
1787 #[test]
1788 #[serial]
1789 fn test_simple_evaluation() {
1790 let ctx = Arc::new(Context::new().expect("Failed to create context"));
1791 let store =
1792 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
1793 let state = EvalStateBuilder::new(&store)
1794 .expect("Failed to create builder")
1795 .build()
1796 .expect("Failed to build state");
1797
1798 let result = state
1799 .eval_from_string("1 + 2", "<eval>")
1800 .expect("Failed to evaluate expression");
1801
1802 assert_eq!(result.value_type(), ValueType::Int);
1803 assert_eq!(result.as_int().expect("Failed to get int value"), 3);
1804 }
1805
1806 #[cfg(feature = "expr")]
1807 #[test]
1808 #[serial]
1809 fn test_value_types() {
1810 let ctx = Arc::new(Context::new().expect("Failed to create context"));
1811 let store =
1812 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
1813 let state = EvalStateBuilder::new(&store)
1814 .expect("Failed to create builder")
1815 .build()
1816 .expect("Failed to build state");
1817
1818 let int_val = state
1820 .eval_from_string("42", "<eval>")
1821 .expect("Failed to evaluate int");
1822 assert_eq!(int_val.value_type(), ValueType::Int);
1823 assert_eq!(int_val.as_int().expect("Failed to get int"), 42);
1824
1825 let bool_val = state
1827 .eval_from_string("true", "<eval>")
1828 .expect("Failed to evaluate bool");
1829 assert_eq!(bool_val.value_type(), ValueType::Bool);
1830 assert!(bool_val.as_bool().expect("Failed to get bool"));
1831
1832 let str_val = state
1834 .eval_from_string("\"hello\"", "<eval>")
1835 .expect("Failed to evaluate string");
1836 assert_eq!(str_val.value_type(), ValueType::String);
1837 assert_eq!(str_val.as_string().expect("Failed to get string"), "hello");
1838 }
1839
1840 #[cfg(feature = "expr")]
1841 #[test]
1842 #[serial]
1843 fn test_value_construction() {
1844 let ctx = Arc::new(Context::new().expect("Failed to create context"));
1845 let store =
1846 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
1847 let state = EvalStateBuilder::new(&store)
1848 .expect("Failed to create builder")
1849 .build()
1850 .expect("Failed to build state");
1851
1852 let int_val = state.make_int(99).expect("Failed to make int");
1853 assert_eq!(int_val.as_int().unwrap(), 99);
1854
1855 let float_val = state.make_float(2.5).expect("Failed to make float");
1856 assert!((float_val.as_float().unwrap() - 2.5).abs() < 1e-9);
1857
1858 let bool_val = state.make_bool(true).expect("Failed to make bool");
1859 assert!(bool_val.as_bool().unwrap());
1860
1861 let null_val = state.make_null().expect("Failed to make null");
1862 assert_eq!(null_val.value_type(), ValueType::Null);
1863
1864 let str_val = state.make_string("hello").expect("Failed to make string");
1865 assert_eq!(str_val.as_string().unwrap(), "hello");
1866 }
1867
1868 #[cfg(feature = "expr")]
1869 #[test]
1870 #[serial]
1871 fn test_make_list() {
1872 let ctx = Arc::new(Context::new().expect("Failed to create context"));
1873 let store =
1874 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
1875 let state = EvalStateBuilder::new(&store)
1876 .expect("Failed to create builder")
1877 .build()
1878 .expect("Failed to build state");
1879
1880 let a = state.make_int(1).unwrap();
1881 let b = state.make_int(2).unwrap();
1882 let c = state.make_int(3).unwrap();
1883
1884 let list = state.make_list(&[&a, &b, &c]).expect("Failed to make list");
1885 assert_eq!(list.value_type(), ValueType::List);
1886 assert_eq!(list.list_len().unwrap(), 3);
1887 }
1888
1889 #[cfg(feature = "expr")]
1890 #[test]
1891 #[serial]
1892 fn test_make_attrs() {
1893 let ctx = Arc::new(Context::new().expect("Failed to create context"));
1894 let store =
1895 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
1896 let state = EvalStateBuilder::new(&store)
1897 .expect("Failed to create builder")
1898 .build()
1899 .expect("Failed to build state");
1900
1901 let a = state.make_int(42).unwrap();
1902 let b = state.make_string("hello").unwrap();
1903
1904 let attrs = state
1905 .make_attrs(&[("answer", &a), ("greeting", &b)])
1906 .expect("Failed to make attrs");
1907 assert_eq!(attrs.value_type(), ValueType::Attrs);
1908
1909 let mut answer = attrs.get_attr("answer").unwrap();
1910 answer.force().unwrap();
1911 assert_eq!(answer.as_int().unwrap(), 42);
1912 }
1913
1914 #[cfg(feature = "expr")]
1915 #[test]
1916 #[serial]
1917 fn test_value_call() {
1918 let ctx = Arc::new(Context::new().expect("Failed to create context"));
1919 let store =
1920 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
1921 let state = EvalStateBuilder::new(&store)
1922 .expect("Failed to create builder")
1923 .build()
1924 .expect("Failed to build state");
1925
1926 let f = state
1927 .eval_from_string("x: x + 1", "<eval>")
1928 .expect("Failed to evaluate function");
1929 let arg = state.make_int(41).unwrap();
1930 let result = f.call(&arg).expect("Failed to call function");
1931 assert_eq!(result.as_int().unwrap(), 42);
1932 }
1933
1934 #[cfg(feature = "expr")]
1935 #[test]
1936 #[serial]
1937 fn test_value_copy() {
1938 let ctx = Arc::new(Context::new().expect("Failed to create context"));
1939 let store =
1940 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
1941 let state = EvalStateBuilder::new(&store)
1942 .expect("Failed to create builder")
1943 .build()
1944 .expect("Failed to build state");
1945
1946 let orig = state.make_int(7).unwrap();
1947 let copy = orig.copy().expect("Failed to copy value");
1948 assert_eq!(copy.as_int().unwrap(), 7);
1949 }
1950
1951 #[cfg(feature = "expr")]
1952 #[test]
1953 #[serial]
1954 fn test_as_string_with_context_plain() {
1955 let ctx = Arc::new(Context::new().expect("Failed to create context"));
1956 let store =
1957 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
1958 let state = EvalStateBuilder::new(&store)
1959 .expect("Failed to create builder")
1960 .build()
1961 .expect("Failed to build state");
1962
1963 let val = state
1964 .eval_from_string("\"hello\"", "<eval>")
1965 .expect("Failed to evaluate string");
1966 let (s, ctx_paths) = val
1967 .as_string_with_context()
1968 .expect("as_string_with_context failed");
1969 assert_eq!(s, "hello");
1970 assert!(
1971 ctx_paths.is_empty(),
1972 "Plain string should have no context paths"
1973 );
1974 }
1975
1976 #[cfg(feature = "expr")]
1977 #[test]
1978 #[serial]
1979 fn test_eval_from_file() {
1980 use std::io::Write as _;
1981 let ctx = Arc::new(Context::new().expect("Failed to create context"));
1982 let store =
1983 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
1984 let state = EvalStateBuilder::new(&store)
1985 .expect("Failed to create builder")
1986 .build()
1987 .expect("Failed to build state");
1988
1989 let mut tmp =
1990 tempfile::NamedTempFile::new().expect("Failed to create temp file");
1991 write!(tmp, "1 + 1").expect("Failed to write temp file");
1992 let result = state
1993 .eval_from_file(tmp.path())
1994 .expect("eval_from_file failed");
1995 assert_eq!(result.as_int().unwrap(), 2);
1996 }
1997
1998 #[cfg(feature = "expr")]
1999 #[test]
2000 #[serial]
2001 fn test_no_load_config() {
2002 let ctx = Arc::new(Context::new().expect("Failed to create context"));
2003 let store =
2004 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
2005 let state = EvalStateBuilder::new(&store)
2006 .expect("Failed to create builder")
2007 .no_load_config()
2008 .build()
2009 .expect("Failed to build state with no_load_config");
2010 let val = state
2011 .eval_from_string("1 + 1", "<eval>")
2012 .expect("Evaluation failed");
2013 assert_eq!(val.as_int().unwrap(), 2);
2014 }
2015}