ipld_schema/
schema.rs

1#![allow(dead_code)]
2
3use std::fmt;
4
5use proptest::{collection::btree_map, prelude::*};
6use serde::{Deserialize, Serialize};
7
8#[cfg(feature = "fast-test")]
9const DEFAULT_SIZE_RANGE: std::ops::RangeInclusive<usize> = 0..=10;
10#[cfg(not(feature = "fast-test"))]
11const DEFAULT_SIZE_RANGE: std::ops::RangeInclusive<usize> = 0..=100;
12
13type Int = i64;
14type Float = f64;
15type Map<K, V> = std::collections::BTreeMap<K, V>;
16
17// TODO: revisit public API
18
19// TODO: docs
20
21#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
22struct Null;
23
24#[derive(
25    Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, test_strategy::Arbitrary,
26)]
27pub(crate) struct TypeName(#[strategy("[A-Z][a-z0-9_]*")] String);
28
29#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
30struct SchemaMap(
31    // TODO: increase size range
32    #[strategy(btree_map(any::<TypeName>(), any::<Type>(), DEFAULT_SIZE_RANGE))]
33    Map<TypeName, Type>,
34);
35
36#[derive(
37    Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, test_strategy::Arbitrary,
38)]
39pub(crate) struct AdvancedDataLayoutName(String);
40
41#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, test_strategy::Arbitrary)]
42pub(crate) struct AdvancedDataLayoutMap(
43    #[strategy(Just(Map::new()))] Map<AdvancedDataLayoutName, AdvancedDataLayout>,
44);
45
46#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
47#[serde(rename_all = "camelCase")]
48#[derive(test_strategy::Arbitrary)]
49pub(crate) struct Schema {
50    types: SchemaMap,
51    #[serde(default, skip_serializing_if = "is_default")]
52    advanced: AdvancedDataLayoutMap,
53}
54
55#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
56#[serde(tag = "kind", rename_all = "lowercase")]
57#[derive(test_strategy::Arbitrary)]
58// TODO: can't handle some variants until fields referred to by representation exist and field orders matches set of fields
59pub(crate) enum Type {
60    Bool(TypeBool),
61    String(TypeString),
62    Bytes(TypeBytes),
63    Int(TypeInt),
64    Float(TypeFloat),
65    Map(TypeMap),
66    List(TypeList),
67    Link(TypeLink),
68    Union(TypeUnion),
69    #[weight(0)]
70    Struct(TypeStruct),
71    #[weight(0)]
72    Enum(TypeEnum),
73    Copy(TypeCopy),
74}
75
76/*
77#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
78#[serde(rename_all = "lowercase")]
79#[derive(test_strategy::Arbitrary)]
80pub(crate) enum TypeKind {
81    Bool,
82    String,
83    Bytes,
84    Int,
85    Float,
86    Map,
87    List,
88    Link,
89    Union,
90    Struct,
91    Enum,
92}
93*/
94
95#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
96#[serde(rename_all = "lowercase")]
97#[derive(test_strategy::Arbitrary)]
98pub(crate) enum RepresentationKind {
99    Bool,
100    String,
101    Bytes,
102    Int,
103    Float,
104    Map,
105    List,
106    Link,
107}
108
109#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
110#[serde(untagged)]
111#[derive(test_strategy::Arbitrary)]
112pub(crate) enum AnyScalar {
113    Bool(bool),
114    String(String),
115    Bytes(Vec<u8>),
116    Int(Int),
117    Float(Float),
118}
119
120#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
121struct AdvancedDataLayout;
122
123#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
124pub(crate) struct TypeBool;
125
126#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
127pub(crate) struct TypeString;
128
129#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
130pub(crate) struct TypeBytes {
131    representation: BytesRepresentation,
132}
133
134#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
135#[serde(rename_all = "lowercase")]
136#[derive(test_strategy::Arbitrary)]
137// TODO: generate all variants
138enum BytesRepresentation {
139    Bytes(bytes_representation::Bytes),
140    #[weight(0)]
141    Advanced(AdvancedDataLayoutName),
142}
143
144mod bytes_representation {
145    use serde::{Deserialize, Serialize};
146
147    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
148    pub(crate) struct Bytes;
149}
150
151impl Default for BytesRepresentation {
152    fn default() -> Self {
153        Self::Bytes(bytes_representation::Bytes)
154    }
155}
156
157#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
158pub(crate) struct TypeInt;
159
160#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
161pub(crate) struct TypeFloat;
162
163fn is_default<D: Default + PartialEq>(d: &D) -> bool {
164    *d == D::default()
165}
166
167#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
168#[serde(rename_all = "camelCase")]
169#[derive(test_strategy::Arbitrary)]
170pub(crate) struct TypeMap {
171    key_type: TypeName,
172
173    value_type: TypeTerm,
174
175    #[serde(default, skip_serializing_if = "is_default")]
176    value_nullable: bool,
177
178    #[serde(default, skip_serializing_if = "is_default")]
179    representation: MapRepresentation,
180}
181
182#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
183#[serde(rename_all = "lowercase")]
184#[derive(test_strategy::Arbitrary)]
185// TODO: generate all variants
186enum MapRepresentation {
187    Map(map_representation::Map),
188    StringPairs(map_representation::StringPairs),
189    ListPairs(map_representation::ListPairs),
190    #[weight(0)]
191    Advanced(AdvancedDataLayoutName),
192}
193
194mod map_representation {
195    use serde::{Deserialize, Serialize};
196
197    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
198    pub(crate) struct Map;
199
200    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
201    #[serde(rename_all = "camelCase")]
202    #[derive(test_strategy::Arbitrary)]
203    pub(crate) struct StringPairs {
204        #[strategy("[^\"]+")]
205        pub(crate) inner_delim: String,
206
207        #[strategy("[^\"]+")]
208        pub(crate) entry_delim: String,
209    }
210
211    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
212    pub(crate) struct ListPairs;
213}
214
215impl Default for MapRepresentation {
216    fn default() -> Self {
217        Self::Map(map_representation::Map)
218    }
219}
220
221#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
222#[serde(rename_all = "camelCase")]
223#[derive(test_strategy::Arbitrary)]
224pub(crate) struct TypeList {
225    value_type: TypeTerm,
226
227    #[serde(default, skip_serializing_if = "is_default")]
228    value_nullable: bool,
229
230    #[serde(default, skip_serializing_if = "is_default")]
231    representation: ListRepresentation,
232}
233
234#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
235enum ListRepresentation {
236    List(list_representation::List),
237    #[weight(0)]
238    Advanced(AdvancedDataLayoutName),
239}
240
241mod list_representation {
242    use serde::{Deserialize, Serialize};
243
244    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
245    pub(crate) struct List;
246}
247
248impl Default for ListRepresentation {
249    fn default() -> Self {
250        Self::List(list_representation::List)
251    }
252}
253
254#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
255#[serde(rename_all = "camelCase")]
256#[derive(test_strategy::Arbitrary)]
257pub(crate) struct TypeLink {
258    #[strategy("[A-Z][a-z0-9_]*")]
259    expected_type: String,
260}
261
262impl Default for TypeLink {
263    fn default() -> Self {
264        Self {
265            expected_type: "Any".to_string(),
266        }
267    }
268}
269
270#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
271#[serde(rename_all = "camelCase")]
272#[derive(test_strategy::Arbitrary)]
273pub(crate) struct TypeUnion {
274    representation: UnionRepresentation,
275}
276
277#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
278#[serde(rename_all = "lowercase")]
279#[derive(test_strategy::Arbitrary)]
280enum UnionRepresentation {
281    Kinded(union_representation::Kinded),
282    Keyed(union_representation::Keyed),
283    Envelope(union_representation::Envelope),
284    Inline(union_representation::Inline),
285    BytePrefix(union_representation::BytePrefix),
286}
287
288mod union_representation {
289    use super::{Map, RepresentationKind, TypeName, DEFAULT_SIZE_RANGE};
290    use proptest::{collection::btree_map, prelude::any};
291    use serde::{Deserialize, Serialize};
292
293    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
294    pub(crate) struct Kinded(pub(crate) Map<RepresentationKind, TypeName>);
295
296    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
297    pub(crate) struct Keyed(
298        #[strategy(btree_map("[^\"]*", any::<TypeName>(), DEFAULT_SIZE_RANGE))]
299        pub(crate)  Map<String, TypeName>,
300    );
301
302    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
303    #[serde(rename_all = "camelCase")]
304    #[derive(test_strategy::Arbitrary)]
305    pub(crate) struct Envelope {
306        #[strategy("[^\"]*")]
307        pub(crate) discriminant_key: String,
308
309        #[strategy("[^\"]*")]
310        pub(crate) content_key: String,
311
312        #[strategy(btree_map("[^\"]*", any::<TypeName>(), DEFAULT_SIZE_RANGE))]
313        pub(crate) discriminant_table: Map<String, TypeName>,
314    }
315
316    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
317    #[serde(rename_all = "camelCase")]
318    #[derive(test_strategy::Arbitrary)]
319    pub(crate) struct Inline {
320        #[strategy("[^\"]*")]
321        pub(crate) discriminant_key: String,
322
323        #[strategy(btree_map("[^\"]*", any::<TypeName>(), DEFAULT_SIZE_RANGE))]
324        pub(crate) discriminant_table: Map<String, TypeName>,
325    }
326
327    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
328    #[serde(rename_all = "camelCase")]
329    #[derive(test_strategy::Arbitrary)]
330    pub(crate) struct BytePrefix {
331        #[strategy(btree_map(any::<TypeName>(), any::<u8>(), DEFAULT_SIZE_RANGE))]
332        pub(crate) discriminant_table: Map<TypeName, u8>,
333    }
334}
335
336#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
337#[serde(rename_all = "camelCase")]
338#[derive(test_strategy::Arbitrary)]
339pub(crate) struct TypeStruct {
340    // TODO: increase size range
341    #[strategy(btree_map(any::<FieldName>(), any::<StructField>(), DEFAULT_SIZE_RANGE))]
342    fields: Map<FieldName, StructField>,
343    representation: StructRepresentation,
344}
345
346#[derive(
347    Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, test_strategy::Arbitrary,
348)]
349pub(crate) struct FieldName(#[strategy("[a-zA-Z0-9_]+")] String);
350
351#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
352#[serde(rename_all = "camelCase")]
353#[derive(test_strategy::Arbitrary)]
354struct StructField {
355    r#type: TypeTerm,
356
357    #[serde(default, skip_serializing_if = "is_default")]
358    optional: bool,
359
360    #[serde(default, skip_serializing_if = "is_default")]
361    nullable: bool,
362}
363
364#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
365#[serde(untagged)]
366#[derive(test_strategy::Arbitrary)]
367// TODO: allow all variants; may require proptest's prop_recursive strategy
368pub(crate) enum TypeTerm {
369    TypeName(TypeName),
370    #[weight(0)]
371    InlineDefn(Box<InlineDefn>),
372}
373
374#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
375#[serde(tag = "kind", rename_all = "lowercase")]
376#[derive(test_strategy::Arbitrary)]
377pub(crate) enum InlineDefn {
378    Map(TypeMap),
379    List(TypeList),
380}
381
382#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
383#[serde(rename_all = "lowercase")]
384#[derive(test_strategy::Arbitrary)]
385// TODO: generate all variants
386// TODO: all FieldNames generated here should correspond to one of TypeStruct's fields
387enum StructRepresentation {
388    Map(struct_representation::Map),
389    Tuple(struct_representation::Tuple),
390    StringPairs(struct_representation::StringPairs),
391
392    // can't handle this variant until field order matches set of fields
393    #[weight(0)]
394    StringJoin(struct_representation::StringJoin),
395    ListPairs(struct_representation::ListPairs),
396}
397
398mod struct_representation {
399    use super::{AnyScalar, FieldName};
400    use serde::{Deserialize, Serialize};
401
402    use super::DEFAULT_SIZE_RANGE;
403    use proptest::{collection::btree_map, prelude::any};
404
405    #[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
406    #[serde(rename_all = "camelCase")]
407    #[derive(test_strategy::Arbitrary)]
408    pub(crate) struct Map {
409        #[serde(default)]
410        #[strategy(btree_map(any::<FieldName>(), any::<MapFieldDetails>(), DEFAULT_SIZE_RANGE))]
411        pub(crate) fields: super::Map<FieldName, MapFieldDetails>,
412    }
413
414    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
415    #[serde(rename_all = "camelCase")]
416    #[derive(test_strategy::Arbitrary)]
417    pub(crate) struct MapFieldDetails {
418        pub(crate) rename: Option<String>,
419        pub(crate) implicit: Option<AnyScalar>,
420    }
421
422    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
423    #[serde(rename_all = "camelCase")]
424    #[derive(test_strategy::Arbitrary)]
425    pub(crate) struct Tuple {
426        // TODO: remove Option
427        pub(crate) field_order: Option<Vec<FieldName>>,
428    }
429
430    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
431    #[serde(rename_all = "camelCase")]
432    #[derive(test_strategy::Arbitrary)]
433    pub(crate) struct StringPairs {
434        #[strategy("[^\"]+")]
435        pub(crate) inner_delim: String,
436
437        #[strategy("[^\"]+")]
438        pub(crate) entry_delim: String,
439    }
440
441    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
442    #[serde(rename_all = "camelCase")]
443    #[derive(test_strategy::Arbitrary)]
444    pub(crate) struct StringJoin {
445        #[strategy("[^\"]+")]
446        pub(crate) join: String,
447
448        pub(crate) field_order: Vec<FieldName>,
449    }
450
451    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
452    pub(crate) struct ListPairs;
453}
454
455impl Default for StructRepresentation {
456    fn default() -> Self {
457        Self::Map(struct_representation::Map::default())
458    }
459}
460
461#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
462pub(crate) struct TypeEnum {
463    #[strategy(btree_map(any::<EnumValue>(), any::<Null>(), DEFAULT_SIZE_RANGE))]
464    members: Map<EnumValue, Null>,
465    representation: EnumRepresentation,
466}
467
468#[derive(
469    Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, test_strategy::Arbitrary,
470)]
471pub(crate) struct EnumValue(#[strategy("[a-z0-9_]+")] String);
472
473#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
474#[serde(rename_all = "lowercase")]
475#[derive(test_strategy::Arbitrary)]
476enum EnumRepresentation {
477    String(enum_representation::String),
478    #[weight(0)]
479    Int(enum_representation::Int),
480}
481
482mod enum_representation {
483    use super::{EnumValue, Map, DEFAULT_SIZE_RANGE};
484    use proptest::{collection::btree_map, prelude::*};
485    use serde::{Deserialize, Serialize};
486
487    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, test_strategy::Arbitrary)]
488    pub(crate) struct String(
489        #[strategy(btree_map(any::<EnumValue>(), "[^\"]*", DEFAULT_SIZE_RANGE))]
490        Map<EnumValue, std::string::String>,
491    );
492
493    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, test_strategy::Arbitrary)]
494    pub(crate) struct Int(
495        #[strategy(btree_map(any::<EnumValue>(), any::<Int>(), DEFAULT_SIZE_RANGE))]
496        Map<EnumValue, Int>,
497    );
498}
499
500impl Default for EnumRepresentation {
501    fn default() -> Self {
502        Self::String(enum_representation::String::default())
503    }
504}
505
506#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, test_strategy::Arbitrary)]
507pub(crate) struct TypeCopy {
508    from_type: TypeName,
509}
510
511const L_BOOL: &str = "bool";
512const L_STRING: &str = "string";
513const L_BYTES: &str = "bytes";
514const L_INT: &str = "int";
515const L_FLOAT: &str = "float";
516const L_MAP: &str = "map";
517const L_LIST: &str = "list";
518const L_LINK: &str = "link";
519const L_UNION: &str = "union";
520const L_STRUCT: &str = "struct";
521const L_ENUM: &str = "enum";
522
523const L_TYPE: &str = "type";
524const L_OPTIONAL: &str = "optional";
525const L_NULLABLE: &str = "nullable";
526const L_LINK_REF: &str = "&";
527const L_COPY: &str = "=";
528const L_REPRESENTATION: &str = "representation";
529const L_KINDED: &str = "kinded";
530const L_KEYED: &str = "keyed";
531const L_ENVELOPE: &str = "envelope";
532const L_INLINE: &str = "inline";
533const L_TUPLE: &str = "tuple";
534const L_STRINGPAIRS: &str = "stringpairs";
535const L_STRINGJOIN: &str = "stringjoin";
536const L_LISTPAIRS: &str = "listpairs";
537const L_DISCRIMINANT_KEY: &str = "discriminantKey";
538const L_CONTENT_KEY: &str = "contentKey";
539const L_IMPLICIT: &str = "implicit";
540const L_BYTEPREFIX: &str = "byteprefix";
541
542impl fmt::Display for TypeName {
543    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
544        write!(f, "{}", self.0)
545    }
546}
547
548impl fmt::Display for SchemaMap {
549    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
550        for (name, ty) in &self.0 {
551            write!(f, "{} {} {}\n\n", L_TYPE, name, ty)?;
552        }
553        Ok(())
554    }
555}
556
557impl fmt::Display for Schema {
558    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
559        // TODO: self.advanced
560        write!(f, "{}", &self.types)
561    }
562}
563
564impl fmt::Display for Type {
565    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
566        match self {
567            Self::Bool(x) => write!(f, "{}", x),
568            Self::String(x) => write!(f, "{}", x),
569            Self::Bytes(x) => write!(f, "{}", x),
570            Self::Int(x) => write!(f, "{}", x),
571            Self::Float(x) => write!(f, "{}", x),
572            Self::Map(x) => write!(f, "{}", x),
573            Self::List(x) => write!(f, "{}", x),
574            Self::Link(x) => write!(f, "{}", x),
575            Self::Union(x) => write!(f, "{}", x),
576            Self::Struct(x) => write!(f, "{}", x),
577            Self::Enum(x) => write!(f, "{}", x),
578            Self::Copy(x) => write!(f, "{}", x),
579        }
580    }
581}
582
583/*
584impl fmt::Display for TypeKind {
585    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
586        match self {
587            Self::Bool => write!(f, "{}", L_BOOL),
588            Self::String => write!(f, "{}", L_STRING),
589            Self::Bytes => write!(f, "{}", L_BYTES),
590            Self::Int => write!(f, "{}", L_INT),
591            Self::Float => write!(f, "{}", L_FLOAT),
592            Self::Map => write!(f, "{}", L_MAP),
593            Self::List => write!(f, "{}", L_LIST),
594            Self::Link => write!(f, "{}", L_LINK),
595            Self::Union => write!(f, "{}", L_UNION),
596            Self::Struct => write!(f, "{}", L_STRUCT),
597            Self::Enum => write!(f, "{}", L_ENUM),
598        }
599    }
600}
601*/
602
603impl fmt::Display for RepresentationKind {
604    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
605        match self {
606            Self::Bool => write!(f, "{}", L_BOOL),
607            Self::String => write!(f, "{}", L_STRING),
608            Self::Bytes => write!(f, "{}", L_BYTES),
609            Self::Int => write!(f, "{}", L_INT),
610            Self::Float => write!(f, "{}", L_FLOAT),
611            Self::Map => write!(f, "{}", L_MAP),
612            Self::List => write!(f, "{}", L_LIST),
613            Self::Link => write!(f, "{}", L_LINK),
614        }
615    }
616}
617
618impl fmt::Display for AnyScalar {
619    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
620        match self {
621            Self::Bool(x) => write!(f, "\"{}\"", x),
622            Self::String(x) => write!(f, "\"{}\"", x),
623            Self::Bytes(_x) => todo!("literal bytes"), // write!(f, "{}", x),
624            Self::Int(x) => write!(f, "{}", x),
625            Self::Float(x) => write!(f, "{}", x),
626        }
627    }
628}
629
630impl fmt::Display for TypeBool {
631    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
632        write!(f, "{}", L_BOOL)
633    }
634}
635
636impl fmt::Display for TypeString {
637    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
638        write!(f, "{}", L_STRING)
639    }
640}
641
642impl fmt::Display for TypeBytes {
643    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
644        write!(f, "{}", self.representation)
645    }
646}
647
648impl fmt::Display for BytesRepresentation {
649    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
650        match self {
651            Self::Bytes(b) => write!(f, "{}", b),
652            Self::Advanced(_name) => todo!("advanced layout for bytes"),
653        }
654    }
655}
656
657impl fmt::Display for bytes_representation::Bytes {
658    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
659        write!(f, "{}", L_BYTES)
660    }
661}
662
663impl fmt::Display for TypeInt {
664    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
665        write!(f, "{}", L_INT)
666    }
667}
668
669impl fmt::Display for TypeFloat {
670    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
671        write!(f, "{}", L_FLOAT)
672    }
673}
674
675impl fmt::Display for TypeMap {
676    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
677        match &self.representation {
678            MapRepresentation::Map(_) => {
679                if self.value_nullable {
680                    write!(f, "{} ", L_NULLABLE)?;
681                }
682                write!(f, "{{{}:{}}}", self.key_type, self.value_type)
683            }
684            MapRepresentation::StringPairs(sp) => {
685                if self.value_nullable {
686                    write!(f, "{} ", L_NULLABLE)?;
687                }
688                writeln!(
689                    f,
690                    "{{{}:{}}} {} {} {{",
691                    self.key_type, self.value_type, L_REPRESENTATION, L_STRINGPAIRS
692                )?;
693                writeln!(f, "  innerDelim \"{}\"", sp.inner_delim)?;
694                writeln!(f, "  entryDelim \"{}\"", sp.entry_delim)?;
695                writeln!(f, "}}")
696            }
697            MapRepresentation::ListPairs(_) => {
698                if self.value_nullable {
699                    write!(f, "{} ", L_NULLABLE)?;
700                }
701                writeln!(
702                    f,
703                    "{{{}:{}}} representation listpairs",
704                    self.key_type, self.value_type
705                )
706            }
707            MapRepresentation::Advanced(_) => todo!(),
708        }
709    }
710}
711
712impl fmt::Display for TypeList {
713    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
714        if self.value_nullable {
715            write!(f, "{} ", L_NULLABLE)?;
716        }
717        write!(f, "[{}]", self.value_type)
718
719        // TODO: handle self.representation
720    }
721}
722
723impl fmt::Display for TypeLink {
724    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
725        write!(f, "{}{}", L_LINK_REF, self.expected_type)
726    }
727}
728
729impl fmt::Display for TypeUnion {
730    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
731        write!(f, "{}", self.representation)
732    }
733}
734
735impl fmt::Display for UnionRepresentation {
736    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
737        match self {
738            Self::Kinded(x) => write!(f, "{}", x),
739            Self::Keyed(x) => write!(f, "{}", x),
740            Self::Envelope(x) => write!(f, "{}", x),
741            Self::Inline(x) => write!(f, "{}", x),
742            Self::BytePrefix(x) => write!(f, "{}", x),
743        }
744    }
745}
746
747impl fmt::Display for union_representation::Kinded {
748    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
749        write!(f, "{} {{", L_UNION)?;
750        for (kind, name) in &self.0 {
751            write!(f, "\n  | {} {}", name, kind)?;
752        }
753        if !self.0.is_empty() {
754            writeln!(f)?;
755        }
756        write!(f, "}} {} {}", L_REPRESENTATION, L_KINDED)
757    }
758}
759
760impl fmt::Display for union_representation::Keyed {
761    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
762        write!(f, "{} {{", L_UNION)?;
763        for (ty, name) in &self.0 {
764            write!(f, "\n  | {} \"{}\"", name, ty)?;
765        }
766        if !self.0.is_empty() {
767            writeln!(f)?;
768        }
769        write!(f, "}} {} {}", L_REPRESENTATION, L_KEYED)
770    }
771}
772
773impl fmt::Display for union_representation::Envelope {
774    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
775        write!(f, "{} {{", L_UNION)?;
776        for (ty, name) in &self.discriminant_table {
777            write!(f, "\n  | {} \"{}\"", name, ty)?;
778        }
779        if !self.discriminant_table.is_empty() {
780            writeln!(f)?;
781        }
782        writeln!(f, "}} {} {} {{", L_REPRESENTATION, L_ENVELOPE)?;
783        writeln!(f, "  {} \"{}\"", L_DISCRIMINANT_KEY, self.discriminant_key)?;
784        writeln!(f, "  {} \"{}\"", L_CONTENT_KEY, self.content_key)?;
785        writeln!(f, "}}")
786    }
787}
788
789impl fmt::Display for union_representation::Inline {
790    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
791        write!(f, "{} {{", L_UNION)?;
792        for (ty, name) in &self.discriminant_table {
793            write!(f, "\n  | {} \"{}\"", name, ty)?;
794        }
795        if !self.discriminant_table.is_empty() {
796            writeln!(f)?;
797        }
798        write!(
799            f,
800            "}} {} {} {{\n  {} \"{}\"\n}}",
801            L_REPRESENTATION, L_INLINE, L_DISCRIMINANT_KEY, self.discriminant_key
802        )
803    }
804}
805
806impl fmt::Display for union_representation::BytePrefix {
807    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
808        write!(f, "{} {{", L_UNION)?;
809        for (ty, byte) in &self.discriminant_table {
810            write!(f, "\n  | {} {}", ty, byte)?;
811        }
812        if !self.discriminant_table.is_empty() {
813            writeln!(f)?;
814        }
815        write!(f, "}} {} {}", L_REPRESENTATION, L_BYTEPREFIX)
816    }
817}
818
819impl fmt::Display for TypeStruct {
820    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
821        match &self.representation {
822            StructRepresentation::Map(m) => {
823                write!(f, "{} {{", L_STRUCT)?;
824                for (name, val) in &self.fields {
825                    write!(f, "\n  {} {}", name, val)?;
826                    if let Some(details) = m.fields.get(name) {
827                        write!(f, " (")?;
828                        if let Some(implicit) = &details.implicit {
829                            write!(f, "{} {}", L_IMPLICIT, implicit)?;
830                        }
831                        write!(f, ")")?;
832                    }
833                }
834                if !self.fields.is_empty() {
835                    writeln!(f)?;
836                }
837                write!(f, "}}")
838            }
839            StructRepresentation::Tuple(t) => {
840                write!(f, "{} {{", L_STRUCT)?;
841                for (name, val) in &self.fields {
842                    write!(f, "\n  {} {}", name, val)?;
843                }
844                if !self.fields.is_empty() {
845                    writeln!(f)?;
846                }
847                write!(f, "}} {} {}", L_REPRESENTATION, L_TUPLE)?;
848                if let Some(field_order) = &t.field_order {
849                    writeln!(f, " {{")?;
850                    writeln!(
851                        f,
852                        "  fieldOrder [{}]",
853                        field_order
854                            .iter()
855                            .map(|f| format!("\"{}\"", f))
856                            .collect::<Vec<_>>()
857                            .join(", ")
858                    )?;
859                    writeln!(f, "}}")?;
860                }
861                Ok(())
862            }
863            StructRepresentation::StringPairs(sp) => {
864                write!(f, "{} {{", L_STRUCT)?;
865                for (name, val) in &self.fields {
866                    write!(f, "\n  {} {}", name, val)?;
867                }
868                if !self.fields.is_empty() {
869                    writeln!(f)?;
870                }
871                writeln!(f, "}} {} {} {{", L_REPRESENTATION, L_STRINGPAIRS)?;
872                writeln!(f, "  innerDelim \"{}\"", sp.inner_delim)?;
873                writeln!(f, "  entryDelim \"{}\"", sp.entry_delim)?;
874                writeln!(f, "}}")
875            }
876            StructRepresentation::StringJoin(sj) => {
877                write!(f, "{} {{", L_STRUCT)?;
878                for (name, val) in &self.fields {
879                    write!(f, "\n  {} {}", name, val)?;
880                }
881                if !self.fields.is_empty() {
882                    writeln!(f)?;
883                }
884                writeln!(f, "}} {} {} {{", L_REPRESENTATION, L_STRINGJOIN)?;
885                writeln!(f, "  join \"{}\"", sj.join)?;
886                writeln!(f, "}}")
887            }
888            StructRepresentation::ListPairs(_) => {
889                write!(f, "{} {{", L_STRUCT)?;
890                for (name, val) in &self.fields {
891                    write!(f, "\n  {} {}", name, val)?;
892                }
893                if !self.fields.is_empty() {
894                    writeln!(f)?;
895                }
896                write!(f, "}} {} {}", L_REPRESENTATION, L_LISTPAIRS)
897            }
898        }
899    }
900}
901
902impl fmt::Display for FieldName {
903    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
904        write!(f, "{}", self.0)
905    }
906}
907
908impl fmt::Display for StructField {
909    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
910        if self.optional {
911            write!(f, "{} ", L_OPTIONAL)?;
912        }
913        if self.nullable {
914            write!(f, "{} ", L_NULLABLE)?;
915        }
916        write!(f, "{}", self.r#type)
917    }
918}
919
920impl fmt::Display for TypeTerm {
921    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
922        match self {
923            Self::TypeName(name) => write!(f, "{}", name),
924            Self::InlineDefn(inline) => write!(f, "{}", inline),
925        }
926    }
927}
928
929impl fmt::Display for InlineDefn {
930    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
931        match self {
932            Self::Map(map) => write!(f, "{}", map),
933            Self::List(list) => write!(f, "{}", list),
934        }
935    }
936}
937
938impl fmt::Display for TypeEnum {
939    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
940        write!(f, "{} {{", L_ENUM)?;
941        #[allow(clippy::for_kv_map)]
942        for (value, _null) in &self.members {
943            write!(f, "\n  | {}", value)?;
944        }
945        if !self.members.is_empty() {
946            writeln!(f)?;
947        }
948        write!(f, "}}")
949
950        // TODO: handle self.representation
951    }
952}
953
954impl fmt::Display for EnumValue {
955    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
956        write!(f, "{}", self.0)
957    }
958}
959
960impl fmt::Display for TypeCopy {
961    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
962        write!(f, "{} {}", L_COPY, self.from_type)
963    }
964}
965
966peg::parser! {
967    pub(crate) grammar schema_dsl() for str {
968        rule _eof() -> () = ![_] { }
969        rule _eol() -> () = "\n" / "\r\n" { }
970        rule _ws1() -> () = " " / "\t" { }
971
972        rule _comment() -> String = _ws1()* "#" s:$((!_eol() [_])*) _eol() { s.to_string() }
973        rule _empty_line() -> () = _ws1()* _eol() { }
974        rule _ws_block() -> () = (_comment() / _empty_line())* _ws1()* { }
975
976        pub(crate) rule type_name() -> TypeName = cs:$(['A'..='Z'] (['A'..='Z'] / ['a'..='z'] / ['0'..='9'] / "_")*) { TypeName(cs.to_string()) }
977
978        rule schema_map() -> SchemaMap = _ws_block() decls:(type_decl() ** _ws_block()) _ws_block() _eof() { SchemaMap(decls.into_iter().collect()) }
979
980        // TODO: `advanced`
981        pub(crate) rule parse() -> Schema = types:schema_map() { Schema { types, advanced: AdvancedDataLayoutMap::default() } }
982
983        rule m_map() -> TypeMap
984            = nil:("nullable" _ws1()+)? "{" _ws1()* n:type_name() _ws1()* ":" _ws1()* t:type_term() "}"
985        {
986            TypeMap {
987                key_type: n,
988                value_type: t,
989                value_nullable: nil.is_some(),
990                representation: MapRepresentation::default()
991            }
992        }
993        rule m_stringpairs() -> TypeMap
994            = nil:("nullable" _ws1()+)? "{" _ws1()* n:type_name() _ws1()* ":" _ws1()* t:type_term() "}"
995              // TODO: support either field order
996              _ws1()* "representation" _ws1()+ "stringpairs" _ws1()* "{" _eol() _ws1()* "innerDelim" _ws1()+ id:string() _eol() _ws1()* "entryDelim" _ws1()+ ed:string() _ws_block() "}"
997        {
998            TypeMap {
999                key_type: n,
1000                value_type: t,
1001                value_nullable: nil.is_some(),
1002                representation: MapRepresentation::StringPairs(map_representation::StringPairs { inner_delim: id, entry_delim: ed })
1003            }
1004        }
1005        rule m_listpairs() -> TypeMap
1006            = nil:("nullable" _ws1()+)? "{" _ws1()* n:type_name() _ws1()* ":" _ws1()* t:type_term() "}" _ws1()* "representation" _ws1()+ "listpairs"
1007        {
1008            TypeMap {
1009                key_type: n,
1010                value_type: t,
1011                value_nullable: nil.is_some(),
1012                representation: MapRepresentation::ListPairs(map_representation::ListPairs)
1013            }
1014        }
1015        rule type_map() -> TypeMap = m:(
1016            m_stringpairs() /
1017            m_listpairs() /
1018            m_map()
1019        )
1020
1021        // TODO: nullable and non-default representation
1022        rule type_list() -> TypeList = nil:("nullable" _ws1()+)?  "[" _ws1()* t:type_term() _ws1()* "]" { TypeList { value_type: t, value_nullable: nil.is_some(), representation: ListRepresentation::default()} }
1023
1024        rule t_bool() -> Type = "bool" { Type::Bool(TypeBool) }
1025        rule t_string() -> Type = "string" { Type::String(TypeString) }
1026        rule t_bytes() -> Type = "bytes" { Type::Bytes(TypeBytes { representation: BytesRepresentation::default() }) }
1027        rule t_int() -> Type = "int" { Type::Int(TypeInt) }
1028        rule t_float() -> Type = "float" { Type::Float(TypeFloat) }
1029        rule t_map() -> Type = m:type_map() { Type::Map(m) }
1030        rule t_list() -> Type = l:type_list() { Type::List(l) }
1031        rule t_link() -> Type = "&" t:type_name() { Type::Link(TypeLink { expected_type: t.to_string() }) }
1032        rule t_union() -> Type = r:union_representation() { Type::Union(TypeUnion { representation: r }) }
1033        rule t_struct() -> Type = s:struct_model() { Type::Struct(s) }
1034        rule t_enum() -> Type = "enum" _ws1()* "{" _ws_block() ms:(enum_member()*) _ws_block() "}" { Type::Enum(TypeEnum { members: ms.into_iter().map(|m| (m, Null)).collect(), representation: EnumRepresentation::default() }) }
1035        rule t_copy() -> Type = "=" _ws1()* n:type_name() { Type::Copy(TypeCopy { from_type: n }) }
1036        rule r#type() -> Type = t:(
1037            t_bool() /
1038            t_string() /
1039            t_bytes() /
1040            t_int() /
1041            t_float() /
1042            t_map() /
1043            t_list() /
1044            t_link() /
1045            t_union() /
1046            t_struct() /
1047            t_enum() /
1048            t_copy()
1049        ) { t }
1050        pub(crate) rule schema_type() -> Type = t:r#type() { t }
1051
1052
1053        rule union_representation() -> UnionRepresentation = ur:(
1054            ur_kinded() /
1055            ur_keyed() /
1056            ur_envelope() /
1057            ur_inline() /
1058            ur_byteprefix()
1059        ) { ur }
1060        rule ur_kinded() -> UnionRepresentation = "union" _ws1()* "{" _ws_block() ts:(type_name_and_representation_kind()*) _ws1()* "}" _ws1()* "representation" _ws1()+ "kinded" _ws1()* (_eol() / _eof()) { UnionRepresentation::Kinded(union_representation::Kinded(ts.into_iter().map(|(tn, rk)| (rk, tn)).collect())) }
1061        rule ur_keyed() -> UnionRepresentation = "union" _ws1()* "{" _ws_block() ts:(type_name_and_string()*) _ws1()* "}" _ws1()* "representation" _ws1()+ "keyed" _ws1()* (_eol() / _eof())  { UnionRepresentation::Keyed(union_representation::Keyed(ts.into_iter().map(|(tn, s)| (s, tn)).collect())) }
1062        rule ur_envelope() -> UnionRepresentation = "union" _ws1()* "{" _ws_block() ts:(type_name_and_string()*) _ws1()* "}" _ws1()* "representation" _ws1()+ "envelope" _ws1()* "{" _ws_block() "discriminantKey" _ws1()+ dk:string() _ws_block() "contentKey" _ws1()+ ck:string() _ws_block() "}" (_eol() / _eof())  { UnionRepresentation::Envelope(union_representation::Envelope { discriminant_table: ts.into_iter().map(|(tn, s)| (s, tn)).collect(), discriminant_key: dk, content_key: ck }) }
1063        rule ur_inline() -> UnionRepresentation = "union" _ws1()* "{" _ws_block() ts:(type_name_and_string()*) _ws1()* "}" _ws1()* "representation" _ws1()+ "inline" _ws1()* "{" _ws_block() "discriminantKey" _ws1()+ k:string() _ws_block() "}" (_eol() / _eof())  { UnionRepresentation::Inline(union_representation::Inline { discriminant_key: k, discriminant_table: ts.into_iter().map(|(tn, s)| (s, tn)).collect() }) }
1064        rule ur_byteprefix() -> UnionRepresentation = "union" _ws1()* "{" _ws_block() ts:(type_name_and_byte()*) _ws1()* "}" _ws1()* "representation" _ws1()+ "byteprefix" (_eol() / _eof())  { UnionRepresentation::BytePrefix(union_representation::BytePrefix { discriminant_table: ts.into_iter().collect() }) }
1065
1066        rule type_name_and_string() -> (TypeName, String) = _ws1()* "|" _ws1()* t:type_name() _ws1()+ s:string() _ws1()* _eol() { (t, s) }
1067        rule string() -> String = "\"" cs:$((!"\"" [_])*) "\"" { cs.to_string() }
1068
1069        rule type_name_and_byte() -> (TypeName, u8) = _ws1()* "|" _ws1()* s:type_name() _ws1()+ b:$(['0'..='9']+) _ws1()* _eol() { (s, b.parse().unwrap()) }
1070
1071        rule type_name_and_representation_kind() -> (TypeName, RepresentationKind) = _ws1()* "|" _ws1()* t:type_name() _ws1()+ r:representation_kind() _ws1()* _eol() { (t, r) }
1072        rule rk_bool() -> RepresentationKind = "bool" { RepresentationKind::Bool }
1073        rule rk_string() -> RepresentationKind = "string" { RepresentationKind::String }
1074        rule rk_bytes() -> RepresentationKind = "bytes" { RepresentationKind::Bytes }
1075        rule rk_int() -> RepresentationKind = "int" { RepresentationKind::Int }
1076        rule rk_float() -> RepresentationKind = "float" { RepresentationKind::Float }
1077        rule rk_map() -> RepresentationKind = "map" { RepresentationKind::Map }
1078        rule rk_list() -> RepresentationKind = "list" { RepresentationKind::List }
1079        rule rk_link() -> RepresentationKind = "link" { RepresentationKind::Link }
1080        rule representation_kind() -> RepresentationKind = r:(
1081            rk_bool() /
1082            rk_string() /
1083            rk_bytes() /
1084            rk_int() /
1085            rk_float() /
1086            rk_map() /
1087            rk_list() /
1088            rk_link()
1089        ) { r }
1090
1091        rule as_bool_false() -> AnyScalar = "\"false\"" { AnyScalar::Bool(false) }
1092        rule as_bool_true() -> AnyScalar = "\"true\"" { AnyScalar::Bool(true) }
1093        rule as_string() -> AnyScalar = s:string() { AnyScalar::String(s) }
1094        rule as_bytes() -> AnyScalar = "x" { todo!() }
1095        rule as_int() -> AnyScalar = "x" { todo!() }
1096        rule as_float() -> AnyScalar = "x" { todo!() }
1097        rule any_scalar() -> AnyScalar = a:(
1098            as_bool_false() /
1099            as_bool_true() /
1100            as_string() /
1101            as_bytes() /
1102            as_int() /
1103            as_float()
1104        ) { a }
1105
1106
1107        pub(crate) rule field_name() -> FieldName = cs:$((['A'..='Z'] / ['a'..='z'] / ['0'..='9'] / "_")+) { FieldName(cs.to_string()) }
1108        rule quoted_field_name() -> FieldName = "\"" f:field_name() "\"" { f }
1109        // TODO: support different ordering of optional and nullable
1110        rule struct_field() -> StructField = o:("optional" _ws1()+)? n:("nullable" _ws1()+)? t:type_term() { StructField { r#type: t, optional: o.is_some(), nullable: n.is_some() } }
1111
1112        rule tt_type_name() -> TypeTerm = n:type_name() { TypeTerm::TypeName(n) }
1113        rule id_map() -> InlineDefn = m:type_map() { InlineDefn::Map(m) }
1114        rule id_list() -> InlineDefn = l:type_list() { InlineDefn::List(l) }
1115        rule tt_inline_defn() -> TypeTerm = i:(id_map() / id_list()) { TypeTerm::InlineDefn(Box::new(i)) }
1116        pub(crate) rule type_term() -> TypeTerm = tt:(tt_type_name() / tt_inline_defn()) { tt }
1117
1118        rule st_map() -> TypeStruct = "struct" _ws1()* "{" _ws_block() fs:(st_map_field()*) _ws1()* "}" {
1119            let fields = fs.iter().cloned().map(|(f, s, _)| (f, s)).collect();
1120            let representation = StructRepresentation::Map(struct_representation::Map {
1121                fields: fs.into_iter().filter_map(|(f, _, x)| x.map(|x| (f, x))).collect()
1122            });
1123
1124            TypeStruct { fields, representation }
1125        }
1126        rule st_map_field() -> (FieldName, StructField, Option<struct_representation::MapFieldDetails>) = _ws1()* n:field_name() _ws1()+ f:struct_field() x:st_map_field_details()? _ws1()* _eol() { (n, f, x) }
1127        rule st_map_field_details() -> struct_representation::MapFieldDetails = _ws1()* "(" _ws1()* "implicit" _ws1()+ i:any_scalar()? _ws1()* ")" { struct_representation::MapFieldDetails { implicit: i, rename: None } }
1128
1129        rule st_tuple() -> TypeStruct
1130            = "struct" _ws1()* "{" _ws_block() fs:(st_map_field()*) _ws1()* "}"
1131            _ws1()* "representation" _ws1()+ "tuple" _ws1()* o:st_field_order()?
1132        {
1133            let fields = fs.iter().cloned().map(|(f, s, _)| (f, s)).collect();
1134            let representation = StructRepresentation::Tuple(struct_representation::Tuple {
1135                field_order: o,
1136            });
1137
1138            TypeStruct { fields, representation }
1139        }
1140        rule st_field_order() -> Vec<FieldName> = "{" _ws_block() "fieldOrder" _ws1()+ "[" fs:(quoted_field_name() ** ("," _ws_block())) "]" _ws_block() "}" { fs }
1141
1142        rule st_stringpairs() -> TypeStruct
1143            = "struct" _ws1()* "{" _ws_block() fs:(st_map_field()*) _ws1()* "}"
1144              // TODO: support either field ordering
1145              _ws1()* "representation" _ws1()+ "stringpairs" _ws1()* "{" _eol() _ws1()* "innerDelim" _ws1()+ id:string() _eol() _ws1()* "entryDelim" _ws1()+ ed:string() _ws_block() "}"
1146        {
1147            TypeStruct {
1148                fields: fs.iter().cloned().map(|(f, s, _)| (f, s)).collect(),
1149                representation: StructRepresentation::StringPairs(struct_representation::StringPairs {
1150                    inner_delim: id,
1151                    entry_delim: ed,
1152                }),
1153            }
1154        }
1155
1156        rule st_stringjoin() -> TypeStruct
1157        = "struct" _ws1()* "{" _ws_block() fs:(st_map_field()*) _ws1()* "}"
1158          // TODO: support either field ordering
1159          _ws1()* "representation" _ws1()+ "stringjoin" _ws1()* "{" _eol() _ws1()* "join" _ws1()+ j:string() _ws_block() "}"
1160        {
1161            TypeStruct {
1162                fields: fs.iter().cloned().map(|(f, s, _)| (f, s)).collect(),
1163                representation: StructRepresentation::StringJoin(struct_representation::StringJoin {
1164                    join: j,
1165                    field_order: fs.into_iter().map(|(f, _, _)| f).collect::<Vec<_>>(),
1166                }),
1167            }
1168        }
1169
1170        rule st_listpairs() -> TypeStruct
1171        = "struct" _ws1()* "{" _ws_block() fs:(st_map_field()*) _ws1()* "}" _ws1()* "representation" _ws1()+ "listpairs"
1172        {
1173            TypeStruct {
1174                fields: fs.iter().cloned().map(|(f, s, _)| (f, s)).collect(),
1175                representation: StructRepresentation::ListPairs(struct_representation::ListPairs),
1176            }
1177        }
1178
1179        pub(crate) rule struct_model() -> TypeStruct = s:(
1180            st_tuple() /
1181            st_stringpairs() /
1182            st_stringjoin() /
1183            st_listpairs() /
1184            st_map()
1185        ) { s }
1186
1187
1188        pub(crate) rule enum_value() -> EnumValue = cs:$((['A'..='Z'] / ['a'..='z'] / ['0'..='9'] / "_")+) { EnumValue(cs.to_string()) }
1189        rule enum_member() -> EnumValue = _ws1()* "|" _ws1()* ev:enum_value() _ws1()* _eol() { ev }
1190
1191        rule type_decl() -> (TypeName, Type) = "type" _ws1()+ n:type_name() _ws1()+ t:r#type() (_eol() / _eof()) { (n, t) }
1192    }
1193}
1194
1195#[cfg(test)]
1196mod tests {
1197    use super::*;
1198
1199    use std::fs::read_to_string;
1200
1201    use insta::{assert_debug_snapshot, assert_json_snapshot, with_settings};
1202    use pretty_assertions::assert_eq;
1203    use test_strategy::proptest;
1204
1205    #[cfg(feature = "fast-test")]
1206    const CASES: u32 = 10;
1207    #[cfg(not(feature = "fast-test"))]
1208    const CASES: u32 = 1000;
1209
1210    #[cfg(feature = "fast-test")]
1211    const MAX_SHRINK_ITERS: u32 = 100;
1212    #[cfg(not(feature = "fast-test"))]
1213    const MAX_SHRINK_ITERS: u32 = 10000;
1214
1215    fn schema_schema() -> Schema {
1216        schema_dsl::parse(&read_to_string("./specs/schemas/schema-schema.ipldsch").unwrap())
1217            .unwrap()
1218    }
1219
1220    fn schema_schema_json() -> String {
1221        read_to_string("./specs/schemas/schema-schema.ipldsch.json").unwrap()
1222    }
1223
1224    fn schema_roundtrips_through_json(schema: &Schema) {
1225        assert_eq!(
1226            *schema,
1227            serde_json::from_str(&serde_json::to_string(schema).unwrap()).unwrap()
1228        );
1229    }
1230
1231    fn schema_roundtrips_through_dsl(schema: &Schema) {
1232        let rendered = schema.to_string();
1233
1234        for (n, line) in rendered.lines().enumerate() {
1235            eprintln!("  {:>4}  │ {}", n + 1, line);
1236        }
1237
1238        assert_eq!(*schema, schema_dsl::parse(&rendered).unwrap());
1239    }
1240
1241    #[test]
1242    fn snapshot_of_parsed_schema_schema() {
1243        assert_debug_snapshot!(schema_schema());
1244    }
1245
1246    #[test]
1247    fn snapshot_of_reified_json_form_of_schema_schema() {
1248        with_settings!({sort_maps => true}, {
1249            assert_json_snapshot!(schema_schema())
1250        });
1251    }
1252
1253    #[test]
1254    fn struct_representation_tuple_reifies_correctly() {
1255        schema_roundtrips_through_json(
1256            &schema_dsl::parse(
1257                r#"type StructRepresentation_Tuple struct {
1258            fieldOrder optional [FieldName]
1259        }"#,
1260            )
1261            .unwrap(),
1262        );
1263    }
1264
1265    #[test]
1266    fn reified_form_of_schema_schema_matches_parsed_dsl_form() {
1267        assert_eq!(
1268            schema_schema(),
1269            serde_json::from_str(&schema_schema_json()).unwrap()
1270        );
1271    }
1272
1273    #[test]
1274    fn schema_schema_roundtrips_through_parsing_and_display() {
1275        schema_roundtrips_through_dsl(&schema_schema());
1276    }
1277
1278    #[proptest(cases = CASES, max_shrink_iters = MAX_SHRINK_ITERS)]
1279    fn roundtrips_through_dsl_form(schema: Schema) {
1280        schema_roundtrips_through_dsl(&schema);
1281    }
1282
1283    #[proptest(cases = CASES, max_shrink_iters = MAX_SHRINK_ITERS)]
1284    fn roundtrips_through_json_form(schema: Schema) {
1285        schema_roundtrips_through_json(&schema);
1286    }
1287}