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        debug_assert!(
410            spans.len() == count || spans.is_empty(),
411            "construct_attrs called with count == {}, but spans.len() == {}",
412            count,
413            spans.len(),
414        );
415
416        // Optimisation: Empty attribute set
417        if count == 0 {
418            return Ok(Ok(AttrsRep::Empty.into()));
419        }
420
421        // Optimisation: KV pattern
422        if count == 2
423            && let Some(kv) = attempt_optimise_kv(&mut stack_slice)
424        {
425            return Ok(Ok(kv));
426        }
427
428        let mut attrs_map = FxHashMap::with_capacity_and_hasher(count, rustc_hash::FxBuildHasher);
429        let mut attrs_pos =
430            FxHashMap::with_capacity_and_hasher(spans.len(), rustc_hash::FxBuildHasher);
431        let mut spans = spans.iter().rev().copied();
432
433        for _ in 0..count {
434            let value = stack_slice.pop().unwrap();
435            let key = stack_slice.pop().unwrap();
436            let span = spans.next();
437
438            match key {
439                Value::String(ks) => set_attr(&mut attrs_map, ks, value, &mut attrs_pos, span)?,
440
441                Value::Null => {
442                    // This is in fact valid, but leads to the value
443                    // being ignored and nothing being set, i.e. `{
444                    // ${null} = 1; } => { }`.
445                    continue;
446                }
447
448                Value::Catchable(err) => return Ok(Err(*err)),
449
450                other => return Err(ErrorKind::InvalidAttributeName(other)),
451            }
452        }
453
454        Ok(Ok(AttrsRep::Map {
455            attrs: attrs_map,
456            pos: if attrs_pos.is_empty() {
457                None
458            } else {
459                Some(Box::new(attrs_pos))
460            },
461        }
462        .into()))
463    }
464
465    /// Construct an optimized "KV"-style attribute set given the value for the
466    /// `"name"` key, and the value for the `"value"` key
467    pub(crate) fn from_kv(name: Value, value: Value) -> Self {
468        AttrsRep::KV { name, value }.into()
469    }
470
471    /// Calculate the intersection of the attribute sets.
472    /// The right side value is used when the keys match.
473    pub(crate) fn intersect(&self, other: &Self) -> NixAttrs {
474        match (self.0.as_ref(), other.0.as_ref()) {
475            (AttrsRep::Empty, _) | (_, AttrsRep::Empty) => AttrsRep::Empty.into(),
476            (AttrsRep::Map { attrs: lhs, .. }, AttrsRep::Map { attrs: rhs, .. }) => {
477                let mut out = FxHashMap::with_capacity_and_hasher(
478                    std::cmp::min(lhs.len(), rhs.len()),
479                    rustc_hash::FxBuildHasher,
480                );
481                if lhs.len() < rhs.len() {
482                    for key in lhs.keys() {
483                        if let Some(val) = rhs.get(key) {
484                            out.insert(key.clone(), val.clone());
485                        }
486                    }
487                } else {
488                    for (key, val) in rhs.iter() {
489                        if lhs.contains_key(key) {
490                            out.insert(key.clone(), val.clone());
491                        }
492                    }
493                };
494                AttrsRep::Map {
495                    attrs: out,
496                    pos: None,
497                }
498                .into()
499            }
500            (AttrsRep::Map { attrs, .. }, AttrsRep::KV { name, value }) => {
501                let mut out = FxHashMap::with_capacity_and_hasher(2, rustc_hash::FxBuildHasher);
502                if attrs.contains_key(NAME.as_bstr()) {
503                    out.insert(NAME.clone(), name.clone());
504                }
505                if attrs.contains_key(VALUE.as_bstr()) {
506                    out.insert(VALUE.clone(), value.clone());
507                }
508
509                if out.is_empty() {
510                    NixAttrs::empty()
511                } else {
512                    out.into()
513                }
514            }
515            (AttrsRep::KV { .. }, AttrsRep::Map { attrs, .. }) => {
516                let mut out = FxHashMap::with_capacity_and_hasher(2, rustc_hash::FxBuildHasher);
517                if let Some(name) = attrs.get(NAME.as_bstr()) {
518                    out.insert(NAME.clone(), name.clone());
519                }
520                if let Some(value) = attrs.get(VALUE.as_bstr()) {
521                    out.insert(VALUE.clone(), value.clone());
522                }
523
524                if out.is_empty() {
525                    NixAttrs::empty()
526                } else {
527                    AttrsRep::Map {
528                        attrs: out,
529                        pos: None,
530                    }
531                    .into()
532                }
533            }
534            (AttrsRep::KV { .. }, AttrsRep::KV { .. }) => other.clone(),
535        }
536    }
537
538    /// Returns whether this attr set is a derivation.
539    pub fn is_derivation(&self) -> bool {
540        let Some(Value::String(kind)) = self.select("type") else {
541            return false;
542        };
543        *kind == "derivation"
544    }
545
546    pub fn get_attr_pos<K>(&self, key: &K) -> Option<Span>
547    where
548        K: Borrow<BStr> + ?Sized,
549    {
550        match self.0.as_ref() {
551            AttrsRep::Map { pos, .. } => pos.as_ref()?.get(key.borrow()).copied(),
552            _ => None,
553        }
554    }
555}
556
557impl IntoIterator for NixAttrs {
558    type Item = (NixString, Value);
559    type IntoIter = OwnedAttrsIterator;
560
561    fn into_iter(self) -> Self::IntoIter {
562        match Rc::unwrap_or_clone(self.0) {
563            AttrsRep::Empty => OwnedAttrsIterator(IntoIterRepr::Empty),
564            AttrsRep::KV { name, value } => OwnedAttrsIterator(IntoIterRepr::Finite(
565                vec![(NAME.clone(), name), (VALUE.clone(), value)].into_iter(),
566            )),
567            AttrsRep::Map { attrs, .. } => OwnedAttrsIterator(IntoIterRepr::Map(attrs.into_iter())),
568        }
569    }
570}
571
572/// In Nix, name/value attribute pairs are frequently constructed from
573/// literals. This particular case should avoid allocation of a map,
574/// additional heap values etc. and use the optimised `KV` variant
575/// instead.
576///
577/// ```norust
578/// `slice` is the top of the stack from which the attrset is being
579/// constructed, e.g.
580///
581///   slice: [ "value" 5 "name" "foo" ]
582///   index:   0       1 2      3
583///   stack:   3       2 1      0
584/// ```
585fn attempt_optimise_kv(slice: &mut [Value]) -> Option<NixAttrs> {
586    let (name_idx, value_idx) = {
587        match (&slice[2], &slice[0]) {
588            (Value::String(s1), Value::String(s2)) if (*s1 == *NAME && *s2 == *VALUE) => (3, 1),
589            (Value::String(s1), Value::String(s2)) if (*s1 == *VALUE && *s2 == *NAME) => (1, 3),
590
591            // Technically this branch lets type errors pass,
592            // but they will be caught during normal attribute
593            // set construction instead.
594            _ => return None,
595        }
596    };
597
598    Some(NixAttrs::from_kv(
599        slice[name_idx].clone(),
600        slice[value_idx].clone(),
601    ))
602}
603
604/// Set an attribute on an in-construction attribute set, while
605/// checking against duplicate keys.
606fn set_attr(
607    map: &mut FxHashMap<NixString, Value>,
608    key: NixString,
609    value: Value,
610    pos: &mut FxHashMap<NixString, Span>,
611    span: Option<Span>,
612) -> Result<(), ErrorKind> {
613    match map.entry(key) {
614        hash_map::Entry::Occupied(entry) => Err(ErrorKind::DuplicateAttrsKey {
615            key: entry.key().to_string(),
616        }),
617
618        hash_map::Entry::Vacant(entry) => {
619            if let Some(span) = span {
620                pos.insert(entry.key().clone(), span);
621            }
622            entry.insert(value);
623            Ok(())
624        }
625    }
626}
627
628/// Internal helper type to track the iteration status of an iterator
629/// over the name/value representation.
630#[derive(Debug, Default)]
631pub enum IterKV {
632    #[default]
633    Name,
634    Value,
635    Done,
636}
637
638impl IterKV {
639    fn next(&mut self) {
640        match *self {
641            Self::Name => *self = Self::Value,
642            Self::Value => *self = Self::Done,
643            Self::Done => {}
644        }
645    }
646}
647
648/// Iterator representation over the keys *and* values of an attribute
649/// set.
650pub enum KeyValue<'a> {
651    Empty,
652
653    KV {
654        name: &'a Value,
655        value: &'a Value,
656        at: IterKV,
657    },
658
659    Map(hash_map::Iter<'a, NixString, Value>),
660
661    Sorted(std::vec::IntoIter<(&'a NixString, &'a Value)>),
662}
663
664/// Iterator over a Nix attribute set.
665// This wrapper type exists to make the inner "raw" iterator
666// inaccessible.
667#[repr(transparent)]
668pub struct Iter<T>(T);
669
670impl<'a> Iterator for Iter<KeyValue<'a>> {
671    type Item = (&'a NixString, &'a Value);
672
673    fn next(&mut self) -> Option<Self::Item> {
674        match &mut self.0 {
675            KeyValue::Map(inner) => inner.next(),
676            KeyValue::Empty => None,
677            KeyValue::KV { name, value, at } => match at {
678                IterKV::Name => {
679                    at.next();
680                    Some((&NAME, name))
681                }
682
683                IterKV::Value => {
684                    at.next();
685                    Some((&VALUE, value))
686                }
687
688                IterKV::Done => None,
689            },
690            KeyValue::Sorted(inner) => inner.next(),
691        }
692    }
693}
694
695impl ExactSizeIterator for Iter<KeyValue<'_>> {
696    fn len(&self) -> usize {
697        match &self.0 {
698            KeyValue::Empty => 0,
699            KeyValue::KV { .. } => 2,
700            KeyValue::Map(inner) => inner.len(),
701            KeyValue::Sorted(inner) => inner.len(),
702        }
703    }
704}
705
706enum KeysInner<'a> {
707    Empty,
708    KV(IterKV),
709    Map(hash_map::Keys<'a, NixString, Value>),
710    Sorted(std::vec::IntoIter<&'a NixString>),
711}
712
713pub struct Keys<'a>(KeysInner<'a>);
714
715impl<'a> Iterator for Keys<'a> {
716    type Item = &'a NixString;
717
718    fn next(&mut self) -> Option<Self::Item> {
719        match &mut self.0 {
720            KeysInner::Empty => None,
721            KeysInner::KV(at @ IterKV::Name) => {
722                at.next();
723                Some(&NAME)
724            }
725            KeysInner::KV(at @ IterKV::Value) => {
726                at.next();
727                Some(&VALUE)
728            }
729            KeysInner::KV(IterKV::Done) => None,
730            KeysInner::Map(m) => m.next(),
731            KeysInner::Sorted(v) => v.next(),
732        }
733    }
734}
735
736impl<'a> IntoIterator for &'a NixAttrs {
737    type Item = (&'a NixString, &'a Value);
738
739    type IntoIter = Iter<KeyValue<'a>>;
740
741    fn into_iter(self) -> Self::IntoIter {
742        self.iter()
743    }
744}
745
746impl ExactSizeIterator for Keys<'_> {
747    fn len(&self) -> usize {
748        match &self.0 {
749            KeysInner::Empty => 0,
750            KeysInner::KV(_) => 2,
751            KeysInner::Map(m) => m.len(),
752            KeysInner::Sorted(v) => v.len(),
753        }
754    }
755}
756
757/// Internal representation of an owning attrset iterator
758pub enum IntoIterRepr {
759    Empty,
760    Finite(std::vec::IntoIter<(NixString, Value)>),
761    Map(hash_map::IntoIter<NixString, Value>),
762}
763
764/// Wrapper type which hides the internal implementation details from
765/// users.
766#[repr(transparent)]
767pub struct OwnedAttrsIterator(IntoIterRepr);
768
769impl Iterator for OwnedAttrsIterator {
770    type Item = (NixString, Value);
771
772    fn next(&mut self) -> Option<Self::Item> {
773        match &mut self.0 {
774            IntoIterRepr::Empty => None,
775            IntoIterRepr::Finite(inner) => inner.next(),
776            IntoIterRepr::Map(m) => m.next(),
777        }
778    }
779}
780
781impl ExactSizeIterator for OwnedAttrsIterator {
782    fn len(&self) -> usize {
783        match &self.0 {
784            IntoIterRepr::Empty => 0,
785            IntoIterRepr::Finite(inner) => inner.len(),
786            IntoIterRepr::Map(inner) => inner.len(),
787        }
788    }
789}
790
791impl DoubleEndedIterator for OwnedAttrsIterator {
792    fn next_back(&mut self) -> Option<Self::Item> {
793        match &mut self.0 {
794            IntoIterRepr::Empty => None,
795            IntoIterRepr::Finite(inner) => inner.next_back(),
796            // hashmaps have arbitary iteration order, so reversing it would not make a difference
797            IntoIterRepr::Map(inner) => inner.next(),
798        }
799    }
800}