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