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#[derive(Debug, PartialEq, Eq, Clone)]
33pub struct Namespace<T = NotFinal> {
34 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 #[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 #[must_use]
69 pub fn has(&self, name: &str) -> bool {
70 self.map.contains_key(name)
71 }
72
73 pub fn names(&self) -> impl Iterator<Item = &str> + '_ {
75 self.map.keys().map(String::as_str)
76 }
77
78 #[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 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 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 .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 #[must_use]
137 pub fn finalize(&self) -> Namespace<Final> {
138 let weak_scope = WeakScope::none();
142
143 let final_map = Ref::new(
144 self.map
145 .iter()
146 .map(|(name, shape)| {
147 let named_shape = shape.as_ref().clone().with_base_name(name, []);
150 (
151 name.clone(),
152 Ref::new(ensure_deep_exclusive_name_ownership(
159 named_shape,
160 &weak_scope,
161 )),
162 )
163 })
164 .collect::<IndexMap<_, _>>(),
165 );
166
167 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 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 *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#[derive(Clone, Debug)]
257pub struct WeakScope(Ref<RwLock<Option<WeakShapeMap>>>);
258
259type 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 #[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 self.clone()
320 }
321
322 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 #[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 #[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 #[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 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 #[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 return self;
392 }
393
394 Ref::make_mut(&mut self.meta).add_name(name.clone());
395
396 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 *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 *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 ShapeCase::Bool(_)
440 | ShapeCase::String(_)
441 | ShapeCase::Int(_)
442 | ShapeCase::Float
443 | ShapeCase::Null
444 | ShapeCase::Unknown
445 | ShapeCase::None => {
446 self
448 }
449 ShapeCase::Name(_, _) => self,
457 }
458 }
459}
460
461#[derive(Clone, Eq)]
462pub struct Name {
463 case: Ref<NameCase>,
464 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 self.case.hash(state);
483 }
484}
485
486impl PartialEq for Name {
487 fn eq(&self, other: &Self) -> bool {
488 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 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 let case_changed = if Ref::ptr_eq(&self.case, &other.case) {
543 false
544 } else {
545 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 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 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 if let NameCase::NotNone(_) = self.case() {
630 self.clone()
631 } else {
632 Self::new(NameCase::NotNone(self.clone()), locs)
633 }
634 }
635
636 #[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 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 self_parent.merge_meta_from(other_parent)
739 }
740
741 _ => 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 "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}