Skip to main content

snix_eval/value/
attrs.rs

1//! This module implements Nix attribute sets. They have flexible
2//! backing implementations, as they are used in very versatile
3//! use-cases that are all exposed the same way in the language
4//! surface.
5//!
6//! Due to this, construction and management of attribute sets has
7//! some peculiarities that are encapsulated within this module.
8use std::borrow::Borrow;
9use std::collections::{BTreeMap, hash_map};
10use std::iter::FromIterator;
11use std::rc::Rc;
12use std::sync::LazyLock;
13
14use bstr::{BStr, ByteSlice};
15use codemap::Span;
16use itertools::Itertools as _;
17use rustc_hash::FxHashMap;
18use serde::Deserialize;
19use serde::de::{Deserializer, Error, Visitor};
20
21use super::TotalDisplay;
22use super::Value;
23use super::string::NixString;
24use super::thunk::ThunkSet;
25use crate::CatchableErrorKind;
26use crate::errors::ErrorKind;
27
28static NAME: LazyLock<NixString> = LazyLock::new(|| "name".into());
29static VALUE: LazyLock<NixString> = LazyLock::new(|| "value".into());
30
31#[cfg(test)]
32mod tests;
33
34#[derive(Clone, Debug, Deserialize, Default)]
35pub(super) enum AttrsRep {
36    #[default]
37    Empty,
38
39    Map {
40        attrs: FxHashMap<NixString, Value>,
41
42        #[serde(skip)]
43        pos: Option<Box<FxHashMap<NixString, Span>>>,
44    },
45
46    /// Warning: this represents a **two**-attribute attrset, with
47    /// attribute names "name" and "value", like `{name="foo";
48    /// value="bar";}`, *not* `{foo="bar";}`!
49    KV { name: Value, value: Value },
50}
51
52impl AttrsRep {
53    fn select(&self, key: &BStr) -> Option<&Value> {
54        match self {
55            AttrsRep::Empty => None,
56
57            AttrsRep::KV { name, value } => match &**key {
58                b"name" => Some(name),
59                b"value" => Some(value),
60                _ => None,
61            },
62
63            AttrsRep::Map { attrs, .. } => attrs.get(key),
64        }
65    }
66
67    fn contains(&self, key: &BStr) -> bool {
68        match self {
69            AttrsRep::Empty => false,
70            AttrsRep::KV { .. } => key == "name" || key == "value",
71            AttrsRep::Map { attrs, .. } => attrs.contains_key(key),
72        }
73    }
74}
75
76#[repr(transparent)]
77#[derive(Clone, Debug, Default)]
78pub struct NixAttrs(pub(super) Rc<AttrsRep>);
79
80impl From<AttrsRep> for NixAttrs {
81    fn from(rep: AttrsRep) -> Self {
82        NixAttrs(Rc::new(rep))
83    }
84}
85
86impl<K, V> FromIterator<(K, V)> for NixAttrs
87where
88    NixString: From<K>,
89    Value: From<V>,
90{
91    fn from_iter<T>(iter: T) -> NixAttrs
92    where
93        T: IntoIterator<Item = (K, V)>,
94    {
95        AttrsRep::Map {
96            attrs: iter
97                .into_iter()
98                .map(|(k, v)| (k.into(), v.into()))
99                .collect(),
100            pos: None,
101        }
102        .into()
103    }
104}
105
106impl From<BTreeMap<NixString, Value>> for NixAttrs {
107    fn from(map: BTreeMap<NixString, Value>) -> Self {
108        AttrsRep::Map {
109            attrs: map.into_iter().collect(),
110            pos: None,
111        }
112        .into()
113    }
114}
115
116impl From<FxHashMap<NixString, Value>> for NixAttrs {
117    fn from(map: FxHashMap<NixString, Value>) -> Self {
118        AttrsRep::Map {
119            attrs: map,
120            pos: None,
121        }
122        .into()
123    }
124}
125
126impl TotalDisplay for NixAttrs {
127    fn total_fmt(&self, f: &mut std::fmt::Formatter<'_>, set: &mut ThunkSet) -> std::fmt::Result {
128        if self.is_derivation() {
129            write!(f, "«derivation")?;
130            // We cant use the default TotalDisplay implementation, because nix doesn't put quotes
131            // around the path here.
132            if let Some(Value::Thunk(p)) = self.select("drvPath")
133                && p.is_forced()
134                && let Ok(drv) = p.value().to_contextful_str()
135            {
136                write!(f, " {}", drv.to_str_lossy())?;
137            };
138            return write!(f, "»");
139        }
140
141        f.write_str("{ ")?;
142
143        for (name, value) in self.iter_sorted() {
144            write!(f, "{} = ", name.ident_str())?;
145            value.total_fmt(f, set)?;
146            f.write_str("; ")?;
147        }
148
149        f.write_str("}")
150    }
151}
152
153impl<'de> Deserialize<'de> for NixAttrs {
154    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
155    where
156        D: Deserializer<'de>,
157    {
158        struct MapVisitor;
159
160        impl<'de> Visitor<'de> for MapVisitor {
161            type Value = NixAttrs;
162
163            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
164                formatter.write_str("a valid Nix attribute set")
165            }
166
167            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
168            where
169                A: serde::de::MapAccess<'de>,
170            {
171                let mut stack_array = Vec::with_capacity(map.size_hint().unwrap_or(0) * 2);
172
173                while let Some((key, value)) = map.next_entry()? {
174                    stack_array.push(key);
175                    stack_array.push(value);
176                }
177
178                Ok(NixAttrs::construct(stack_array.len() / 2, stack_array, &[])
179                    .map_err(A::Error::custom)?
180                    .expect("Catchable values are unreachable here"))
181            }
182        }
183
184        deserializer.deserialize_map(MapVisitor)
185    }
186}
187
188impl NixAttrs {
189    pub fn empty() -> Self {
190        AttrsRep::Empty.into()
191    }
192
193    /// Compare two attribute sets by pointer equality. Only makes
194    /// sense for some attribute set representations, i.e. returning
195    /// `false` does not mean that the two attribute sets do not have
196    /// equal *content*.
197    pub fn ptr_eq(&self, other: &Self) -> bool {
198        Rc::ptr_eq(&self.0, &other.0)
199    }
200
201    /// Return an attribute set containing the merge of the two
202    /// provided sets. Keys from the `other` set have precedence.
203    pub fn update(self, other: Self) -> Self {
204        // Short-circuit on some optimal cases:
205        match (self.0.as_ref(), other.0.as_ref()) {
206            (AttrsRep::Empty, AttrsRep::Empty) => return self,
207            (AttrsRep::Empty, _) => return other,
208            (_, AttrsRep::Empty) => return self,
209            (AttrsRep::KV { .. }, AttrsRep::KV { .. }) => return other,
210
211            // Explicitly handle all branches instead of falling
212            // through, to ensure that we get at least some compiler
213            // errors if variants are modified.
214            (AttrsRep::Map { .. }, AttrsRep::Map { .. })
215            | (AttrsRep::Map { .. }, AttrsRep::KV { .. })
216            | (AttrsRep::KV { .. }, AttrsRep::Map { .. }) => {}
217        };
218
219        // Slightly more advanced, but still optimised updates
220        match (Rc::unwrap_or_clone(self.0), Rc::unwrap_or_clone(other.0)) {
221            (AttrsRep::Map { mut attrs, .. }, AttrsRep::KV { name, value }) => {
222                attrs.insert(NAME.clone(), name);
223                attrs.insert(VALUE.clone(), value);
224                AttrsRep::Map { attrs, pos: None }.into()
225            }
226
227            (AttrsRep::KV { name, value }, AttrsRep::Map { mut attrs, .. }) => {
228                match attrs.entry(NAME.clone()) {
229                    hash_map::Entry::Vacant(e) => {
230                        e.insert(name);
231                    }
232
233                    hash_map::Entry::Occupied(_) => { /* name from `m` has precedence */ }
234                };
235
236                match attrs.entry(VALUE.clone()) {
237                    hash_map::Entry::Vacant(e) => {
238                        e.insert(value);
239                    }
240
241                    hash_map::Entry::Occupied(_) => { /* value from `m` has precedence */ }
242                };
243
244                AttrsRep::Map { attrs, pos: None }.into()
245            }
246
247            // Plain merge of maps.
248            (AttrsRep::Map { attrs: mut m1, .. }, AttrsRep::Map { attrs: mut m2, .. }) => {
249                let map = if m1.len() >= m2.len() {
250                    m1.extend(m2);
251                    m1
252                } else {
253                    for (key, val) in m1.into_iter() {
254                        m2.entry(key).or_insert(val);
255                    }
256                    m2
257                };
258                AttrsRep::Map {
259                    attrs: map,
260                    pos: None,
261                }
262                .into()
263            }
264
265            // Cases handled above by the borrowing match:
266            _ => unreachable!(),
267        }
268    }
269
270    /// Return the number of key-value entries in an attrset.
271    pub fn len(&self) -> usize {
272        match self.0.as_ref() {
273            AttrsRep::Map { attrs, .. } => attrs.len(),
274            AttrsRep::Empty => 0,
275            AttrsRep::KV { .. } => 2,
276        }
277    }
278
279    pub fn is_empty(&self) -> bool {
280        match self.0.as_ref() {
281            AttrsRep::Map { attrs, .. } => attrs.is_empty(),
282            AttrsRep::Empty => true,
283            AttrsRep::KV { .. } => false,
284        }
285    }
286
287    /// Select a value from an attribute set by key.
288    pub fn select<K>(&self, key: &K) -> Option<&Value>
289    where
290        K: Borrow<BStr> + ?Sized,
291    {
292        self.0.select(key.borrow())
293    }
294
295    /// Select a required value from an attribute set by key, return
296    /// an `AttributeNotFound` error if it is missing.
297    pub fn select_required<K>(&self, key: &K) -> Result<&Value, ErrorKind>
298    where
299        K: Borrow<BStr> + ?Sized,
300    {
301        self.select(key)
302            .ok_or_else(|| ErrorKind::AttributeNotFound {
303                name: key.borrow().to_string(),
304            })
305    }
306
307    pub fn contains<'a, K: 'a>(&self, key: K) -> bool
308    where
309        &'a BStr: From<K>,
310    {
311        self.0.contains(key.into())
312    }
313
314    /// Construct an iterator over all the key-value pairs in the attribute set.
315    #[allow(clippy::needless_lifetimes)]
316    pub fn iter<'a>(&'a self) -> Iter<KeyValue<'a>> {
317        Iter(match &self.0.as_ref() {
318            AttrsRep::Map { attrs, .. } => KeyValue::Map(attrs.iter()),
319            AttrsRep::Empty => KeyValue::Empty,
320
321            AttrsRep::KV { name, value } => KeyValue::KV {
322                name,
323                value,
324                at: IterKV::default(),
325            },
326        })
327    }
328
329    /// Same as iter(), but marks call sites which rely on the
330    /// iteration being lexicographic.
331    pub fn iter_sorted(&self) -> Iter<KeyValue<'_>> {
332        Iter(match self.0.as_ref() {
333            AttrsRep::Empty => KeyValue::Empty,
334            AttrsRep::Map { attrs, .. } => {
335                let sorted = attrs.iter().sorted_by_key(|x| x.0);
336                KeyValue::Sorted(sorted)
337            }
338            AttrsRep::KV { name, value } => KeyValue::KV {
339                name,
340                value,
341                at: IterKV::default(),
342            },
343        })
344    }
345
346    /// Same as [IntoIterator::into_iter], but marks call sites which rely on the
347    /// iteration being lexicographic.
348    pub fn into_iter_sorted(self) -> OwnedAttrsIterator {
349        let iter = match Rc::<AttrsRep>::try_unwrap(self.0) {
350            Ok(attrs) => match attrs {
351                AttrsRep::Empty => IntoIterRepr::Empty,
352                AttrsRep::Map { attrs, .. } => {
353                    IntoIterRepr::Finite(attrs.into_iter().sorted_by(|(a, _), (b, _)| a.cmp(b)))
354                }
355                AttrsRep::KV { name, value } => IntoIterRepr::Finite(
356                    vec![(NAME.clone(), name), (VALUE.clone(), value)].into_iter(),
357                ),
358            },
359            Err(rc) => match rc.as_ref() {
360                AttrsRep::Empty => IntoIterRepr::Empty,
361                AttrsRep::Map { attrs, .. } => IntoIterRepr::Finite(
362                    attrs
363                        .iter()
364                        .map(|(k, v)| (k.clone(), v.clone()))
365                        .sorted_by(|(a, _), (b, _)| a.cmp(b)),
366                ),
367                AttrsRep::KV { name, value } => IntoIterRepr::Finite(
368                    vec![(NAME.clone(), name.clone()), (VALUE.clone(), value.clone())].into_iter(),
369                ),
370            },
371        };
372        OwnedAttrsIterator(iter)
373    }
374
375    /// Construct an iterator over all the keys of the attribute set
376    pub fn keys(&self) -> Keys<'_> {
377        Keys(match self.0.as_ref() {
378            AttrsRep::Empty => KeysInner::Empty,
379            AttrsRep::KV { .. } => KeysInner::KV(IterKV::default()),
380
381            // TODO(tazjin): only sort when required, not always.
382            AttrsRep::Map { attrs, .. } => KeysInner::Map(attrs.keys()),
383        })
384    }
385
386    /// Same as [Self::keys], but marks call sites which rely on the
387    /// iteration being lexicographic.
388    pub fn keys_sorted(&self) -> Keys<'_> {
389        Keys(match self.0.as_ref() {
390            AttrsRep::Map { attrs, .. } => KeysInner::Sorted(attrs.keys().sorted()),
391            AttrsRep::Empty => KeysInner::Empty,
392            AttrsRep::KV { .. } => KeysInner::KV(IterKV::default()),
393        })
394    }
395
396    /// Implement construction logic of an attribute set, to encapsulate
397    /// logic about attribute set optimisations inside of this module.
398    pub fn construct(
399        count: usize,
400        mut stack_slice: Vec<Value>,
401        spans: &[Span],
402    ) -> Result<Result<Self, CatchableErrorKind>, ErrorKind> {
403        debug_assert!(
404            stack_slice.len() == count * 2,
405            "construct_attrs called with count == {}, but slice.len() == {}",
406            count,
407            stack_slice.len(),
408        );
409        // FUTUREWORK: this invariant will be true once we fully
410        // implement span collection from everywhere e.g. inherit (from),
411        // formals, etc.
412        //
413        // debug_assert!(
414        //     spans.len() == count || spans.is_empty(),
415        //     "construct_attrs called with count == {}, but spans.len() == {}",
416        //     count,
417        //     spans.len(),
418        // );
419
420        // Optimisation: Empty attribute set
421        if count == 0 {
422            return Ok(Ok(AttrsRep::Empty.into()));
423        }
424
425        // Optimisation: KV pattern
426        if count == 2
427            && let Some(kv) = attempt_optimise_kv(&mut stack_slice)
428        {
429            return Ok(Ok(kv));
430        }
431
432        let mut attrs_map = FxHashMap::with_capacity_and_hasher(count, rustc_hash::FxBuildHasher);
433        let mut attrs_pos =
434            FxHashMap::with_capacity_and_hasher(spans.len(), rustc_hash::FxBuildHasher);
435        let mut spans = spans.iter().rev().copied();
436
437        for _ in 0..count {
438            let value = stack_slice.pop().unwrap();
439            let key = stack_slice.pop().unwrap();
440            let span = spans.next();
441
442            match key {
443                Value::String(ks) => set_attr(&mut attrs_map, ks, value, &mut attrs_pos, span)?,
444
445                Value::Null => {
446                    // This is in fact valid, but leads to the value
447                    // being ignored and nothing being set, i.e. `{
448                    // ${null} = 1; } => { }`.
449                    continue;
450                }
451
452                Value::Catchable(err) => return Ok(Err(*err)),
453
454                other => return Err(ErrorKind::InvalidAttributeName(other)),
455            }
456        }
457
458        Ok(Ok(AttrsRep::Map {
459            attrs: attrs_map,
460            pos: if attrs_pos.is_empty() {
461                None
462            } else {
463                Some(Box::new(attrs_pos))
464            },
465        }
466        .into()))
467    }
468
469    /// Construct an optimized "KV"-style attribute set given the value for the
470    /// `"name"` key, and the value for the `"value"` key
471    pub(crate) fn from_kv(name: Value, value: Value) -> Self {
472        AttrsRep::KV { name, value }.into()
473    }
474
475    /// Calculate the intersection of the attribute sets.
476    /// The right side value is used when the keys match.
477    pub(crate) fn intersect(&self, other: &Self) -> NixAttrs {
478        match (self.0.as_ref(), other.0.as_ref()) {
479            (AttrsRep::Empty, _) | (_, AttrsRep::Empty) => AttrsRep::Empty.into(),
480            (AttrsRep::Map { attrs: lhs, .. }, AttrsRep::Map { attrs: rhs, .. }) => {
481                let mut out = FxHashMap::with_capacity_and_hasher(
482                    std::cmp::min(lhs.len(), rhs.len()),
483                    rustc_hash::FxBuildHasher,
484                );
485                if lhs.len() < rhs.len() {
486                    for key in lhs.keys() {
487                        if let Some(val) = rhs.get(key) {
488                            out.insert(key.clone(), val.clone());
489                        }
490                    }
491                } else {
492                    for (key, val) in rhs.iter() {
493                        if lhs.contains_key(key) {
494                            out.insert(key.clone(), val.clone());
495                        }
496                    }
497                };
498                AttrsRep::Map {
499                    attrs: out,
500                    pos: None,
501                }
502                .into()
503            }
504            (AttrsRep::Map { attrs, .. }, AttrsRep::KV { name, value }) => {
505                let mut out = FxHashMap::with_capacity_and_hasher(2, rustc_hash::FxBuildHasher);
506                if attrs.contains_key(NAME.as_bstr()) {
507                    out.insert(NAME.clone(), name.clone());
508                }
509                if attrs.contains_key(VALUE.as_bstr()) {
510                    out.insert(VALUE.clone(), value.clone());
511                }
512
513                if out.is_empty() {
514                    NixAttrs::empty()
515                } else {
516                    out.into()
517                }
518            }
519            (AttrsRep::KV { .. }, AttrsRep::Map { attrs, .. }) => {
520                let mut out = FxHashMap::with_capacity_and_hasher(2, rustc_hash::FxBuildHasher);
521                if let Some(name) = attrs.get(NAME.as_bstr()) {
522                    out.insert(NAME.clone(), name.clone());
523                }
524                if let Some(value) = attrs.get(VALUE.as_bstr()) {
525                    out.insert(VALUE.clone(), value.clone());
526                }
527
528                if out.is_empty() {
529                    NixAttrs::empty()
530                } else {
531                    AttrsRep::Map {
532                        attrs: out,
533                        pos: None,
534                    }
535                    .into()
536                }
537            }
538            (AttrsRep::KV { .. }, AttrsRep::KV { .. }) => other.clone(),
539        }
540    }
541
542    /// Returns whether this attr set is a derivation.
543    pub fn is_derivation(&self) -> bool {
544        let Some(Value::String(kind)) = self.select("type") else {
545            return false;
546        };
547        *kind == "derivation"
548    }
549
550    pub fn get_attr_pos<K>(&self, key: &K) -> Option<Span>
551    where
552        K: Borrow<BStr> + ?Sized,
553    {
554        match self.0.as_ref() {
555            AttrsRep::Map { pos, .. } => pos.as_ref()?.get(key.borrow()).copied(),
556            _ => None,
557        }
558    }
559}
560
561impl IntoIterator for NixAttrs {
562    type Item = (NixString, Value);
563    type IntoIter = OwnedAttrsIterator;
564
565    fn into_iter(self) -> Self::IntoIter {
566        match Rc::unwrap_or_clone(self.0) {
567            AttrsRep::Empty => OwnedAttrsIterator(IntoIterRepr::Empty),
568            AttrsRep::KV { name, value } => OwnedAttrsIterator(IntoIterRepr::Finite(
569                vec![(NAME.clone(), name), (VALUE.clone(), value)].into_iter(),
570            )),
571            AttrsRep::Map { attrs, .. } => OwnedAttrsIterator(IntoIterRepr::Map(attrs.into_iter())),
572        }
573    }
574}
575
576/// In Nix, name/value attribute pairs are frequently constructed from
577/// literals. This particular case should avoid allocation of a map,
578/// additional heap values etc. and use the optimised `KV` variant
579/// instead.
580///
581/// ```norust
582/// `slice` is the top of the stack from which the attrset is being
583/// constructed, e.g.
584///
585///   slice: [ "value" 5 "name" "foo" ]
586///   index:   0       1 2      3
587///   stack:   3       2 1      0
588/// ```
589fn attempt_optimise_kv(slice: &mut [Value]) -> Option<NixAttrs> {
590    let (name_idx, value_idx) = {
591        match (&slice[2], &slice[0]) {
592            (Value::String(s1), Value::String(s2)) if (*s1 == *NAME && *s2 == *VALUE) => (3, 1),
593            (Value::String(s1), Value::String(s2)) if (*s1 == *VALUE && *s2 == *NAME) => (1, 3),
594
595            // Technically this branch lets type errors pass,
596            // but they will be caught during normal attribute
597            // set construction instead.
598            _ => return None,
599        }
600    };
601
602    Some(NixAttrs::from_kv(
603        slice[name_idx].clone(),
604        slice[value_idx].clone(),
605    ))
606}
607
608/// Set an attribute on an in-construction attribute set, while
609/// checking against duplicate keys.
610fn set_attr(
611    map: &mut FxHashMap<NixString, Value>,
612    key: NixString,
613    value: Value,
614    pos: &mut FxHashMap<NixString, Span>,
615    span: Option<Span>,
616) -> Result<(), ErrorKind> {
617    match map.entry(key) {
618        hash_map::Entry::Occupied(entry) => Err(ErrorKind::DuplicateAttrsKey {
619            key: entry.key().to_string(),
620        }),
621
622        hash_map::Entry::Vacant(entry) => {
623            if let Some(span) = span {
624                pos.insert(entry.key().clone(), span);
625            }
626            entry.insert(value);
627            Ok(())
628        }
629    }
630}
631
632/// Internal helper type to track the iteration status of an iterator
633/// over the name/value representation.
634#[derive(Debug, Default)]
635pub enum IterKV {
636    #[default]
637    Name,
638    Value,
639    Done,
640}
641
642impl IterKV {
643    fn next(&mut self) {
644        match *self {
645            Self::Name => *self = Self::Value,
646            Self::Value => *self = Self::Done,
647            Self::Done => {}
648        }
649    }
650}
651
652/// Iterator representation over the keys *and* values of an attribute
653/// set.
654pub enum KeyValue<'a> {
655    Empty,
656
657    KV {
658        name: &'a Value,
659        value: &'a Value,
660        at: IterKV,
661    },
662
663    Map(hash_map::Iter<'a, NixString, Value>),
664
665    Sorted(std::vec::IntoIter<(&'a NixString, &'a Value)>),
666}
667
668/// Iterator over a Nix attribute set.
669// This wrapper type exists to make the inner "raw" iterator
670// inaccessible.
671#[repr(transparent)]
672pub struct Iter<T>(T);
673
674impl<'a> Iterator for Iter<KeyValue<'a>> {
675    type Item = (&'a NixString, &'a Value);
676
677    fn next(&mut self) -> Option<Self::Item> {
678        match &mut self.0 {
679            KeyValue::Map(inner) => inner.next(),
680            KeyValue::Empty => None,
681            KeyValue::KV { name, value, at } => match at {
682                IterKV::Name => {
683                    at.next();
684                    Some((&NAME, name))
685                }
686
687                IterKV::Value => {
688                    at.next();
689                    Some((&VALUE, value))
690                }
691
692                IterKV::Done => None,
693            },
694            KeyValue::Sorted(inner) => inner.next(),
695        }
696    }
697}
698
699impl ExactSizeIterator for Iter<KeyValue<'_>> {
700    fn len(&self) -> usize {
701        match &self.0 {
702            KeyValue::Empty => 0,
703            KeyValue::KV { .. } => 2,
704            KeyValue::Map(inner) => inner.len(),
705            KeyValue::Sorted(inner) => inner.len(),
706        }
707    }
708}
709
710enum KeysInner<'a> {
711    Empty,
712    KV(IterKV),
713    Map(hash_map::Keys<'a, NixString, Value>),
714    Sorted(std::vec::IntoIter<&'a NixString>),
715}
716
717pub struct Keys<'a>(KeysInner<'a>);
718
719impl<'a> Iterator for Keys<'a> {
720    type Item = &'a NixString;
721
722    fn next(&mut self) -> Option<Self::Item> {
723        match &mut self.0 {
724            KeysInner::Empty => None,
725            KeysInner::KV(at @ IterKV::Name) => {
726                at.next();
727                Some(&NAME)
728            }
729            KeysInner::KV(at @ IterKV::Value) => {
730                at.next();
731                Some(&VALUE)
732            }
733            KeysInner::KV(IterKV::Done) => None,
734            KeysInner::Map(m) => m.next(),
735            KeysInner::Sorted(v) => v.next(),
736        }
737    }
738}
739
740impl<'a> IntoIterator for &'a NixAttrs {
741    type Item = (&'a NixString, &'a Value);
742
743    type IntoIter = Iter<KeyValue<'a>>;
744
745    fn into_iter(self) -> Self::IntoIter {
746        self.iter()
747    }
748}
749
750impl ExactSizeIterator for Keys<'_> {
751    fn len(&self) -> usize {
752        match &self.0 {
753            KeysInner::Empty => 0,
754            KeysInner::KV(_) => 2,
755            KeysInner::Map(m) => m.len(),
756            KeysInner::Sorted(v) => v.len(),
757        }
758    }
759}
760
761/// Internal representation of an owning attrset iterator
762pub enum IntoIterRepr {
763    Empty,
764    Finite(std::vec::IntoIter<(NixString, Value)>),
765    Map(hash_map::IntoIter<NixString, Value>),
766}
767
768/// Wrapper type which hides the internal implementation details from
769/// users.
770#[repr(transparent)]
771pub struct OwnedAttrsIterator(IntoIterRepr);
772
773impl Iterator for OwnedAttrsIterator {
774    type Item = (NixString, Value);
775
776    fn next(&mut self) -> Option<Self::Item> {
777        match &mut self.0 {
778            IntoIterRepr::Empty => None,
779            IntoIterRepr::Finite(inner) => inner.next(),
780            IntoIterRepr::Map(m) => m.next(),
781        }
782    }
783}
784
785impl ExactSizeIterator for OwnedAttrsIterator {
786    fn len(&self) -> usize {
787        match &self.0 {
788            IntoIterRepr::Empty => 0,
789            IntoIterRepr::Finite(inner) => inner.len(),
790            IntoIterRepr::Map(inner) => inner.len(),
791        }
792    }
793}
794
795impl DoubleEndedIterator for OwnedAttrsIterator {
796    fn next_back(&mut self) -> Option<Self::Item> {
797        match &mut self.0 {
798            IntoIterRepr::Empty => None,
799            IntoIterRepr::Finite(inner) => inner.next_back(),
800            // hashmaps have arbitary iteration order, so reversing it would not make a difference
801            IntoIterRepr::Map(inner) => inner.next(),
802        }
803    }
804}