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#[derive(Debug, PartialEq, Eq, Clone)]
32pub struct Namespace<T = NotFinal> {
33 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 #[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 #[must_use]
68 pub fn has(&self, name: &str) -> bool {
69 self.map.contains_key(name)
70 }
71
72 pub fn names(&self) -> impl Iterator<Item = &str> + '_ {
74 self.map.keys().map(String::as_str)
75 }
76
77 #[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 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 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 .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 #[must_use]
136 pub fn finalize(&self) -> Namespace<Final> {
137 let weak_scope = WeakScope::none();
141
142 let final_map = Ref::new(
143 self.map
144 .iter()
145 .map(|(name, shape)| {
146 let named_shape = shape.as_ref().clone().with_base_name(name, []);
149 (
150 name.clone(),
151 Ref::new(ensure_deep_exclusive_name_ownership(
158 named_shape,
159 &weak_scope,
160 )),
161 )
162 })
163 .collect::<IndexMap<_, _>>(),
164 );
165
166 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 return shape;
189 }
190
191 match &mut Ref::make_mut(&mut shape.case).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 *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#[derive(Clone, Debug)]
250pub struct WeakScope(Ref<RwLock<Option<WeakShapeMap>>>);
251
252type 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 #[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 self.clone()
313 }
314
315 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 #[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 #[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 #[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 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 #[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 return self;
385 }
386
387 Ref::make_mut(&mut self.meta).add_name(name.clone());
388
389 match &mut Ref::make_mut(&mut self.case).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 *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 *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 ShapeCase::Bool(_)
426 | ShapeCase::String(_)
427 | ShapeCase::Int(_)
428 | ShapeCase::Float
429 | ShapeCase::Null
430 | ShapeCase::Unknown
431 | ShapeCase::None => {
432 self
434 }
435 ShapeCase::Name(_, _) => self,
443 }
444 }
445}
446
447#[derive(Clone, Eq)]
448pub struct Name {
449 case: Ref<NameCase>,
450 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 self.case.hash(state);
469 }
470}
471
472impl PartialEq for Name {
473 fn eq(&self, other: &Self) -> bool {
474 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 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 let case_changed = if Ref::ptr_eq(&self.case, &other.case) {
529 false
530 } else {
531 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 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 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 if let NameCase::NotNone(_) = self.case() {
616 self.clone()
617 } else {
618 Self::new(NameCase::NotNone(self.clone()), locs)
619 }
620 }
621
622 #[must_use]
624 pub fn case(&self) -> &NameCase {
625 self.case.as_ref()
626 }
627
628 pub fn iter(&self) -> impl Iterator<Item = &Name> {
631 let mut names = vec![self];
632 let mut current = self;
633 while let Some(parent) = current.parent() {
634 names.push(parent);
635 current = parent;
636 }
637 names.into_iter().rev()
638 }
639
640 #[allow(dead_code)]
641 pub(crate) fn iter_cases(&self) -> impl Iterator<Item = &NameCase> {
642 self.iter().map(Name::case)
643 }
644
645 pub fn locations(&self) -> impl Iterator<Item = &Location> {
649 let unique_locs: IndexSet<&Location> = self.iter().flat_map(Name::locs).collect();
650 unique_locs.into_iter()
651 }
652
653 pub fn locs(&self) -> impl Iterator<Item = &Location> {
657 self.locs.iter()
658 }
659
660 #[allow(dead_code)]
661 pub(crate) fn with_locs<'a>(&self, locs: impl IntoIterator<Item = &'a Location>) -> Self {
662 let mut clone = self.clone();
663 clone.add_locs(locs);
664 clone
665 }
666
667 #[allow(dead_code)]
668 pub(crate) fn add_locs<'a>(&mut self, locs: impl IntoIterator<Item = &'a Location>) -> bool {
669 let mut changed = false;
670 for loc in locs {
671 if !self.locs.contains(loc) {
672 Ref::make_mut(&mut self.locs).insert(loc.clone());
673 changed = true;
674 }
675 }
676 changed
677 }
678
679 pub(crate) fn parent(&self) -> Option<&Name> {
680 self.case.parent()
681 }
682}
683
684#[derive(Clone, Debug, PartialEq, Eq, Hash)]
685pub enum NameCase {
686 Base(String),
687 Field(Name, String),
688 AnyField(Name),
689 Item(Name, usize),
690 AnyItem(Name),
691 Question(Name),
692 NotNone(Name),
693}
694
695impl NameCase {
696 pub(crate) fn parent(&self) -> Option<&Name> {
697 match self {
698 Self::Base(_) => None,
699 Self::Field(parent, _)
700 | Self::AnyField(parent)
701 | Self::Item(parent, _)
702 | Self::Question(parent)
703 | Self::NotNone(parent)
704 | Self::AnyItem(parent) => Some(parent),
705 }
706 }
707
708 pub(crate) fn parent_mut(&mut self) -> Option<&mut Name> {
709 match self {
710 Self::Base(_) => None,
711 Self::Field(parent, _)
712 | Self::AnyField(parent)
713 | Self::Item(parent, _)
714 | Self::Question(parent)
715 | Self::NotNone(parent)
716 | Self::AnyItem(parent) => Some(parent),
717 }
718 }
719
720 pub(crate) fn merge_parent_meta_from(&mut self, other: &NameCase) -> bool {
721 match (self.parent_mut(), other.parent()) {
722 (Some(self_parent), Some(other_parent))
723 if self_parent.case() == other_parent.case() =>
724 {
725 self_parent.merge_meta_from(other_parent)
728 }
729
730 _ => false,
739 }
740 }
741}
742
743impl Display for Name {
744 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
745 if let Some(parent) = self.parent() {
746 write!(f, "{parent}")?;
747 }
748 write!(f, "{}", self.case())
749 }
750}
751
752impl Display for NameCase {
753 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
754 match self {
755 Self::Base(name) => write!(f, "{name}"),
756 Self::Field(_parent, field) => write!(f, ".{}", quote_non_identifier(field)),
757 Self::AnyField(_parent) => write!(f, ".**"),
758 Self::Item(_parent, index) => write!(f, ".{index}"),
759 Self::AnyItem(_parent) => write!(f, ".*"),
760 Self::Question(_parent) => write!(f, "?"),
761 Self::NotNone(_parent) => write!(f, "!"),
762 }
763 }
764}
765
766#[cfg(test)]
767mod tests {
768 use super::*;
769 use crate::Shape;
770
771 #[test]
772 fn test_name() {
773 let args = Name::base("$args", []);
774 assert_eq!(args.to_string(), "$args");
775
776 let args_limit = args.field("limit", []);
777 assert_eq!(args_limit.to_string(), "$args.limit");
778
779 let args_object_any_field = args.field("object", []).any_field([]);
780 assert_eq!(args_object_any_field.to_string(), "$args.object.**");
781
782 let args_array_item_0 = args.field("array", []).item(0, []);
783 assert_eq!(args_array_item_0.to_string(), "$args.array.0");
784
785 let args_array_any_item = args.field("array", []).any_item([]);
786 assert_eq!(args_array_any_item.to_string(), "$args.array.*");
787
788 let args_array_name = args_array_any_item.field("name", []);
789 assert_eq!(args_array_name.to_string(), "$args.array.*.name");
790 }
791
792 #[test]
793 fn test_name_iter_and_locations_order() {
794 let source = crate::location::SourceId::new("name.json");
795 let loc_a = source.location(0..1);
796 let loc_b = source.location(1..2);
797 let loc_c = source.location(2..3);
798
799 let base = Name::base("$root", [loc_a.clone()]);
800 let b = base.field("b", [loc_b.clone(), loc_a.clone()]);
801 let c = b.field("c", [loc_c.clone(), loc_b.clone()]);
802 let c_item = c.item(0, []);
803
804 assert_eq!(
805 c_item.iter().map(ToString::to_string).collect::<Vec<_>>(),
806 vec!["$root", "$root.b", "$root.b.c", "$root.b.c.0"],
807 );
808 assert_eq!(base.iter().collect::<Vec<_>>(), vec![&base]);
809
810 assert_eq!(
813 c_item.locations().collect::<Vec<_>>(),
814 vec![&loc_a, &loc_b, &loc_c],
815 );
816 assert_eq!(c.locs().collect::<Vec<_>>(), vec![&loc_c, &loc_b]);
817 }
818
819 #[test]
820 fn test_shapes_with_names() {
821 let args = Shape::closed_record(
822 {
823 let mut fields = Shape::empty_map();
824 fields.insert("value".to_string(), Shape::int_value(123, []));
825 fields.insert("array".to_string(), Shape::list(Shape::string([]), []));
826 fields
827 },
828 [],
829 )
830 .with_base_name("$args", []);
831
832 let args_value = args.field("value", []);
833 assert_eq!(
834 args_value.pretty_print_with_names(),
835 "123 (aka $args.value)"
836 );
837
838 let list_of_args_value = Shape::list(args_value.clone(), []);
839 assert_eq!(
840 list_of_args_value.pretty_print_with_names(),
841 "[...123 (aka $args.value)]"
842 );
843 assert_eq!(
844 list_of_args_value
845 .with_base_name("TheList", [])
846 .pretty_print_with_names(),
847 "[...123 (aka $args.value, TheList.*)] (aka TheList)"
848 );
849
850 let record_of_args_value = Shape::closed_record(
851 {
852 let mut fields = Shape::empty_map();
853 fields.insert("limit".to_string(), Shape::int([]));
854 fields.insert("value".to_string(), args_value.clone());
855 fields
856 },
857 [],
858 );
859 assert_eq!(
860 record_of_args_value.pretty_print_with_names(),
861 "{ limit: Int, value: 123 (aka $args.value) }"
862 );
863 assert_eq!(
864 record_of_args_value
865 .with_base_name("SomeRecord", [])
866 .pretty_print_with_names(),
867 r#"{
868 limit: Int (aka SomeRecord.limit),
869 value: 123 (aka $args.value, SomeRecord.value),
870} (aka SomeRecord)"#,
871 );
872
873 let args_value_any_item = args_value.any_item([]);
874 assert_eq!(
875 args_value_any_item.pretty_print_with_names(),
876 "123 (aka $args.value)"
878 );
879
880 let args_any_field = args.any_field([]);
881 assert_eq!(
882 args_any_field.pretty_print_with_names(),
883 r#"One<
884 123 (aka $args.value),
885 [...String (aka $args.array.*)] (aka $args.array),
886>"#
887 );
888
889 let args_array_item_2 = args.field("array", []).item(2, []);
890 assert_eq!(
891 args_array_item_2.pretty_print_with_names(),
892 r#"One<
893 String (aka $args.array.*, $args.array.2),
894 None (aka $args.array.2),
895> (aka $args.array.2)"#
896 );
897 }
898
899 #[test]
900 fn test_name_merging() {
901 let record = Shape::closed_record(
902 {
903 let mut fields = Shape::empty_map();
904
905 fields.insert("a".to_string(), Shape::int([]));
906 fields.insert("b".to_string(), Shape::string([]));
907 fields.insert("c".to_string(), Shape::bool([]));
908 fields.insert("d".to_string(), Shape::null([]));
909
910 fields.insert("A".to_string(), Shape::int([]));
911 fields.insert("B".to_string(), Shape::string([]));
912 fields.insert("C".to_string(), Shape::bool([]));
913 fields.insert("D".to_string(), Shape::null([]));
914
915 fields.insert("xyz".to_string(), Shape::list(Shape::string([]), []));
916
917 fields
918 },
919 [],
920 )
921 .with_base_name("Record", []);
922
923 assert_eq!(
924 record.pretty_print(),
925 r#"{
926 A: Int,
927 B: String,
928 C: Bool,
929 D: null,
930 a: Int,
931 b: String,
932 c: Bool,
933 d: null,
934 xyz: [...String],
935}"#
936 );
937
938 assert_eq!(
939 record.pretty_print_with_names(),
940 r#"{
941 A: Int (aka Record.A),
942 B: String (aka Record.B),
943 C: Bool (aka Record.C),
944 D: null (aka Record.D),
945 a: Int (aka Record.a),
946 b: String (aka Record.b),
947 c: Bool (aka Record.c),
948 d: null (aka Record.d),
949 xyz: [...String (aka Record.xyz.*)] (aka Record.xyz),
950} (aka Record)"#
951 );
952
953 let record_any_field = record.any_field([]);
954
955 assert_eq!(
956 record_any_field.pretty_print(),
957 "One<Int, String, Bool, null, [...String]>"
958 );
959
960 assert_eq!(
961 record_any_field.pretty_print_with_names(),
962 r#"One<
963 Int (aka Record.a, Record.A),
964 String (aka Record.b, Record.B),
965 Bool (aka Record.c, Record.C),
966 null (aka Record.d, Record.D),
967 [...String (aka Record.xyz.*)] (aka Record.xyz),
968>"#,
969 );
970
971 let case_insensitive_a = Shape::one([record.field("a", []), record.field("A", [])], []);
972
973 assert_eq!(case_insensitive_a.pretty_print(), "Int");
974
975 assert_eq!(
976 case_insensitive_a.pretty_print_with_names(),
977 "Int (aka Record.a, Record.A)",
978 );
979 }
980}