tvix_eval/value/
attrs.rs

1//! This module implements Nix attribute sets, backed by Rust hash maps.
2use std::borrow::Borrow;
3use std::collections::hash_map;
4use std::hash::Hash;
5use std::iter::FromIterator;
6use std::rc::Rc;
7
8use itertools::Itertools as _;
9use rustc_hash::FxHashMap;
10use serde::de::{Deserializer, Error, Visitor};
11use serde::Deserialize;
12
13use super::string::NixString;
14use super::thunk::ThunkSet;
15use super::TotalDisplay;
16use super::Value;
17use crate::errors::ErrorKind;
18use crate::CatchableErrorKind;
19
20#[cfg(test)]
21mod tests;
22
23type AttrsRep = FxHashMap<NixString, Value>;
24
25#[repr(transparent)]
26#[derive(Clone, Debug, Default)]
27pub struct NixAttrs(Rc<AttrsRep>);
28
29impl From<AttrsRep> for NixAttrs {
30    fn from(rep: AttrsRep) -> Self {
31        NixAttrs(Rc::new(rep))
32    }
33}
34
35impl<K, V> FromIterator<(K, V)> for NixAttrs
36where
37    NixString: From<K>,
38    Value: From<V>,
39{
40    fn from_iter<T>(iter: T) -> NixAttrs
41    where
42        T: IntoIterator<Item = (K, V)>,
43    {
44        iter.into_iter()
45            .map(|(k, v)| (k.into(), v.into()))
46            .collect::<AttrsRep>()
47            .into()
48    }
49}
50
51impl TotalDisplay for NixAttrs {
52    fn total_fmt(&self, f: &mut std::fmt::Formatter<'_>, set: &mut ThunkSet) -> std::fmt::Result {
53        if let Some(Value::String(s)) = self.select_str("type") {
54            if *s == "derivation" {
55                write!(f, "«derivation ")?;
56                if let Some(p) = self.select_str("drvPath") {
57                    p.total_fmt(f, set)?;
58                } else {
59                    write!(f, "???")?;
60                }
61                return write!(f, "»");
62            }
63        }
64
65        f.write_str("{ ")?;
66
67        for (name, value) in self.iter_sorted() {
68            write!(f, "{} = ", name.ident_str())?;
69            value.total_fmt(f, set)?;
70            f.write_str("; ")?;
71        }
72
73        f.write_str("}")
74    }
75}
76
77impl<'de> Deserialize<'de> for NixAttrs {
78    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
79    where
80        D: Deserializer<'de>,
81    {
82        struct MapVisitor;
83
84        impl<'de> Visitor<'de> for MapVisitor {
85            type Value = NixAttrs;
86
87            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
88                formatter.write_str("a valid Nix attribute set")
89            }
90
91            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
92            where
93                A: serde::de::MapAccess<'de>,
94            {
95                let mut stack_array = Vec::with_capacity(map.size_hint().unwrap_or(0) * 2);
96
97                while let Some((key, value)) = map.next_entry()? {
98                    stack_array.push(key);
99                    stack_array.push(value);
100                }
101
102                Ok(NixAttrs::construct(stack_array.len() / 2, stack_array)
103                    .map_err(A::Error::custom)?
104                    .expect("Catchable values are unreachable here"))
105            }
106        }
107
108        deserializer.deserialize_map(MapVisitor)
109    }
110}
111
112impl NixAttrs {
113    pub fn empty() -> Self {
114        AttrsRep::default().into()
115    }
116
117    /// Compare two attribute sets by pointer equality, but returning `false`
118    /// does not mean that the two attribute sets do not have equal content.
119    pub fn ptr_eq(&self, other: &Self) -> bool {
120        Rc::ptr_eq(&self.0, &other.0)
121    }
122
123    /// Return an attribute set containing the merge of the two
124    /// provided sets. Keys from the `other` set have precedence.
125    pub fn update(self, other: Self) -> Self {
126        let mut out = Rc::unwrap_or_clone(self.0);
127        for (key, value) in other {
128            out.insert(key, value);
129        }
130
131        out.into()
132    }
133
134    /// Return the number of key-value entries in an attrset.
135    pub fn len(&self) -> usize {
136        self.0.len()
137    }
138
139    pub fn is_empty(&self) -> bool {
140        self.0.is_empty()
141    }
142
143    /// Select a value from an attribute set by key.
144    pub fn select<Q>(&self, key: &Q) -> Option<&Value>
145    where
146        NixString: Borrow<Q>,
147        Q: Hash + Eq + ?Sized,
148    {
149        self.0.get(key)
150    }
151
152    /// Select a value from an attribute set by a key in string format. This is
153    /// separated out to avoid unintended copies, as the NixString
154    /// representation is not guaranteed to be valid UTF-8 and doesn't fit the
155    /// usual `Borrow` trick.
156    pub fn select_str(&self, key: &str) -> Option<&Value> {
157        self.select(key.as_bytes())
158    }
159
160    /// Select a required value from an attribute set by key, return
161    /// an `AttributeNotFound` error if it is missing.
162    pub fn select_required(&self, key: &str) -> Result<&Value, ErrorKind> {
163        self.0
164            .get(key.as_bytes())
165            .ok_or_else(|| ErrorKind::AttributeNotFound {
166                name: key.to_string(),
167            })
168    }
169
170    pub fn contains<Q>(&self, key: &Q) -> bool
171    where
172        NixString: Borrow<Q>,
173        Q: Hash + Eq + ?Sized,
174    {
175        self.0.contains_key(key)
176    }
177
178    /// Construct an iterator over all the key-value pairs in the attribute set.
179    #[allow(clippy::needless_lifetimes)]
180    pub fn iter<'a>(&'a self) -> Iter<KeyValue<'a>> {
181        Iter(KeyValue::Map(self.0.iter()))
182    }
183
184    /// Construct an iterator over all the key-value pairs in lexicographic
185    /// order of their keys.
186    pub fn iter_sorted(&self) -> Iter<KeyValue<'_>> {
187        let sorted = self.0.iter().sorted_by_key(|x| x.0);
188        Iter(KeyValue::Sorted(sorted))
189    }
190
191    /// Same as [IntoIterator::into_iter], but marks call sites which rely on the
192    /// iteration being lexicographic.
193    pub fn into_iter_sorted(self) -> OwnedAttrsIterator {
194        OwnedAttrsIterator(IntoIterRepr::Finite(
195            self.0
196                .as_ref()
197                .iter()
198                .map(|(k, v)| (k.clone(), v.clone()))
199                .sorted_by(|(a, _), (b, _)| a.cmp(b)),
200        ))
201    }
202
203    /// Construct an iterator over all the keys of the attribute set
204    pub fn keys(&self) -> Keys<'_> {
205        Keys(KeysInner::Map(self.0.keys()))
206    }
207
208    /// Same as [Self::keys], but marks call sites which rely on the
209    /// iteration being lexicographic.
210    pub fn keys_sorted(&self) -> Keys<'_> {
211        Keys(KeysInner::Sorted(self.0.keys().sorted()))
212    }
213
214    /// Implement construction logic of an attribute set, to encapsulate
215    /// logic about attribute set optimisations inside of this module.
216    pub fn construct(
217        count: usize,
218        mut stack_slice: Vec<Value>,
219    ) -> Result<Result<Self, CatchableErrorKind>, ErrorKind> {
220        debug_assert!(
221            stack_slice.len() == count * 2,
222            "construct_attrs called with count == {}, but slice.len() == {}",
223            count,
224            stack_slice.len(),
225        );
226
227        let mut attrs_map = FxHashMap::with_capacity_and_hasher(count, rustc_hash::FxBuildHasher);
228
229        for _ in 0..count {
230            let value = stack_slice.pop().unwrap();
231            let key = stack_slice.pop().unwrap();
232
233            match key {
234                Value::String(ks) => set_attr(&mut attrs_map, ks, value)?,
235
236                Value::Null => {
237                    // This is in fact valid, but leads to the value being
238                    // ignored and nothing being set, i.e. `{ ${null} = 1; } =>
239                    // { }`.
240                    continue;
241                }
242
243                Value::Catchable(err) => return Ok(Err(*err)),
244
245                other => return Err(ErrorKind::InvalidAttributeName(other)),
246            }
247        }
248
249        Ok(Ok(attrs_map.into()))
250    }
251
252    /// Calculate the intersection of the attribute sets.
253    /// The right side value is used when the keys match.
254    pub(crate) fn intersect(&self, other: &Self) -> NixAttrs {
255        let lhs = &self.0;
256        let rhs = &other.0;
257
258        let mut out = FxHashMap::with_capacity_and_hasher(
259            std::cmp::min(lhs.len(), rhs.len()),
260            rustc_hash::FxBuildHasher,
261        );
262
263        if lhs.len() < rhs.len() {
264            for key in lhs.keys() {
265                if let Some(val) = rhs.get(key) {
266                    out.insert(key.clone(), val.clone());
267                }
268            }
269        } else {
270            for (key, val) in rhs.iter() {
271                if lhs.contains_key(key) {
272                    out.insert(key.clone(), val.clone());
273                }
274            }
275        };
276
277        out.into()
278    }
279}
280
281impl IntoIterator for NixAttrs {
282    type Item = (NixString, Value);
283    type IntoIter = OwnedAttrsIterator;
284
285    fn into_iter(self) -> Self::IntoIter {
286        OwnedAttrsIterator(IntoIterRepr::Map(Rc::unwrap_or_clone(self.0).into_iter()))
287    }
288}
289
290/// Set an attribute on an in-construction attribute set, while
291/// checking against duplicate keys.
292fn set_attr(map: &mut AttrsRep, key: NixString, value: Value) -> Result<(), ErrorKind> {
293    match map.entry(key) {
294        hash_map::Entry::Occupied(entry) => Err(ErrorKind::DuplicateAttrsKey {
295            key: entry.key().to_string(),
296        }),
297
298        hash_map::Entry::Vacant(entry) => {
299            entry.insert(value);
300            Ok(())
301        }
302    }
303}
304
305/// Iterator representation over the keys *and* values of an attribute
306/// set.
307pub enum KeyValue<'a> {
308    Map(hash_map::Iter<'a, NixString, Value>),
309    Sorted(std::vec::IntoIter<(&'a NixString, &'a Value)>),
310}
311
312/// Iterator over a Nix attribute set.
313// This wrapper type exists to make the inner "raw" iterator
314// inaccessible.
315#[repr(transparent)]
316pub struct Iter<T>(T);
317
318impl<'a> Iterator for Iter<KeyValue<'a>> {
319    type Item = (&'a NixString, &'a Value);
320
321    fn next(&mut self) -> Option<Self::Item> {
322        match &mut self.0 {
323            KeyValue::Map(inner) => inner.next(),
324            KeyValue::Sorted(inner) => inner.next(),
325        }
326    }
327}
328
329impl ExactSizeIterator for Iter<KeyValue<'_>> {
330    fn len(&self) -> usize {
331        match &self.0 {
332            KeyValue::Map(inner) => inner.len(),
333            KeyValue::Sorted(inner) => inner.len(),
334        }
335    }
336}
337
338enum KeysInner<'a> {
339    Map(hash_map::Keys<'a, NixString, Value>),
340    Sorted(std::vec::IntoIter<&'a NixString>),
341}
342
343pub struct Keys<'a>(KeysInner<'a>);
344
345impl<'a> Iterator for Keys<'a> {
346    type Item = &'a NixString;
347
348    fn next(&mut self) -> Option<Self::Item> {
349        match &mut self.0 {
350            KeysInner::Map(m) => m.next(),
351            KeysInner::Sorted(v) => v.next(),
352        }
353    }
354}
355
356impl<'a> IntoIterator for &'a NixAttrs {
357    type Item = (&'a NixString, &'a Value);
358
359    type IntoIter = Iter<KeyValue<'a>>;
360
361    fn into_iter(self) -> Self::IntoIter {
362        self.iter()
363    }
364}
365
366impl ExactSizeIterator for Keys<'_> {
367    fn len(&self) -> usize {
368        match &self.0 {
369            KeysInner::Map(m) => m.len(),
370            KeysInner::Sorted(v) => v.len(),
371        }
372    }
373}
374
375/// Internal representation of an owning attrset iterator
376pub enum IntoIterRepr {
377    Finite(std::vec::IntoIter<(NixString, Value)>),
378    Map(hash_map::IntoIter<NixString, Value>),
379}
380
381/// Wrapper type which hides the internal implementation details from
382/// users.
383#[repr(transparent)]
384pub struct OwnedAttrsIterator(IntoIterRepr);
385
386impl Iterator for OwnedAttrsIterator {
387    type Item = (NixString, Value);
388
389    fn next(&mut self) -> Option<Self::Item> {
390        match &mut self.0 {
391            IntoIterRepr::Finite(inner) => inner.next(),
392            IntoIterRepr::Map(m) => m.next(),
393        }
394    }
395}
396
397impl ExactSizeIterator for OwnedAttrsIterator {
398    fn len(&self) -> usize {
399        match &self.0 {
400            IntoIterRepr::Finite(inner) => inner.len(),
401            IntoIterRepr::Map(inner) => inner.len(),
402        }
403    }
404}
405
406impl DoubleEndedIterator for OwnedAttrsIterator {
407    fn next_back(&mut self) -> Option<Self::Item> {
408        match &mut self.0 {
409            IntoIterRepr::Finite(inner) => inner.next_back(),
410            // hashmaps have arbitary iteration order, so reversing it would not make a difference
411            IntoIterRepr::Map(inner) => inner.next(),
412        }
413    }
414}