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