1use 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 itertools::Itertools as _;
16use rustc_hash::FxHashMap;
17use serde::Deserialize;
18use serde::de::{Deserializer, Error, Visitor};
19
20use super::TotalDisplay;
21use super::Value;
22use super::string::NixString;
23use super::thunk::ThunkSet;
24use crate::CatchableErrorKind;
25use crate::errors::ErrorKind;
26
27static NAME: LazyLock<NixString> = LazyLock::new(|| "name".into());
28static VALUE: LazyLock<NixString> = LazyLock::new(|| "value".into());
29
30#[cfg(test)]
31mod tests;
32
33#[derive(Clone, Debug, Deserialize, Default)]
34pub(super) enum AttrsRep {
35 #[default]
36 Empty,
37
38 Map(FxHashMap<NixString, Value>),
39
40 KV {
44 name: Value,
45 value: Value,
46 },
47}
48
49impl AttrsRep {
50 fn select(&self, key: &BStr) -> Option<&Value> {
51 match self {
52 AttrsRep::Empty => None,
53
54 AttrsRep::KV { name, value } => match &**key {
55 b"name" => Some(name),
56 b"value" => Some(value),
57 _ => None,
58 },
59
60 AttrsRep::Map(map) => map.get(key),
61 }
62 }
63
64 fn contains(&self, key: &BStr) -> bool {
65 match self {
66 AttrsRep::Empty => false,
67 AttrsRep::KV { .. } => key == "name" || key == "value",
68 AttrsRep::Map(map) => map.contains_key(key),
69 }
70 }
71}
72
73#[repr(transparent)]
74#[derive(Clone, Debug, Default)]
75pub struct NixAttrs(pub(super) Rc<AttrsRep>);
76
77impl From<AttrsRep> for NixAttrs {
78 fn from(rep: AttrsRep) -> Self {
79 NixAttrs(Rc::new(rep))
80 }
81}
82
83impl<K, V> FromIterator<(K, V)> for NixAttrs
84where
85 NixString: From<K>,
86 Value: From<V>,
87{
88 fn from_iter<T>(iter: T) -> NixAttrs
89 where
90 T: IntoIterator<Item = (K, V)>,
91 {
92 AttrsRep::Map(
93 iter.into_iter()
94 .map(|(k, v)| (k.into(), v.into()))
95 .collect(),
96 )
97 .into()
98 }
99}
100
101impl From<BTreeMap<NixString, Value>> for NixAttrs {
102 fn from(map: BTreeMap<NixString, Value>) -> Self {
103 AttrsRep::Map(map.into_iter().collect()).into()
104 }
105}
106
107impl From<FxHashMap<NixString, Value>> for NixAttrs {
108 fn from(map: FxHashMap<NixString, Value>) -> Self {
109 AttrsRep::Map(map).into()
110 }
111}
112
113impl TotalDisplay for NixAttrs {
114 fn total_fmt(&self, f: &mut std::fmt::Formatter<'_>, set: &mut ThunkSet) -> std::fmt::Result {
115 if self.is_derivation() {
116 write!(f, "«derivation")?;
117 if let Some(Value::Thunk(p)) = self.select("drvPath") {
120 if p.is_forced()
121 && let Ok(drv) = p.value().to_contextful_str()
122 {
123 write!(f, " {}", drv.to_str_lossy())?;
124 };
125 }
126 return write!(f, "»");
127 }
128
129 f.write_str("{ ")?;
130
131 for (name, value) in self.iter_sorted() {
132 write!(f, "{} = ", name.ident_str())?;
133 value.total_fmt(f, set)?;
134 f.write_str("; ")?;
135 }
136
137 f.write_str("}")
138 }
139}
140
141impl<'de> Deserialize<'de> for NixAttrs {
142 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
143 where
144 D: Deserializer<'de>,
145 {
146 struct MapVisitor;
147
148 impl<'de> Visitor<'de> for MapVisitor {
149 type Value = NixAttrs;
150
151 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
152 formatter.write_str("a valid Nix attribute set")
153 }
154
155 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
156 where
157 A: serde::de::MapAccess<'de>,
158 {
159 let mut stack_array = Vec::with_capacity(map.size_hint().unwrap_or(0) * 2);
160
161 while let Some((key, value)) = map.next_entry()? {
162 stack_array.push(key);
163 stack_array.push(value);
164 }
165
166 Ok(NixAttrs::construct(stack_array.len() / 2, stack_array)
167 .map_err(A::Error::custom)?
168 .expect("Catchable values are unreachable here"))
169 }
170 }
171
172 deserializer.deserialize_map(MapVisitor)
173 }
174}
175
176impl NixAttrs {
177 pub fn empty() -> Self {
178 AttrsRep::Empty.into()
179 }
180
181 pub fn ptr_eq(&self, other: &Self) -> bool {
186 Rc::ptr_eq(&self.0, &other.0)
187 }
188
189 pub fn update(self, other: Self) -> Self {
192 match (self.0.as_ref(), other.0.as_ref()) {
194 (AttrsRep::Empty, AttrsRep::Empty) => return self,
195 (AttrsRep::Empty, _) => return other,
196 (_, AttrsRep::Empty) => return self,
197 (AttrsRep::KV { .. }, AttrsRep::KV { .. }) => return other,
198
199 (AttrsRep::Map(_), AttrsRep::Map(_))
203 | (AttrsRep::Map(_), AttrsRep::KV { .. })
204 | (AttrsRep::KV { .. }, AttrsRep::Map(_)) => {}
205 };
206
207 match (Rc::unwrap_or_clone(self.0), Rc::unwrap_or_clone(other.0)) {
209 (AttrsRep::Map(mut m), AttrsRep::KV { name, value }) => {
210 m.insert(NAME.clone(), name);
211 m.insert(VALUE.clone(), value);
212 AttrsRep::Map(m).into()
213 }
214
215 (AttrsRep::KV { name, value }, AttrsRep::Map(mut m)) => {
216 match m.entry(NAME.clone()) {
217 hash_map::Entry::Vacant(e) => {
218 e.insert(name);
219 }
220
221 hash_map::Entry::Occupied(_) => { }
222 };
223
224 match m.entry(VALUE.clone()) {
225 hash_map::Entry::Vacant(e) => {
226 e.insert(value);
227 }
228
229 hash_map::Entry::Occupied(_) => { }
230 };
231
232 AttrsRep::Map(m).into()
233 }
234
235 (AttrsRep::Map(mut m1), AttrsRep::Map(mut m2)) => {
237 let map = if m1.len() >= m2.len() {
238 m1.extend(m2);
239 m1
240 } else {
241 for (key, val) in m1.into_iter() {
242 m2.entry(key).or_insert(val);
243 }
244 m2
245 };
246 AttrsRep::Map(map).into()
247 }
248
249 _ => unreachable!(),
251 }
252 }
253
254 pub fn len(&self) -> usize {
256 match self.0.as_ref() {
257 AttrsRep::Map(map) => map.len(),
258 AttrsRep::Empty => 0,
259 AttrsRep::KV { .. } => 2,
260 }
261 }
262
263 pub fn is_empty(&self) -> bool {
264 match self.0.as_ref() {
265 AttrsRep::Map(map) => map.is_empty(),
266 AttrsRep::Empty => true,
267 AttrsRep::KV { .. } => false,
268 }
269 }
270
271 pub fn select<K>(&self, key: &K) -> Option<&Value>
273 where
274 K: Borrow<BStr> + ?Sized,
275 {
276 self.0.select(key.borrow())
277 }
278
279 pub fn select_required<K>(&self, key: &K) -> Result<&Value, ErrorKind>
282 where
283 K: Borrow<BStr> + ?Sized,
284 {
285 self.select(key)
286 .ok_or_else(|| ErrorKind::AttributeNotFound {
287 name: key.borrow().to_string(),
288 })
289 }
290
291 pub fn contains<'a, K: 'a>(&self, key: K) -> bool
292 where
293 &'a BStr: From<K>,
294 {
295 self.0.contains(key.into())
296 }
297
298 #[allow(clippy::needless_lifetimes)]
300 pub fn iter<'a>(&'a self) -> Iter<KeyValue<'a>> {
301 Iter(match &self.0.as_ref() {
302 AttrsRep::Map(map) => KeyValue::Map(map.iter()),
303 AttrsRep::Empty => KeyValue::Empty,
304
305 AttrsRep::KV { name, value } => KeyValue::KV {
306 name,
307 value,
308 at: IterKV::default(),
309 },
310 })
311 }
312
313 pub fn iter_sorted(&self) -> Iter<KeyValue<'_>> {
316 Iter(match self.0.as_ref() {
317 AttrsRep::Empty => KeyValue::Empty,
318 AttrsRep::Map(map) => {
319 let sorted = map.iter().sorted_by_key(|x| x.0);
320 KeyValue::Sorted(sorted)
321 }
322 AttrsRep::KV { name, value } => KeyValue::KV {
323 name,
324 value,
325 at: IterKV::default(),
326 },
327 })
328 }
329
330 pub fn into_iter_sorted(self) -> OwnedAttrsIterator {
333 let iter = match Rc::<AttrsRep>::try_unwrap(self.0) {
334 Ok(attrs) => match attrs {
335 AttrsRep::Empty => IntoIterRepr::Empty,
336 AttrsRep::Map(map) => {
337 IntoIterRepr::Finite(map.into_iter().sorted_by(|(a, _), (b, _)| a.cmp(b)))
338 }
339 AttrsRep::KV { name, value } => IntoIterRepr::Finite(
340 vec![(NAME.clone(), name), (VALUE.clone(), value)].into_iter(),
341 ),
342 },
343 Err(rc) => match rc.as_ref() {
344 AttrsRep::Empty => IntoIterRepr::Empty,
345 AttrsRep::Map(map) => IntoIterRepr::Finite(
346 map.iter()
347 .map(|(k, v)| (k.clone(), v.clone()))
348 .sorted_by(|(a, _), (b, _)| a.cmp(b)),
349 ),
350 AttrsRep::KV { name, value } => IntoIterRepr::Finite(
351 vec![(NAME.clone(), name.clone()), (VALUE.clone(), value.clone())].into_iter(),
352 ),
353 },
354 };
355 OwnedAttrsIterator(iter)
356 }
357
358 pub fn keys(&self) -> Keys {
360 Keys(match self.0.as_ref() {
361 AttrsRep::Empty => KeysInner::Empty,
362 AttrsRep::KV { .. } => KeysInner::KV(IterKV::default()),
363
364 AttrsRep::Map(m) => KeysInner::Map(m.keys()),
366 })
367 }
368
369 pub fn keys_sorted(&self) -> Keys {
372 Keys(match self.0.as_ref() {
373 AttrsRep::Map(map) => KeysInner::Sorted(map.keys().sorted()),
374 AttrsRep::Empty => KeysInner::Empty,
375 AttrsRep::KV { .. } => KeysInner::KV(IterKV::default()),
376 })
377 }
378
379 pub fn construct(
382 count: usize,
383 mut stack_slice: Vec<Value>,
384 ) -> Result<Result<Self, CatchableErrorKind>, ErrorKind> {
385 debug_assert!(
386 stack_slice.len() == count * 2,
387 "construct_attrs called with count == {}, but slice.len() == {}",
388 count,
389 stack_slice.len(),
390 );
391
392 if count == 0 {
394 return Ok(Ok(AttrsRep::Empty.into()));
395 }
396
397 if count == 2 {
399 if let Some(kv) = attempt_optimise_kv(&mut stack_slice) {
400 return Ok(Ok(kv));
401 }
402 }
403
404 let mut attrs_map = FxHashMap::with_capacity_and_hasher(count, rustc_hash::FxBuildHasher);
405
406 for _ in 0..count {
407 let value = stack_slice.pop().unwrap();
408 let key = stack_slice.pop().unwrap();
409
410 match key {
411 Value::String(ks) => set_attr(&mut attrs_map, ks, value)?,
412
413 Value::Null => {
414 continue;
418 }
419
420 Value::Catchable(err) => return Ok(Err(*err)),
421
422 other => return Err(ErrorKind::InvalidAttributeName(other)),
423 }
424 }
425
426 Ok(Ok(AttrsRep::Map(attrs_map).into()))
427 }
428
429 pub(crate) fn from_kv(name: Value, value: Value) -> Self {
432 AttrsRep::KV { name, value }.into()
433 }
434
435 pub(crate) fn intersect(&self, other: &Self) -> NixAttrs {
438 match (self.0.as_ref(), other.0.as_ref()) {
439 (AttrsRep::Empty, _) | (_, AttrsRep::Empty) => AttrsRep::Empty.into(),
440 (AttrsRep::Map(lhs), AttrsRep::Map(rhs)) => {
441 let mut out = FxHashMap::with_capacity_and_hasher(
442 std::cmp::min(lhs.len(), rhs.len()),
443 rustc_hash::FxBuildHasher,
444 );
445 if lhs.len() < rhs.len() {
446 for key in lhs.keys() {
447 if let Some(val) = rhs.get(key) {
448 out.insert(key.clone(), val.clone());
449 }
450 }
451 } else {
452 for (key, val) in rhs.iter() {
453 if lhs.contains_key(key) {
454 out.insert(key.clone(), val.clone());
455 }
456 }
457 };
458 out.into()
459 }
460 (AttrsRep::Map(map), AttrsRep::KV { name, value }) => {
461 let mut out = FxHashMap::with_capacity_and_hasher(2, rustc_hash::FxBuildHasher);
462 if map.contains_key(NAME.as_bstr()) {
463 out.insert(NAME.clone(), name.clone());
464 }
465 if map.contains_key(VALUE.as_bstr()) {
466 out.insert(VALUE.clone(), value.clone());
467 }
468
469 if out.is_empty() {
470 NixAttrs::empty()
471 } else {
472 out.into()
473 }
474 }
475 (AttrsRep::KV { .. }, AttrsRep::Map(map)) => {
476 let mut out = FxHashMap::with_capacity_and_hasher(2, rustc_hash::FxBuildHasher);
477 if let Some(name) = map.get(NAME.as_bstr()) {
478 out.insert(NAME.clone(), name.clone());
479 }
480 if let Some(value) = map.get(VALUE.as_bstr()) {
481 out.insert(VALUE.clone(), value.clone());
482 }
483
484 if out.is_empty() {
485 NixAttrs::empty()
486 } else {
487 out.into()
488 }
489 }
490 (AttrsRep::KV { .. }, AttrsRep::KV { .. }) => other.clone(),
491 }
492 }
493
494 pub fn is_derivation(&self) -> bool {
496 let Some(Value::String(kind)) = self.select("type") else {
497 return false;
498 };
499 *kind == "derivation"
500 }
501}
502
503impl IntoIterator for NixAttrs {
504 type Item = (NixString, Value);
505 type IntoIter = OwnedAttrsIterator;
506
507 fn into_iter(self) -> Self::IntoIter {
508 match Rc::unwrap_or_clone(self.0) {
509 AttrsRep::Empty => OwnedAttrsIterator(IntoIterRepr::Empty),
510 AttrsRep::KV { name, value } => OwnedAttrsIterator(IntoIterRepr::Finite(
511 vec![(NAME.clone(), name), (VALUE.clone(), value)].into_iter(),
512 )),
513 AttrsRep::Map(map) => OwnedAttrsIterator(IntoIterRepr::Map(map.into_iter())),
514 }
515 }
516}
517
518fn attempt_optimise_kv(slice: &mut [Value]) -> Option<NixAttrs> {
532 let (name_idx, value_idx) = {
533 match (&slice[2], &slice[0]) {
534 (Value::String(s1), Value::String(s2)) if (*s1 == *NAME && *s2 == *VALUE) => (3, 1),
535 (Value::String(s1), Value::String(s2)) if (*s1 == *VALUE && *s2 == *NAME) => (1, 3),
536
537 _ => return None,
541 }
542 };
543
544 Some(NixAttrs::from_kv(
545 slice[name_idx].clone(),
546 slice[value_idx].clone(),
547 ))
548}
549
550fn set_attr(
553 map: &mut FxHashMap<NixString, Value>,
554 key: NixString,
555 value: Value,
556) -> Result<(), ErrorKind> {
557 match map.entry(key) {
558 hash_map::Entry::Occupied(entry) => Err(ErrorKind::DuplicateAttrsKey {
559 key: entry.key().to_string(),
560 }),
561
562 hash_map::Entry::Vacant(entry) => {
563 entry.insert(value);
564 Ok(())
565 }
566 }
567}
568
569#[derive(Debug, Default)]
572pub enum IterKV {
573 #[default]
574 Name,
575 Value,
576 Done,
577}
578
579impl IterKV {
580 fn next(&mut self) {
581 match *self {
582 Self::Name => *self = Self::Value,
583 Self::Value => *self = Self::Done,
584 Self::Done => {}
585 }
586 }
587}
588
589pub enum KeyValue<'a> {
592 Empty,
593
594 KV {
595 name: &'a Value,
596 value: &'a Value,
597 at: IterKV,
598 },
599
600 Map(hash_map::Iter<'a, NixString, Value>),
601
602 Sorted(std::vec::IntoIter<(&'a NixString, &'a Value)>),
603}
604
605#[repr(transparent)]
609pub struct Iter<T>(T);
610
611impl<'a> Iterator for Iter<KeyValue<'a>> {
612 type Item = (&'a NixString, &'a Value);
613
614 fn next(&mut self) -> Option<Self::Item> {
615 match &mut self.0 {
616 KeyValue::Map(inner) => inner.next(),
617 KeyValue::Empty => None,
618 KeyValue::KV { name, value, at } => match at {
619 IterKV::Name => {
620 at.next();
621 Some((&NAME, name))
622 }
623
624 IterKV::Value => {
625 at.next();
626 Some((&VALUE, value))
627 }
628
629 IterKV::Done => None,
630 },
631 KeyValue::Sorted(inner) => inner.next(),
632 }
633 }
634}
635
636impl ExactSizeIterator for Iter<KeyValue<'_>> {
637 fn len(&self) -> usize {
638 match &self.0 {
639 KeyValue::Empty => 0,
640 KeyValue::KV { .. } => 2,
641 KeyValue::Map(inner) => inner.len(),
642 KeyValue::Sorted(inner) => inner.len(),
643 }
644 }
645}
646
647enum KeysInner<'a> {
648 Empty,
649 KV(IterKV),
650 Map(hash_map::Keys<'a, NixString, Value>),
651 Sorted(std::vec::IntoIter<&'a NixString>),
652}
653
654pub struct Keys<'a>(KeysInner<'a>);
655
656impl<'a> Iterator for Keys<'a> {
657 type Item = &'a NixString;
658
659 fn next(&mut self) -> Option<Self::Item> {
660 match &mut self.0 {
661 KeysInner::Empty => None,
662 KeysInner::KV(at @ IterKV::Name) => {
663 at.next();
664 Some(&NAME)
665 }
666 KeysInner::KV(at @ IterKV::Value) => {
667 at.next();
668 Some(&VALUE)
669 }
670 KeysInner::KV(IterKV::Done) => None,
671 KeysInner::Map(m) => m.next(),
672 KeysInner::Sorted(v) => v.next(),
673 }
674 }
675}
676
677impl<'a> IntoIterator for &'a NixAttrs {
678 type Item = (&'a NixString, &'a Value);
679
680 type IntoIter = Iter<KeyValue<'a>>;
681
682 fn into_iter(self) -> Self::IntoIter {
683 self.iter()
684 }
685}
686
687impl ExactSizeIterator for Keys<'_> {
688 fn len(&self) -> usize {
689 match &self.0 {
690 KeysInner::Empty => 0,
691 KeysInner::KV(_) => 2,
692 KeysInner::Map(m) => m.len(),
693 KeysInner::Sorted(v) => v.len(),
694 }
695 }
696}
697
698pub enum IntoIterRepr {
700 Empty,
701 Finite(std::vec::IntoIter<(NixString, Value)>),
702 Map(hash_map::IntoIter<NixString, Value>),
703}
704
705#[repr(transparent)]
708pub struct OwnedAttrsIterator(IntoIterRepr);
709
710impl Iterator for OwnedAttrsIterator {
711 type Item = (NixString, Value);
712
713 fn next(&mut self) -> Option<Self::Item> {
714 match &mut self.0 {
715 IntoIterRepr::Empty => None,
716 IntoIterRepr::Finite(inner) => inner.next(),
717 IntoIterRepr::Map(m) => m.next(),
718 }
719 }
720}
721
722impl ExactSizeIterator for OwnedAttrsIterator {
723 fn len(&self) -> usize {
724 match &self.0 {
725 IntoIterRepr::Empty => 0,
726 IntoIterRepr::Finite(inner) => inner.len(),
727 IntoIterRepr::Map(inner) => inner.len(),
728 }
729 }
730}
731
732impl DoubleEndedIterator for OwnedAttrsIterator {
733 fn next_back(&mut self) -> Option<Self::Item> {
734 match &mut self.0 {
735 IntoIterRepr::Empty => None,
736 IntoIterRepr::Finite(inner) => inner.next_back(),
737 IntoIterRepr::Map(inner) => inner.next(),
739 }
740 }
741}