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