shape/
name.rs

1use std::fmt::Debug;
2use std::fmt::Display;
3use std::hash::Hash;
4use std::hash::Hasher;
5use std::marker::PhantomData;
6use std::sync::RwLock;
7
8use indexmap::IndexMap;
9use indexmap::IndexSet;
10
11use crate::helpers::quote_non_identifier;
12use crate::helpers::RefWeak;
13use crate::location::Location;
14use crate::merge::MetaMergeable;
15use crate::Error;
16use crate::MergeSet;
17use crate::Ref;
18use crate::Shape;
19use crate::ShapeCase;
20
21/// A [`Namespace`] is a collection of named [`Shape`]s, which can be used to
22/// give meaning to (resolve) [`ShapeCase::Name`] references.
23///
24/// The [`Namespace<NotFinal>`] and [`Namespace<Final>`] types represent the two
25/// stages of a namespace's lifecycle: before and after finalization. The
26/// finalization process resolves all [`ShapeCase::Name`] references within the
27/// shapes of the namespace, ensuring every name has a stable weak reference
28/// back to the named shape's [`ShapeCase`].
29///
30/// We implement `Clone` for `Namespace` manually below, always returning a
31/// `Namespace<NotFinal>`.
32#[derive(Debug, PartialEq, Eq, Clone)]
33pub struct Namespace<T = NotFinal> {
34    // Once the Namespace has been finalized, the pointer addresses of the
35    // Ref<Shape> references must not change, because nested child shapes may
36    // refer back to them via WeakRef<Shape> pointers. This means the map must
37    // be assigned at most once per key.
38    map: Ref<IndexMap<String, Ref<Shape>>>,
39    _final_state: PhantomData<T>,
40}
41
42#[derive(Debug, PartialEq, Eq, Clone)]
43pub struct NotFinal;
44
45#[derive(Debug, PartialEq, Eq, Clone)]
46pub struct Final;
47
48impl Namespace {
49    /// Create a new empty unfinalized [`Namespace`].
50    #[must_use]
51    pub fn new() -> Namespace<NotFinal> {
52        Namespace::<NotFinal> {
53            map: Ref::new(IndexMap::new()),
54            _final_state: PhantomData::<NotFinal>,
55        }
56    }
57}
58
59impl Default for Namespace<NotFinal> {
60    fn default() -> Self {
61        Self::new()
62    }
63}
64
65impl<T> Namespace<T> {
66    /// Any [`Namespace`] (`Namespace<NotFinal>` or `Namespace<Final>`) can tell
67    /// you whether a given name is bound, using the `has` method.
68    #[must_use]
69    pub fn has(&self, name: &str) -> bool {
70        self.map.contains_key(name)
71    }
72
73    /// Returns an iterator over the names in the namespace.
74    pub fn names(&self) -> impl Iterator<Item = &str> + '_ {
75        self.map.keys().map(String::as_str)
76    }
77
78    /// Non-destructively merges two [`Namespace`]s (`self` and `other`)
79    /// together into a new [`Namespace`]. When there are collisions, [`Shape`]s
80    /// of the same name will be combined with `Shape::all([self_shape,
81    /// other_shape], [])`. The `T` and `U` types of `self: &Namespace<T>` and
82    /// `other: &Namespace<U>` are unrestricted and do not have to agree, though
83    /// the final merged result is always `Namespace<NotFinal>`.
84    #[must_use]
85    pub fn merge<U>(&self, other: &Namespace<U>) -> Namespace<NotFinal> {
86        let mut merged = Namespace::<NotFinal> {
87            map: self.map.clone(),
88            _final_state: PhantomData::<NotFinal>,
89        };
90        merged.extend(other);
91        merged
92    }
93}
94
95impl Namespace<NotFinal> {
96    /// Mutably extends the current [`Namespace`] with the contents of another
97    /// [`Namespace`]. If a name already exists in `self`, the corresponding
98    /// `self_shape` will be merged with `other_shape` (from `other`) using
99    /// `Shape::all([self_shape, other_shape], [])`.
100    pub fn extend<T>(&mut self, other: &Namespace<T>) {
101        for (name, shape) in other.map.as_ref() {
102            self.insert(name.clone(), shape.as_ref().clone());
103        }
104    }
105
106    /// Mutably binds `name` to `shape` in the current [`Namespace`], cloning
107    /// `shape` and recursively propagating names derived from `name` to all
108    /// nested child shapes. Since this cloning and name propagation generally
109    /// creates a new [`Shape`], the `insert` method returns a clone of that
110    /// final `Shape` (inspectable with [`Shape::pretty_print_with_names`]).
111    pub fn insert(&mut self, name: impl Into<String>, shape: Shape) -> Shape {
112        let name = name.into();
113        if let Some(existing_shape) = self.map.get(&name) {
114            let merged = Ref::new(
115                Shape::all([existing_shape.as_ref().clone(), shape], [])
116                    // Note that [`Shape::with_base_name`] and
117                    // [`Shape::with_name`] are intentionally crate-private
118                    // (that is, `pub(crate)`), so external code can only assign
119                    // names to shapes by inserting them into [`Namespace`]s.
120                    .with_base_name(&name, []),
121            );
122            let merged_shape = merged.as_ref().clone();
123            Ref::make_mut(&mut self.map).insert(name.clone(), merged);
124            merged_shape
125        } else {
126            let named_shape_ref = Ref::new(shape.with_base_name(&name, []));
127            let named_shape_clone = named_shape_ref.as_ref().clone();
128            Ref::make_mut(&mut self.map).insert(name, named_shape_ref);
129            named_shape_clone
130        }
131    }
132
133    /// Returns a finalized version of `self`, ensuring exclusive ownership of
134    /// all `Ref<Shape>` references, then binding all [`ShapeCase::Name`]
135    /// references nested within shapes owned by the new namespace.
136    #[must_use]
137    pub fn finalize(&self) -> Namespace<Final> {
138        // Create a shared WeakScope that will be assigned to all ShapeCase::Name
139        // elements during ensure_deep_exclusive_name_ownership. Initially empty,
140        // it will be populated with the weak map reference after final_map is built.
141        let weak_scope = WeakScope::none();
142
143        let final_map = Ref::new(
144            self.map
145                .iter()
146                .map(|(name, shape)| {
147                    // Ensure the shape has the name, and all its descendants have
148                    // recursively derived child names.
149                    let named_shape = shape.as_ref().clone().with_base_name(name, []);
150                    (
151                        name.clone(),
152                        // Ensuring deep exclusive ShapeCase::Name ownership means
153                        // recursively calling Ref::make_mut(&mut self.case) to
154                        // enforce Ref::strong_count(&self.case) == 1 throughout any
155                        // subtrees that transitively contain ShapeCase::Name
156                        // elements. While doing so, we also assign the shared
157                        // weak_scope to each ShapeCase::Name element.
158                        Ref::new(ensure_deep_exclusive_name_ownership(
159                            named_shape,
160                            &weak_scope,
161                        )),
162                    )
163                })
164                .collect::<IndexMap<_, _>>(),
165        );
166
167        // Now that final_map exists, we can create a weak reference to it and
168        // populate the shared WeakScope. All ShapeCase::Name elements that were
169        // assigned this weak_scope will now be able to resolve their names.
170        weak_scope.set_weak(Ref::downgrade(&final_map));
171
172        if cfg!(debug_assertions) {
173            for (name, shape_ref) in final_map.as_ref() {
174                debug_assert!(shape_ref.has_base_name(name));
175            }
176        }
177
178        Namespace::<Final> {
179            map: final_map,
180            _final_state: PhantomData::<Final>,
181        }
182    }
183}
184
185fn ensure_deep_exclusive_name_ownership(mut shape: Shape, weak_scope: &WeakScope) -> Shape {
186    if shape.meta.nested_base_names().next().is_none() {
187        // If the shape has no names, we can skip the deep exclusive ownership
188        // check and just return the shape as-is.
189        return shape;
190    }
191
192    match Ref::make_mut(&mut shape.case) {
193        ShapeCase::Object { fields, rest } => {
194            for field_shape in fields.values_mut() {
195                *field_shape =
196                    ensure_deep_exclusive_name_ownership(field_shape.clone(), weak_scope);
197            }
198            *rest = ensure_deep_exclusive_name_ownership(rest.clone(), weak_scope);
199        }
200
201        ShapeCase::Array { prefix, tail } => {
202            for element_shape in prefix {
203                *element_shape =
204                    ensure_deep_exclusive_name_ownership(element_shape.clone(), weak_scope);
205            }
206            *tail = ensure_deep_exclusive_name_ownership(tail.clone(), weak_scope);
207        }
208
209        ShapeCase::One(shapes) => {
210            shapes.shapes = MergeSet::new(
211                shapes
212                    .iter()
213                    .map(|shape| ensure_deep_exclusive_name_ownership(shape.clone(), weak_scope)),
214            );
215        }
216
217        ShapeCase::All(shapes) => {
218            shapes.shapes = MergeSet::new(
219                shapes
220                    .iter()
221                    .map(|shape| ensure_deep_exclusive_name_ownership(shape.clone(), weak_scope)),
222            );
223        }
224
225        ShapeCase::Error(Error { partial, .. }) => {
226            if let Some(partial) = partial {
227                *partial = ensure_deep_exclusive_name_ownership(partial.clone(), weak_scope);
228            }
229        }
230
231        ShapeCase::Name(_name, weak) => {
232            // Assign the shared WeakScope to this ShapeCase::Name. All Name
233            // elements in the same namespace will share this WeakScope, so
234            // when we call set_weak() once after building the final map, all
235            // of them will see the update through the shared RwLock.
236            *weak = weak_scope.clone();
237        }
238
239        ShapeCase::Null
240        | ShapeCase::None
241        | ShapeCase::Unknown
242        | ShapeCase::Bool(_)
243        | ShapeCase::Int(_)
244        | ShapeCase::Float
245        | ShapeCase::String(_) => {}
246    }
247
248    shape
249}
250
251/// A [`WeakScope`] is a interior-mutable `RwLock` holding a map of weak
252/// shape references that can be used to resolve the [`Name`]s of
253/// [`ShapeCase::Name`] shapes. Though this struct is `pub`, its
254/// [`Ref<RwLock<WeakShapeMap>>`] parameter is not, so you cannot
255/// directly create your own [`WeakScope`] outside this crate.
256#[derive(Clone, Debug)]
257pub struct WeakScope(Ref<RwLock<Option<WeakShapeMap>>>);
258
259/// A reference-counted [`Ref`] to a map from shape names to [`RefWeak<Shape>`]
260/// references, referring weakly into some [`Namespace<Final>`]. The `Weak` in
261/// `WeakShapeMap` refers to the weakness of the `IndexMap` values, not the
262/// [`WeakShapeMap`] itself, which is a strong [`Ref`] (for easy cloning).
263type WeakShapeMap = RefWeak<IndexMap<String, Ref<Shape>>>;
264
265impl WeakScope {
266    #[must_use]
267    pub fn none() -> Self {
268        Self(Ref::new(RwLock::new(None)))
269    }
270
271    /// Upgrades a given [`Name`] in the [`WeakScope`] to a [`Shape`] if
272    /// possible.
273    #[must_use]
274    pub fn upgrade(&self, name: &Name) -> Option<Shape> {
275        match name.case() {
276            NameCase::Base(base_name) => {
277                if let Ok(guard) = self.0.read() {
278                    guard
279                        .as_ref()
280                        .and_then(RefWeak::upgrade)
281                        .and_then(|map| map.get(base_name).map(|shape| shape.as_ref().clone()))
282                } else {
283                    None
284                }
285            }
286            NameCase::Field(parent, field_name) => self
287                .upgrade(parent)
288                .map(|shape| shape.field(field_name, name.locations().cloned())),
289            NameCase::Item(parent, index) => self
290                .upgrade(parent)
291                .map(|shape| shape.item(*index, name.locations().cloned())),
292            NameCase::AnyField(parent) => self
293                .upgrade(parent)
294                .map(|shape| shape.any_field(name.locations().cloned())),
295            NameCase::AnyItem(parent) => self
296                .upgrade(parent)
297                .map(|shape| shape.any_item(name.locations().cloned())),
298            NameCase::Question(parent) => self
299                .upgrade(parent)
300                .map(|shape| shape.question(name.locations().cloned())),
301            NameCase::NotNone(parent) => self
302                .upgrade(parent)
303                .map(|shape| shape.not_none(name.locations().cloned())),
304        }
305    }
306
307    fn set_weak(&self, weak: WeakShapeMap) {
308        *self.0.write().unwrap() = Some(weak);
309    }
310}
311
312impl Namespace<Final> {
313    #[must_use]
314    pub fn finalize(&self) -> Namespace<Final> {
315        // Cloning the IndexMap<String, Ref<Shape>> of self.map when self is a
316        // Namespace<Final> is safe because it increments the reference count of
317        // each Ref<Shape> but does not change any Ref pointer addresses, so we
318        // do not have to re-propagate weak references.
319        self.clone()
320    }
321
322    /// Returns an iterator over the name-shape pairs in the finalized namespace.
323    pub fn iter(&self) -> impl Iterator<Item = (String, Shape)> + '_ {
324        self.map
325            .iter()
326            .map(|(name, shape)| (name.clone(), shape.as_ref().clone()))
327    }
328
329    /// You must have a finalized [`Namespace<Final>`] to use the `get` method
330    /// to look up shapes in the [`Namespace`].
331    ///
332    /// While [`Namespace<NotFinal>`] supports `has` and `insert` and other
333    /// [`Namespace`]-building methods, the `Namespace` has to be promoted to
334    /// [`Namespace<Final>`] before you can actually look up any names.
335    #[must_use]
336    pub fn get(&self, name: &str) -> Option<Shape> {
337        self.map.get(name).map(|shape| shape.as_ref().clone())
338    }
339
340    /// In case self.get returns a Rust None, this method returns a
341    /// [`Shape::none()`] that's named with the given `name`.
342    #[must_use]
343    pub fn get_or_none(&self, name: &str) -> Shape {
344        if let Some(shape) = self.get(name) {
345            shape
346        } else {
347            Shape::none().with_base_name(name, [])
348        }
349    }
350
351    /// In case self.get returns a Rust None, this method returns an `Unknown`
352    /// shape that's named with the given `name`.
353    #[must_use]
354    pub fn get_or_unknown(&self, name: &str) -> Shape {
355        if let Some(shape) = self.get(name) {
356            shape
357        } else {
358            Shape::unknown([]).with_base_name(name, [])
359        }
360    }
361}
362
363impl Shape {
364    /// Returns `true` if this [`Shape`] is known by the given base
365    /// name, among potentially multiple names it may have.
366    pub fn has_base_name(&self, base_name: impl Into<String>) -> bool {
367        let name = Name::base(base_name.into(), []);
368        self.meta.has_name(&name)
369    }
370
371    /// Assigns a base `name` to this [`Shape`], propagating derived
372    /// child names to all nested child shapes. This method is called
373    /// automatically when adding shapes to a [`Namespace`] (which
374    /// always requires providing a base name). The `locs` for this
375    /// operation should indicate where the `name` came from in source
376    /// code, if that information is available.
377    #[must_use]
378    pub fn with_base_name(
379        self,
380        name: impl Into<String>,
381        locs: impl IntoIterator<Item = Location>,
382    ) -> Self {
383        self.with_name(&Name::base(name, locs))
384    }
385
386    #[must_use]
387    pub(crate) fn with_name(mut self, name: &Name) -> Self {
388        if self.meta.has_name(name) {
389            // If the name is already present, we can assume its child names
390            // have also already been propagated.
391            return self;
392        }
393
394        Ref::make_mut(&mut self.meta).add_name(name.clone());
395
396        // Recursively propagate the name to child shapes
397        match Ref::make_mut(&mut self.case) {
398            ShapeCase::Object { fields, rest } => {
399                for (field_name, field_shape) in fields.iter_mut() {
400                    *field_shape = field_shape.clone().with_name(&name.field(field_name, []));
401                }
402                // Even if rest is None, it may still be useful for it to carry
403                // the ObjectType.** name from name.any_field([]).
404                *rest = rest.clone().with_name(&name.any_field([]));
405                self
406            }
407
408            ShapeCase::Array { prefix, tail } => {
409                for (index, prefix_shape) in prefix.iter_mut().enumerate() {
410                    *prefix_shape = prefix_shape.clone().with_name(&name.item(index, []));
411                }
412                // Even if tail is None, it may still be useful for it to carry
413                // the ArrayType.* name from name.any_item([]).
414                *tail = tail.clone().with_name(&name.any_item([]));
415                self
416            }
417
418            ShapeCase::One(shapes) => {
419                shapes.shapes =
420                    MergeSet::new(shapes.iter().map(|shape| shape.clone().with_name(name)));
421                self
422            }
423
424            ShapeCase::All(shapes) => {
425                shapes.shapes =
426                    MergeSet::new(shapes.iter().map(|shape| shape.clone().with_name(name)));
427                self
428            }
429
430            ShapeCase::Error(crate::Error { partial, .. }) => {
431                if let Some(partial_shape) = partial {
432                    *partial_shape = partial_shape.clone().with_name(name);
433                }
434                self
435            }
436
437            // Listing these cases explicitly rather than using a catch-all case
438            // to avoid missing ShapeCase variants added in the future.
439            ShapeCase::Bool(_)
440            | ShapeCase::String(_)
441            | ShapeCase::Int(_)
442            | ShapeCase::Float
443            | ShapeCase::Null
444            | ShapeCase::Unknown
445            | ShapeCase::None => {
446                // These shapes do not have children to propagate names to.
447                self
448            }
449            // Although some ShapeCase::Name(name, weak) variants have
450            // weakly-held children that can be traversed, those children
451            // typically correspond to immutable shapes retrieved from finalized
452            // namespaces, so we do not want to propagate child names into them.
453            // Also, ShapeCase::Name is the gateway to cyclic shape references,
454            // so if we avoid propagating names through the ::Name variant, name
455            // propagation won't have to worry about cycles.
456            ShapeCase::Name(_, _) => self,
457        }
458    }
459}
460
461#[derive(Clone, Eq)]
462pub struct Name {
463    case: Ref<NameCase>,
464    // Names can have Location metadata, but they cannot have other names as
465    // metadata, because names for names for names (and so on) is not a pattern
466    // we want to support.
467    locs: Ref<IndexSet<Location>>,
468}
469
470impl Debug for Name {
471    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
472        write!(f, "Name(")?;
473        <Name as Display>::fmt(self, f)?;
474        write!(f, ")")
475    }
476}
477
478impl Hash for Name {
479    fn hash<H: Hasher>(&self, state: &mut H) {
480        // Only self.case matters for hashing (a la Shape::hash), because only
481        // self.case matters for partial equality.
482        self.case.hash(state);
483    }
484}
485
486impl PartialEq for Name {
487    fn eq(&self, other: &Self) -> bool {
488        // Only self.case matters for equality, a la Shape::eq.
489        self.case == other.case
490    }
491}
492
493impl MetaMergeable for Name {
494    fn merge_meta_from(&mut self, other: &Self) -> bool {
495        let mut locs_changed = false;
496
497        if !Ref::ptr_eq(&self.locs, &other.locs) {
498            // Keep a backup Arc reference so we can restore the original Arc if
499            // it gets unnecessarily cloned/severed by Ref::make_mut, but only
500            // if the Ref::strong_count is greater than 1.
501            //
502            // Restoring the backup only matters when Ref::strong_count > 1
503            // because if the count is 1, Ref::make_mut is a no-op (needing no
504            // defensive cloning), and if we call self.locs.clone() in that case
505            // we end up forcing a needless defensive clone because the
506            // Ref::strong_count increases to 2 before we call Ref::make_mut.
507            let self_locs_backup = if Ref::strong_count(&self.locs) > 1 {
508                Some(self.locs.clone())
509            } else {
510                None
511            };
512
513            for locs in other.locs.as_ref() {
514                if !self.locs.contains(locs) {
515                    Ref::make_mut(&mut self.locs).insert(locs.clone());
516                    locs_changed = true;
517                }
518            }
519
520            if let (false, Some(backup)) = (locs_changed, self_locs_backup) {
521                self.locs = backup;
522            }
523        }
524
525        // One of the reasons it's important to "heal" unnecessarily cloned Arc
526        // references is that it gives us a better chance of hitting the
527        // Ref::ptr_eq case in the future. We can't unpay the cost of the
528        // unnecessary clone, but limiting memory fragmentation can save a
529        // significant number of future clones.
530        //
531        // Another way to prevent fragmentation would be to require
532        // MetaMergeable-implementing types also to implement a `fn
533        // should_merge_meta_from(&self, other: &Self) -> bool` method, so we
534        // could avoid doing any mutative merging if we detect in advance that
535        // no metadata will change. However, in the steady state, that approach
536        // would always require calling should_merge_meta_from, which is linear
537        // in the recursive size of self.case. By contrast, the current method
538        // sometimes makes unnecessary shallow clones of self.case via
539        // Ref::make_mut, but then falls into a steady state where the Ref data
540        // is exclusively owned and no more cloning needs to happen to permit
541        // future merges.
542        let case_changed = if Ref::ptr_eq(&self.case, &other.case) {
543            false
544        } else {
545            // Similar backup rationale as above, but for self.case.
546            let self_case_backup = if Ref::strong_count(&self.case) > 1 {
547                Some(self.case.clone())
548            } else {
549                None
550            };
551
552            let case_changed = Ref::make_mut(&mut self.case).merge_parent_meta_from(&other.case);
553
554            if let (false, Some(backup)) = (case_changed, self_case_backup) {
555                self.case = backup;
556            }
557
558            case_changed
559        };
560
561        locs_changed || case_changed
562    }
563}
564
565impl Name {
566    pub(crate) fn new(case: NameCase, locs: impl IntoIterator<Item = Location>) -> Self {
567        Self {
568            case: Ref::new(case),
569            locs: Ref::new(locs.into_iter().collect()),
570        }
571    }
572
573    pub(crate) fn base(name: impl Into<String>, locs: impl IntoIterator<Item = Location>) -> Self {
574        Self::new(NameCase::Base(name.into()), locs)
575    }
576
577    #[must_use]
578    pub fn base_shape_name(&self) -> &str {
579        match self.case() {
580            NameCase::Base(name) => name.as_str(),
581            NameCase::Field(parent, _)
582            | NameCase::AnyField(parent)
583            | NameCase::Item(parent, _)
584            | NameCase::Question(parent)
585            | NameCase::NotNone(parent)
586            | NameCase::AnyItem(parent) => parent.base_shape_name(),
587        }
588    }
589
590    pub(crate) fn field(
591        &self,
592        field: impl Into<String>,
593        locs: impl IntoIterator<Item = Location>,
594    ) -> Self {
595        Self::new(NameCase::Field(self.clone(), field.into()), locs)
596    }
597
598    pub(crate) fn any_field(&self, locs: impl IntoIterator<Item = Location>) -> Self {
599        Self::new(NameCase::AnyField(self.clone()), locs)
600    }
601
602    pub(crate) fn item(&self, index: usize, locs: impl IntoIterator<Item = Location>) -> Self {
603        Self::new(NameCase::Item(self.clone(), index), locs)
604    }
605
606    pub(crate) fn any_item(&self, locs: impl IntoIterator<Item = Location>) -> Self {
607        // Disallow consecutive .*.* name cases, which should collapse to just a
608        // single .* case.
609        if let NameCase::AnyItem(_) = self.case() {
610            self.clone()
611        } else {
612            Self::new(NameCase::AnyItem(self.clone()), locs)
613        }
614    }
615
616    pub(crate) fn question(&self, locs: impl IntoIterator<Item = Location>) -> Self {
617        // Disallow consecutive ??? operators, as their effect should be
618        // idempotent, and ?? is ambiguous with the nullish coalescing operator.
619        if let NameCase::Question(_) = self.case() {
620            self.clone()
621        } else {
622            Self::new(NameCase::Question(self.clone()), locs)
623        }
624    }
625
626    pub(crate) fn not_none(&self, locs: impl IntoIterator<Item = Location>) -> Self {
627        // Disallow consecutive !!! operators, as their effect should be
628        // idempotent.
629        if let NameCase::NotNone(_) = self.case() {
630            self.clone()
631        } else {
632            Self::new(NameCase::NotNone(self.clone()), locs)
633        }
634    }
635
636    /// Returns an immutable reference to the [`NameCase`] of this name.
637    #[must_use]
638    pub fn case(&self) -> &NameCase {
639        self.case.as_ref()
640    }
641
642    pub fn iter(&self) -> impl Iterator<Item = &Name> {
643        self.parent().map_or_else(
644            || vec![self].into_iter(),
645            |parent| {
646                let mut previous = parent.iter().collect::<Vec<_>>();
647                previous.push(self);
648                previous.into_iter()
649            },
650        )
651    }
652
653    #[allow(dead_code)]
654    pub(crate) fn iter_cases(&self) -> impl Iterator<Item = &NameCase> {
655        self.iter().map(Name::case)
656    }
657
658    /// Returns all Location metadata for this name and all its parents.
659    pub fn locations(&self) -> impl Iterator<Item = &Location> {
660        let mut unique_locs = self
661            .parent()
662            .map_or_else(IndexSet::new, |parent| parent.locations().collect());
663        unique_locs.extend(self.locs.iter());
664        unique_locs.into_iter()
665    }
666
667    pub(crate) fn locs(&self) -> impl Iterator<Item = &Location> {
668        self.locs.iter()
669    }
670
671    #[allow(dead_code)]
672    pub(crate) fn with_locs<'a>(&self, locs: impl IntoIterator<Item = &'a Location>) -> Self {
673        let mut clone = self.clone();
674        clone.add_locs(locs);
675        clone
676    }
677
678    #[allow(dead_code)]
679    pub(crate) fn add_locs<'a>(&mut self, locs: impl IntoIterator<Item = &'a Location>) -> bool {
680        let mut changed = false;
681        for loc in locs {
682            if !self.locs.contains(loc) {
683                Ref::make_mut(&mut self.locs).insert(loc.clone());
684                changed = true;
685            }
686        }
687        changed
688    }
689
690    pub(crate) fn parent(&self) -> Option<&Name> {
691        self.case.parent()
692    }
693}
694
695#[derive(Clone, Debug, PartialEq, Eq, Hash)]
696pub enum NameCase {
697    Base(String),
698    Field(Name, String),
699    AnyField(Name),
700    Item(Name, usize),
701    AnyItem(Name),
702    Question(Name),
703    NotNone(Name),
704}
705
706impl NameCase {
707    pub(crate) fn parent(&self) -> Option<&Name> {
708        match self {
709            Self::Base(_) => None,
710            Self::Field(parent, _)
711            | Self::AnyField(parent)
712            | Self::Item(parent, _)
713            | Self::Question(parent)
714            | Self::NotNone(parent)
715            | Self::AnyItem(parent) => Some(parent),
716        }
717    }
718
719    pub(crate) fn parent_mut(&mut self) -> Option<&mut Name> {
720        match self {
721            Self::Base(_) => None,
722            Self::Field(parent, _)
723            | Self::AnyField(parent)
724            | Self::Item(parent, _)
725            | Self::Question(parent)
726            | Self::NotNone(parent)
727            | Self::AnyItem(parent) => Some(parent),
728        }
729    }
730
731    pub(crate) fn merge_parent_meta_from(&mut self, other: &NameCase) -> bool {
732        match (self.parent_mut(), other.parent()) {
733            (Some(self_parent), Some(other_parent))
734                if self_parent.case() == other_parent.case() =>
735            {
736                // If (and only if) the parent names are the same, then we can
737                // merge their metadata.
738                self_parent.merge_meta_from(other_parent)
739            }
740
741            // This happens normally with NameCase::Base, which can have
742            // metadata but has no children with metadata to be merged. However,
743            // it is covered by the _ => false catch-all case, so we keep Clippy
744            // happy by commenting it out.
745            // (None, None) => false,
746
747            // If the parent names are different (or both None; see above), we
748            // cannot merge metadata.
749            _ => false,
750        }
751    }
752}
753
754impl Display for Name {
755    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
756        if let Some(parent) = self.parent() {
757            write!(f, "{parent}")?;
758        }
759        write!(f, "{}", self.case())
760    }
761}
762
763impl Display for NameCase {
764    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
765        match self {
766            Self::Base(name) => write!(f, "{name}"),
767            Self::Field(_parent, field) => write!(f, ".{}", quote_non_identifier(field)),
768            Self::AnyField(_parent) => write!(f, ".**"),
769            Self::Item(_parent, index) => write!(f, ".{index}"),
770            Self::AnyItem(_parent) => write!(f, ".*"),
771            Self::Question(_parent) => write!(f, "?"),
772            Self::NotNone(_parent) => write!(f, "!"),
773        }
774    }
775}
776
777#[cfg(test)]
778mod tests {
779    use super::*;
780    use crate::Shape;
781
782    #[test]
783    fn test_name() {
784        let args = Name::base("$args", []);
785        assert_eq!(args.to_string(), "$args");
786
787        let args_limit = args.field("limit", []);
788        assert_eq!(args_limit.to_string(), "$args.limit");
789
790        let args_object_any_field = args.field("object", []).any_field([]);
791        assert_eq!(args_object_any_field.to_string(), "$args.object.**");
792
793        let args_array_item_0 = args.field("array", []).item(0, []);
794        assert_eq!(args_array_item_0.to_string(), "$args.array.0");
795
796        let args_array_any_item = args.field("array", []).any_item([]);
797        assert_eq!(args_array_any_item.to_string(), "$args.array.*");
798
799        let args_array_name = args_array_any_item.field("name", []);
800        assert_eq!(args_array_name.to_string(), "$args.array.*.name");
801    }
802
803    #[test]
804    fn test_shapes_with_names() {
805        let args = Shape::record(
806            {
807                let mut fields = Shape::empty_map();
808                fields.insert("value".to_string(), Shape::int_value(123, []));
809                fields.insert("array".to_string(), Shape::list(Shape::string([]), []));
810                fields
811            },
812            [],
813        )
814        .with_base_name("$args", []);
815
816        let args_value = args.field("value", []);
817        assert_eq!(
818            args_value.pretty_print_with_names(),
819            "123 (aka $args.value)"
820        );
821
822        let list_of_args_value = Shape::list(args_value.clone(), []);
823        assert_eq!(
824            list_of_args_value.pretty_print_with_names(),
825            "List<123 (aka $args.value)>"
826        );
827        assert_eq!(
828            list_of_args_value
829                .with_base_name("TheList", [])
830                .pretty_print_with_names(),
831            "List<123 (aka $args.value, TheList.*)> (aka TheList)"
832        );
833
834        let record_of_args_value = Shape::record(
835            {
836                let mut fields = Shape::empty_map();
837                fields.insert("limit".to_string(), Shape::int([]));
838                fields.insert("value".to_string(), args_value.clone());
839                fields
840            },
841            [],
842        );
843        assert_eq!(
844            record_of_args_value.pretty_print_with_names(),
845            "{ limit: Int, value: 123 (aka $args.value) }"
846        );
847        assert_eq!(
848            record_of_args_value
849                .with_base_name("SomeRecord", [])
850                .pretty_print_with_names(),
851            r#"{
852  limit: Int (aka SomeRecord.limit),
853  value: 123 (aka $args.value, SomeRecord.value),
854} (aka SomeRecord)"#,
855        );
856
857        let args_value_any_item = args_value.any_item([]);
858        assert_eq!(
859            args_value_any_item.pretty_print_with_names(),
860            // TODO These names seem a bit redundant?
861            "123 (aka $args.value)"
862        );
863
864        let args_any_field = args.any_field([]);
865        assert_eq!(
866            args_any_field.pretty_print_with_names(),
867            r#"One<
868  123 (aka $args.value),
869  List<String (aka $args.array.*)> (aka $args.array),
870>"#
871        );
872
873        let args_array_item_2 = args.field("array", []).item(2, []);
874        assert_eq!(
875            args_array_item_2.pretty_print_with_names(),
876            r#"One<
877  String (aka $args.array.*, $args.array.2),
878  None (aka $args.array.2),
879> (aka $args.array.2)"#
880        );
881    }
882
883    #[test]
884    fn test_name_merging() {
885        let record = Shape::record(
886            {
887                let mut fields = Shape::empty_map();
888
889                fields.insert("a".to_string(), Shape::int([]));
890                fields.insert("b".to_string(), Shape::string([]));
891                fields.insert("c".to_string(), Shape::bool([]));
892                fields.insert("d".to_string(), Shape::null([]));
893
894                fields.insert("A".to_string(), Shape::int([]));
895                fields.insert("B".to_string(), Shape::string([]));
896                fields.insert("C".to_string(), Shape::bool([]));
897                fields.insert("D".to_string(), Shape::null([]));
898
899                fields.insert("xyz".to_string(), Shape::list(Shape::string([]), []));
900
901                fields
902            },
903            [],
904        )
905        .with_base_name("Record", []);
906
907        assert_eq!(
908            record.pretty_print(),
909            r#"{
910  A: Int,
911  B: String,
912  C: Bool,
913  D: null,
914  a: Int,
915  b: String,
916  c: Bool,
917  d: null,
918  xyz: List<String>,
919}"#
920        );
921
922        assert_eq!(
923            record.pretty_print_with_names(),
924            r#"{
925  A: Int (aka Record.A),
926  B: String (aka Record.B),
927  C: Bool (aka Record.C),
928  D: null (aka Record.D),
929  a: Int (aka Record.a),
930  b: String (aka Record.b),
931  c: Bool (aka Record.c),
932  d: null (aka Record.d),
933  xyz: List<String (aka Record.xyz.*)> (aka Record.xyz),
934} (aka Record)"#
935        );
936
937        let record_any_field = record.any_field([]);
938
939        assert_eq!(
940            record_any_field.pretty_print(),
941            "One<Int, String, Bool, null, List<String>>"
942        );
943
944        assert_eq!(
945            record_any_field.pretty_print_with_names(),
946            r#"One<
947  Int (aka Record.a, Record.A),
948  String (aka Record.b, Record.B),
949  Bool (aka Record.c, Record.C),
950  null (aka Record.d, Record.D),
951  List<String (aka Record.xyz.*)> (aka Record.xyz),
952>"#,
953        );
954
955        let case_insensitive_a = Shape::one([record.field("a", []), record.field("A", [])], []);
956
957        assert_eq!(case_insensitive_a.pretty_print(), "Int");
958
959        assert_eq!(
960            case_insensitive_a.pretty_print_with_names(),
961            "Int (aka Record.a, Record.A)",
962        );
963    }
964}