nix_bindings_expr/value.rs
1pub mod __private;
2
3use nix_bindings_expr_sys as raw;
4use nix_bindings_util::{check_call, context::Context};
5use std::ptr::{null_mut, NonNull};
6
7// TODO: test: cloning a thunk does not duplicate the evaluation.
8
9pub type Int = i64;
10
11/// The type discriminator of a [`Value`] that has successfully evaluated to at least [weak head normal form](https://nix.dev/manual/nix/latest/language/evaluation.html?highlight=WHNF#values).
12///
13/// Typically acquired with [`EvalState::value_type`][`crate::eval_state::EvalState::value_type`]
14#[derive(Eq, PartialEq, Debug)]
15pub enum ValueType {
16 /// A Nix [attribute set](https://nix.dev/manual/nix/stable/language/types.html#type-attrs)
17 AttrSet,
18 /// A Nix [boolean](https://nix.dev/manual/nix/stable/language/types.html#type-bool)
19 Bool,
20 /// A Nix external value (mostly-opaque value for plugins, linked applications)
21 External,
22 /// A Nix [float](https://nix.dev/manual/nix/stable/language/types.html#type-float)
23 Float,
24 /// A Nix [function](https://nix.dev/manual/nix/stable/language/types.html#type-function)
25 Function,
26 /// A Nix [integer](https://nix.dev/manual/nix/stable/language/types.html#type-int)
27 Int,
28 /// A Nix [list](https://nix.dev/manual/nix/stable/language/types.html#type-list)
29 List,
30 /// A Nix [`null`](https://nix.dev/manual/nix/stable/language/types.html#type-null)
31 Null,
32 /// A Nix [path value](https://nix.dev/manual/nix/stable/language/types.html#type-path)
33 Path,
34 /// A Nix [string](https://nix.dev/manual/nix/stable/language/types.html#type-string)
35 String,
36 /// An unknown value, presumably from a new, partially unsupported version of Nix
37 Unknown,
38}
39
40impl ValueType {
41 /// Convert a raw value type to a [`ValueType`].
42 ///
43 /// Return [`None`] if the Value is still a thunk (i.e. not yet evaluated).
44 ///
45 /// Return `Some(`[`ValueType::Unknown`]`)` if the value type is not recognized.
46 pub(crate) fn from_raw(raw: raw::ValueType) -> Option<ValueType> {
47 match raw {
48 raw::ValueType_NIX_TYPE_ATTRS => Some(ValueType::AttrSet),
49 raw::ValueType_NIX_TYPE_BOOL => Some(ValueType::Bool),
50 raw::ValueType_NIX_TYPE_EXTERNAL => Some(ValueType::External),
51 raw::ValueType_NIX_TYPE_FLOAT => Some(ValueType::Float),
52 raw::ValueType_NIX_TYPE_FUNCTION => Some(ValueType::Function),
53 raw::ValueType_NIX_TYPE_INT => Some(ValueType::Int),
54 raw::ValueType_NIX_TYPE_LIST => Some(ValueType::List),
55 raw::ValueType_NIX_TYPE_NULL => Some(ValueType::Null),
56 raw::ValueType_NIX_TYPE_PATH => Some(ValueType::Path),
57 raw::ValueType_NIX_TYPE_STRING => Some(ValueType::String),
58
59 raw::ValueType_NIX_TYPE_THUNK => None,
60
61 // This would happen if a new type of value is added in Nix.
62 _ => Some(ValueType::Unknown),
63 }
64 }
65}
66
67/// A pointer to a [value](https://nix.dev/manual/nix/latest/language/types.html) or [thunk](https://nix.dev/manual/nix/2.31/language/evaluation.html?highlight=thunk#laziness), to be used with [`EvalState`][`crate::eval_state::EvalState`] methods.
68///
69/// # Shared Evaluation State
70///
71/// Multiple `Value` instances can reference the same underlying Nix value.
72/// This occurs when a `Value` is [cloned](Clone), or when multiple Nix
73/// expressions reference the same binding.
74///
75/// When any reference to a thunk is evaluated—whether through
76/// [`force`](crate::eval_state::EvalState::force), other `EvalState` methods,
77/// or indirectly as a consequence of evaluating something else—all references
78/// observe the evaluated result. This means
79/// [`value_type_unforced`](crate::eval_state::EvalState::value_type_unforced)
80/// can return `None` (thunk) initially but a specific type later, even without
81/// directly operating on that `Value`. The state will not regress back to a
82/// less determined state.
83pub struct Value {
84 inner: NonNull<raw::Value>,
85}
86impl Value {
87 /// Take ownership of a new [`Value`].
88 ///
89 /// This does not call [`nix_bindings_util_sys::gc_incref`], but does call [`nix_bindings_util_sys::gc_decref`] when [dropped][`Drop`].
90 ///
91 /// # Safety
92 ///
93 /// The caller must ensure that the provided `inner` has a positive reference count, and that `inner` is not used after the returned `Value` is dropped.
94 pub(crate) unsafe fn new(inner: *mut raw::Value) -> Self {
95 Value {
96 inner: NonNull::new(inner).unwrap(),
97 }
98 }
99
100 /// Borrow a reference to a [`Value`].
101 ///
102 /// This calls [`nix_bindings_util_sys::value_incref`], and the returned Value will call [`nix_bindings_util_sys::value_decref`] when dropped.
103 ///
104 /// # Safety
105 ///
106 /// The caller must ensure that the provided `inner` has a positive reference count.
107 pub(crate) unsafe fn new_borrowed(inner: *mut raw::Value) -> Self {
108 let v = Value::new(inner);
109 unsafe { raw::value_incref(null_mut(), inner) };
110 v
111 }
112
113 /// # Safety
114 ///
115 /// The caller must ensure that the returned pointer is not used after the `Value` is dropped.
116 pub(crate) unsafe fn raw_ptr(&self) -> *mut raw::Value {
117 self.inner.as_ptr()
118 }
119}
120impl Drop for Value {
121 fn drop(&mut self) {
122 unsafe {
123 // ignoring error because the only failure mode is leaking memory
124 raw::value_decref(null_mut(), self.inner.as_ptr());
125 }
126 }
127}
128impl Clone for Value {
129 fn clone(&self) -> Self {
130 // TODO: Is it worth allocating a new Context here? Ideally cloning is cheap.
131 // this is very unlikely to error, and it is not recoverable
132 // Maybe try without, and try again with context to report details?
133 unsafe {
134 check_call!(raw::value_incref(&mut Context::new(), self.inner.as_ptr())).unwrap();
135 }
136 // can't return an error here, but we don't want to ignore the error either as it means we could use-after-free
137 Value { inner: self.inner }
138 }
139}
140
141// Tested in eval_state.rs