1#![warn(missing_docs)]
2mod store;
52
53use std::{ffi::CString, fmt, ptr::NonNull, sync::Arc};
54
55#[cfg(test)] use serial_test::serial;
56pub use store::{Store, StorePath};
57
58#[doc(hidden)]
65pub mod sys {
66 pub use nix_bindings_sys::*;
67}
68
69pub type Result<T> = std::result::Result<T, Error>;
71
72#[derive(Debug)]
74pub enum Error {
75 Unknown(String),
77
78 Overflow,
80
81 KeyNotFound(String),
83
84 EvalError(String),
86
87 InvalidType {
89 expected: &'static str,
91 actual: String,
93 },
94 NullPointer,
96
97 StringConversion(std::ffi::NulError),
99}
100
101impl fmt::Display for Error {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 Error::Unknown(msg) => write!(f, "Unknown error: {msg}"),
105 Error::Overflow => write!(f, "Overflow error"),
106 Error::KeyNotFound(key) => write!(f, "Key not found: {key}"),
107 Error::EvalError(msg) => write!(f, "Evaluation error: {msg}"),
108 Error::InvalidType { expected, actual } => {
109 write!(f, "Invalid type: expected {expected}, got {actual}")
110 },
111 Error::NullPointer => write!(f, "Null pointer error"),
112 Error::StringConversion(e) => write!(f, "String conversion error: {e}"),
113 }
114 }
115}
116
117impl std::error::Error for Error {}
118
119impl From<std::ffi::NulError> for Error {
120 fn from(e: std::ffi::NulError) -> Self {
121 Error::StringConversion(e)
122 }
123}
124
125fn check_err(err: sys::nix_err) -> Result<()> {
127 match err {
128 sys::nix_err_NIX_OK => Ok(()),
129 sys::nix_err_NIX_ERR_UNKNOWN => {
130 Err(Error::Unknown("Unknown error".to_string()))
131 },
132 sys::nix_err_NIX_ERR_OVERFLOW => Err(Error::Overflow),
133 sys::nix_err_NIX_ERR_KEY => {
134 Err(Error::KeyNotFound("Key not found".to_string()))
135 },
136 sys::nix_err_NIX_ERR_NIX_ERROR => {
137 Err(Error::EvalError("Evaluation error".to_string()))
138 },
139 _ => Err(Error::Unknown(format!("Error code: {err}"))),
140 }
141}
142
143pub struct Context {
148 inner: NonNull<sys::nix_c_context>,
149}
150
151impl Context {
152 pub fn new() -> Result<Self> {
160 let ctx_ptr = unsafe { sys::nix_c_context_create() };
162 let inner = NonNull::new(ctx_ptr).ok_or(Error::NullPointer)?;
163
164 let ctx = Context { inner };
165
166 unsafe {
168 check_err(sys::nix_libutil_init(ctx.inner.as_ptr()))?;
169 check_err(sys::nix_libstore_init(ctx.inner.as_ptr()))?;
170 check_err(sys::nix_libexpr_init(ctx.inner.as_ptr()))?;
171 }
172
173 Ok(ctx)
174 }
175
176 unsafe fn as_ptr(&self) -> *mut sys::nix_c_context {
182 self.inner.as_ptr()
183 }
184}
185
186impl Drop for Context {
187 fn drop(&mut self) {
188 unsafe {
190 sys::nix_c_context_free(self.inner.as_ptr());
191 }
192 }
193}
194
195unsafe impl Send for Context {}
197unsafe impl Sync for Context {}
198
199pub struct EvalStateBuilder {
204 inner: NonNull<sys::nix_eval_state_builder>,
205 store: Arc<Store>,
206 context: Arc<Context>,
207}
208
209impl EvalStateBuilder {
210 pub fn new(store: &Arc<Store>) -> Result<Self> {
220 let builder_ptr = unsafe {
222 sys::nix_eval_state_builder_new(store._context.as_ptr(), store.as_ptr())
223 };
224
225 let inner = NonNull::new(builder_ptr).ok_or(Error::NullPointer)?;
226
227 Ok(EvalStateBuilder {
228 inner,
229 store: Arc::clone(store),
230 context: Arc::clone(&store._context),
231 })
232 }
233
234 pub fn build(self) -> Result<EvalState> {
240 unsafe {
243 check_err(sys::nix_eval_state_builder_load(
244 self.context.as_ptr(),
245 self.inner.as_ptr(),
246 ))?;
247 }
248
249 let state_ptr = unsafe {
252 sys::nix_eval_state_build(self.context.as_ptr(), self.inner.as_ptr())
253 };
254
255 let inner = NonNull::new(state_ptr).ok_or(Error::NullPointer)?;
256
257 Ok(EvalState {
259 inner,
260 store: self.store.clone(),
261 context: self.context.clone(),
262 })
263 }
264}
265
266impl Drop for EvalStateBuilder {
267 fn drop(&mut self) {
268 unsafe {
270 sys::nix_eval_state_builder_free(self.inner.as_ptr());
271 }
272 }
273}
274
275pub struct EvalState {
280 inner: NonNull<sys::EvalState>,
281 #[allow(dead_code)]
282 store: Arc<Store>,
283 context: Arc<Context>,
284}
285
286impl EvalState {
287 pub fn eval_from_string(&self, expr: &str, path: &str) -> Result<Value<'_>> {
298 let expr_c = CString::new(expr)?;
299 let path_c = CString::new(path)?;
300
301 let value_ptr = unsafe {
304 sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr())
305 };
306 if value_ptr.is_null() {
307 return Err(Error::NullPointer);
308 }
309
310 unsafe {
313 check_err(sys::nix_expr_eval_from_string(
314 self.context.as_ptr(),
315 self.inner.as_ptr(),
316 expr_c.as_ptr(),
317 path_c.as_ptr(),
318 value_ptr,
319 ))?;
320 }
321
322 let inner = NonNull::new(value_ptr).ok_or(Error::NullPointer)?;
323
324 Ok(Value { inner, state: self })
325 }
326
327 pub fn alloc_value(&self) -> Result<Value<'_>> {
333 let value_ptr = unsafe {
335 sys::nix_alloc_value(self.context.as_ptr(), self.inner.as_ptr())
336 };
337 let inner = NonNull::new(value_ptr).ok_or(Error::NullPointer)?;
338
339 Ok(Value { inner, state: self })
340 }
341
342 unsafe fn as_ptr(&self) -> *mut sys::EvalState {
348 self.inner.as_ptr()
349 }
350}
351
352impl Drop for EvalState {
353 fn drop(&mut self) {
354 unsafe {
356 sys::nix_state_free(self.inner.as_ptr());
357 }
358 }
359}
360
361unsafe impl Send for EvalState {}
363unsafe impl Sync for EvalState {}
364
365#[derive(Debug, Clone, Copy, PartialEq, Eq)]
367pub enum ValueType {
368 Thunk,
370 Int,
372 Float,
374 Bool,
376 String,
378 Path,
380 Null,
382 Attrs,
384 List,
386 Function,
388 External,
390}
391
392impl ValueType {
393 fn from_c(value_type: sys::ValueType) -> Self {
394 match value_type {
395 sys::ValueType_NIX_TYPE_THUNK => ValueType::Thunk,
396 sys::ValueType_NIX_TYPE_INT => ValueType::Int,
397 sys::ValueType_NIX_TYPE_FLOAT => ValueType::Float,
398 sys::ValueType_NIX_TYPE_BOOL => ValueType::Bool,
399 sys::ValueType_NIX_TYPE_STRING => ValueType::String,
400 sys::ValueType_NIX_TYPE_PATH => ValueType::Path,
401 sys::ValueType_NIX_TYPE_NULL => ValueType::Null,
402 sys::ValueType_NIX_TYPE_ATTRS => ValueType::Attrs,
403 sys::ValueType_NIX_TYPE_LIST => ValueType::List,
404 sys::ValueType_NIX_TYPE_FUNCTION => ValueType::Function,
405 sys::ValueType_NIX_TYPE_EXTERNAL => ValueType::External,
406 _ => ValueType::Thunk, }
408 }
409}
410
411impl fmt::Display for ValueType {
412 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413 let name = match self {
414 ValueType::Thunk => "thunk",
415 ValueType::Int => "int",
416 ValueType::Float => "float",
417 ValueType::Bool => "bool",
418 ValueType::String => "string",
419 ValueType::Path => "path",
420 ValueType::Null => "null",
421 ValueType::Attrs => "attrs",
422 ValueType::List => "list",
423 ValueType::Function => "function",
424 ValueType::External => "external",
425 };
426 write!(f, "{name}")
427 }
428}
429
430pub struct Value<'a> {
435 inner: NonNull<sys::nix_value>,
436 state: &'a EvalState,
437}
438
439impl Value<'_> {
440 pub fn force(&mut self) -> Result<()> {
448 unsafe {
450 check_err(sys::nix_value_force(
451 self.state.context.as_ptr(),
452 self.state.as_ptr(),
453 self.inner.as_ptr(),
454 ))
455 }
456 }
457
458 pub fn force_deep(&mut self) -> Result<()> {
466 unsafe {
468 check_err(sys::nix_value_force_deep(
469 self.state.context.as_ptr(),
470 self.state.as_ptr(),
471 self.inner.as_ptr(),
472 ))
473 }
474 }
475
476 #[must_use]
478 pub fn value_type(&self) -> ValueType {
479 let c_type = unsafe {
481 sys::nix_get_type(self.state.context.as_ptr(), self.inner.as_ptr())
482 };
483 ValueType::from_c(c_type)
484 }
485
486 pub fn as_int(&self) -> Result<i64> {
492 if self.value_type() != ValueType::Int {
493 return Err(Error::InvalidType {
494 expected: "int",
495 actual: self.value_type().to_string(),
496 });
497 }
498
499 let result = unsafe {
501 sys::nix_get_int(self.state.context.as_ptr(), self.inner.as_ptr())
502 };
503
504 Ok(result)
505 }
506
507 pub fn as_float(&self) -> Result<f64> {
513 if self.value_type() != ValueType::Float {
514 return Err(Error::InvalidType {
515 expected: "float",
516 actual: self.value_type().to_string(),
517 });
518 }
519
520 let result = unsafe {
522 sys::nix_get_float(self.state.context.as_ptr(), self.inner.as_ptr())
523 };
524
525 Ok(result)
526 }
527
528 pub fn as_bool(&self) -> Result<bool> {
534 if self.value_type() != ValueType::Bool {
535 return Err(Error::InvalidType {
536 expected: "bool",
537 actual: self.value_type().to_string(),
538 });
539 }
540
541 let result = unsafe {
543 sys::nix_get_bool(self.state.context.as_ptr(), self.inner.as_ptr())
544 };
545
546 Ok(result)
547 }
548
549 pub fn as_string(&self) -> Result<String> {
555 if self.value_type() != ValueType::String {
556 return Err(Error::InvalidType {
557 expected: "string",
558 actual: self.value_type().to_string(),
559 });
560 }
561
562 let realised_str = unsafe {
565 sys::nix_string_realise(
566 self.state.context.as_ptr(),
567 self.state.as_ptr(),
568 self.inner.as_ptr(),
569 false, )
571 };
572
573 if realised_str.is_null() {
574 return Err(Error::NullPointer);
575 }
576
577 let buffer_start =
579 unsafe { sys::nix_realised_string_get_buffer_start(realised_str) };
580
581 let buffer_size =
582 unsafe { sys::nix_realised_string_get_buffer_size(realised_str) };
583
584 if buffer_start.is_null() {
585 unsafe {
587 sys::nix_realised_string_free(realised_str);
588 }
589 return Err(Error::NullPointer);
590 }
591
592 let bytes = unsafe {
594 std::slice::from_raw_parts(buffer_start.cast::<u8>(), buffer_size)
595 };
596 let string = std::str::from_utf8(bytes)
597 .map_err(|_| Error::Unknown("Invalid UTF-8 in string".to_string()))?
598 .to_owned();
599
600 unsafe {
602 sys::nix_realised_string_free(realised_str);
603 }
604
605 Ok(string)
606 }
607
608 #[allow(dead_code)]
614 unsafe fn as_ptr(&self) -> *mut sys::nix_value {
615 self.inner.as_ptr()
616 }
617
618 pub fn to_nix_string(&self) -> Result<String> {
628 match self.value_type() {
629 ValueType::Int => Ok(self.as_int()?.to_string()),
630 ValueType::Float => Ok(self.as_float()?.to_string()),
631 ValueType::Bool => {
632 Ok(if self.as_bool()? {
633 "true".to_string()
634 } else {
635 "false".to_string()
636 })
637 },
638 ValueType::String => {
639 Ok(format!("\"{}\"", self.as_string()?.replace('"', "\\\"")))
640 },
641 ValueType::Null => Ok("null".to_string()),
642 ValueType::Attrs => Ok("{ <attrs> }".to_string()),
643 ValueType::List => Ok("[ <list> ]".to_string()),
644 ValueType::Function => Ok("<function>".to_string()),
645 ValueType::Path => Ok("<path>".to_string()),
646 ValueType::Thunk => Ok("<thunk>".to_string()),
647 ValueType::External => Ok("<external>".to_string()),
648 }
649 }
650}
651
652impl Drop for Value<'_> {
653 fn drop(&mut self) {
654 unsafe {
656 sys::nix_value_decref(self.state.context.as_ptr(), self.inner.as_ptr());
657 }
658 }
659}
660
661impl fmt::Display for Value<'_> {
662 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
663 match self.value_type() {
664 ValueType::Int => {
665 if let Ok(val) = self.as_int() {
666 write!(f, "{val}")
667 } else {
668 write!(f, "<int error>")
669 }
670 },
671 ValueType::Float => {
672 if let Ok(val) = self.as_float() {
673 write!(f, "{val}")
674 } else {
675 write!(f, "<float error>")
676 }
677 },
678 ValueType::Bool => {
679 if let Ok(val) = self.as_bool() {
680 write!(f, "{val}")
681 } else {
682 write!(f, "<bool error>")
683 }
684 },
685 ValueType::String => {
686 if let Ok(val) = self.as_string() {
687 write!(f, "{val}")
688 } else {
689 write!(f, "<string error>")
690 }
691 },
692 ValueType::Null => write!(f, "null"),
693 ValueType::Attrs => write!(f, "{{ <attrs> }}"),
694 ValueType::List => write!(f, "[ <list> ]"),
695 ValueType::Function => write!(f, "<function>"),
696 ValueType::Path => write!(f, "<path>"),
697 ValueType::Thunk => write!(f, "<thunk>"),
698 ValueType::External => write!(f, "<external>"),
699 }
700 }
701}
702
703impl fmt::Debug for Value<'_> {
704 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
705 let value_type = self.value_type();
706 match value_type {
707 ValueType::Int => {
708 if let Ok(val) = self.as_int() {
709 write!(f, "Value::Int({val})")
710 } else {
711 write!(f, "Value::Int(<error>)")
712 }
713 },
714 ValueType::Float => {
715 if let Ok(val) = self.as_float() {
716 write!(f, "Value::Float({val})")
717 } else {
718 write!(f, "Value::Float(<error>)")
719 }
720 },
721 ValueType::Bool => {
722 if let Ok(val) = self.as_bool() {
723 write!(f, "Value::Bool({val})")
724 } else {
725 write!(f, "Value::Bool(<error>)")
726 }
727 },
728 ValueType::String => {
729 if let Ok(val) = self.as_string() {
730 write!(f, "Value::String({val:?})")
731 } else {
732 write!(f, "Value::String(<error>)")
733 }
734 },
735 ValueType::Null => write!(f, "Value::Null"),
736 ValueType::Attrs => write!(f, "Value::Attrs({{ <attrs> }})"),
737 ValueType::List => write!(f, "Value::List([ <list> ])"),
738 ValueType::Function => write!(f, "Value::Function(<function>)"),
739 ValueType::Path => write!(f, "Value::Path(<path>)"),
740 ValueType::Thunk => write!(f, "Value::Thunk(<thunk>)"),
741 ValueType::External => write!(f, "Value::External(<external>)"),
742 }
743 }
744}
745
746#[cfg(test)]
747mod tests {
748 use super::*;
749
750 #[test]
751 #[serial]
752 fn test_context_creation() {
753 let _ctx = Context::new().expect("Failed to create context");
754 }
756
757 #[test]
758 #[serial]
759 fn test_eval_state_builder() {
760 let ctx = Arc::new(Context::new().expect("Failed to create context"));
761 let store =
762 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
763 let _state = EvalStateBuilder::new(&store)
764 .expect("Failed to create builder")
765 .build()
766 .expect("Failed to build state");
767 }
769
770 #[test]
771 #[serial]
772 fn test_simple_evaluation() {
773 let ctx = Arc::new(Context::new().expect("Failed to create context"));
774 let store =
775 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
776 let state = EvalStateBuilder::new(&store)
777 .expect("Failed to create builder")
778 .build()
779 .expect("Failed to build state");
780
781 let result = state
782 .eval_from_string("1 + 2", "<eval>")
783 .expect("Failed to evaluate expression");
784
785 assert_eq!(result.value_type(), ValueType::Int);
786 assert_eq!(result.as_int().expect("Failed to get int value"), 3);
787 }
788
789 #[test]
790 #[serial]
791 fn test_value_types() {
792 let ctx = Arc::new(Context::new().expect("Failed to create context"));
793 let store =
794 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
795 let state = EvalStateBuilder::new(&store)
796 .expect("Failed to create builder")
797 .build()
798 .expect("Failed to build state");
799
800 let int_val = state
802 .eval_from_string("42", "<eval>")
803 .expect("Failed to evaluate int");
804 assert_eq!(int_val.value_type(), ValueType::Int);
805 assert_eq!(int_val.as_int().expect("Failed to get int"), 42);
806
807 let bool_val = state
809 .eval_from_string("true", "<eval>")
810 .expect("Failed to evaluate bool");
811 assert_eq!(bool_val.value_type(), ValueType::Bool);
812 assert!(bool_val.as_bool().expect("Failed to get bool"));
813
814 let str_val = state
816 .eval_from_string("\"hello\"", "<eval>")
817 .expect("Failed to evaluate string");
818 assert_eq!(str_val.value_type(), ValueType::String);
819 assert_eq!(str_val.as_string().expect("Failed to get string"), "hello");
820 }
821
822 #[test]
823 #[serial]
824 fn test_value_formatting() {
825 let ctx = Arc::new(Context::new().expect("Failed to create context"));
826 let store =
827 Arc::new(Store::open(&ctx, None).expect("Failed to open store"));
828 let state = EvalStateBuilder::new(&store)
829 .expect("Failed to create builder")
830 .build()
831 .expect("Failed to build state");
832
833 let int_val = state
835 .eval_from_string("42", "<eval>")
836 .expect("Failed to evaluate int");
837 assert_eq!(format!("{int_val}"), "42");
838 assert_eq!(format!("{int_val:?}"), "Value::Int(42)");
839 assert_eq!(int_val.to_nix_string().expect("Failed to format"), "42");
840
841 let bool_val = state
843 .eval_from_string("true", "<eval>")
844 .expect("Failed to evaluate bool");
845 assert_eq!(format!("{bool_val}"), "true");
846 assert_eq!(format!("{bool_val:?}"), "Value::Bool(true)");
847 assert_eq!(bool_val.to_nix_string().expect("Failed to format"), "true");
848
849 let false_val = state
850 .eval_from_string("false", "<eval>")
851 .expect("Failed to evaluate bool");
852 assert_eq!(format!("{false_val}"), "false");
853 assert_eq!(
854 false_val.to_nix_string().expect("Failed to format"),
855 "false"
856 );
857
858 let str_val = state
860 .eval_from_string("\"hello world\"", "<eval>")
861 .expect("Failed to evaluate string");
862 assert_eq!(format!("{str_val}"), "hello world");
863 assert_eq!(format!("{str_val:?}"), "Value::String(\"hello world\")");
864 assert_eq!(
865 str_val.to_nix_string().expect("Failed to format"),
866 "\"hello world\""
867 );
868
869 let quoted_str = state
871 .eval_from_string("\"say \\\"hello\\\"\"", "<eval>")
872 .expect("Failed to evaluate quoted string");
873 assert_eq!(format!("{quoted_str}"), "say \"hello\"");
874 assert_eq!(
875 quoted_str.to_nix_string().expect("Failed to format"),
876 "\"say \\\"hello\\\"\""
877 );
878
879 let null_val = state
881 .eval_from_string("null", "<eval>")
882 .expect("Failed to evaluate null");
883 assert_eq!(format!("{null_val}"), "null");
884 assert_eq!(format!("{null_val:?}"), "Value::Null");
885 assert_eq!(null_val.to_nix_string().expect("Failed to format"), "null");
886
887 let attrs_val = state
889 .eval_from_string("{ a = 1; }", "<eval>")
890 .expect("Failed to evaluate attrs");
891 assert_eq!(format!("{attrs_val}"), "{ <attrs> }");
892 assert_eq!(format!("{attrs_val:?}"), "Value::Attrs({ <attrs> })");
893
894 let list_val = state
895 .eval_from_string("[ 1 2 3 ]", "<eval>")
896 .expect("Failed to evaluate list");
897 assert_eq!(format!("{list_val}"), "[ <list> ]");
898 assert_eq!(format!("{list_val:?}"), "Value::List([ <list> ])");
899 }
900}