ironsmith_parser/
lib.rs

1#![deny(clippy::pedantic)]
2#![allow(clippy::missing_errors_doc)]
3
4use node_values::NodeValue;
5use nom::{
6    combinator::{all_consuming, opt},
7    sequence::{delimited, tuple},
8    IResult,
9};
10use shapes::ShapeSection;
11use whitespace::ws;
12
13/// Abstract syntax tree for a Smithy file
14#[derive(Debug, Clone)]
15pub struct Ast<'a> {
16    /// Control section of the file
17    pub control: Vec<(&'a str, NodeValue<'a>)>,
18    /// Metadata section of the file
19    pub metadata: Vec<(&'a str, NodeValue<'a>)>,
20    /// Shapes defined within the file
21    pub shapes: Option<ShapeSection<'a>>,
22}
23
24/// Parses an entire Smithy 2.0 IDL format file and returns the abstract syntax tree for it.
25pub fn parse_ast(input: &str) -> IResult<&str, Ast<'_>> {
26    // TODO: integrate with https://docs.rs/nom_locate and https://docs.rs/nom-supreme to provide better errors
27    let (rest, (control, metadata, shapes)) = all_consuming(delimited(
28        opt(ws),
29        tuple((
30            control::control_section,
31            metadata::metadata_section,
32            shapes::shape_section,
33        )),
34        opt(ws),
35    ))(input)?;
36
37    Ok((
38        rest,
39        Ast {
40            control,
41            metadata,
42            shapes,
43        },
44    ))
45}
46
47pub mod comment {
48    use nom::{
49        branch::alt,
50        bytes::complete::tag,
51        character::complete::{line_ending, not_line_ending},
52        combinator::{map, opt},
53        sequence::delimited,
54        IResult,
55    };
56
57    /// ```text
58    /// Comment = DocumentationComment / LineComment
59    /// ```
60    pub fn comment(input: &str) -> IResult<&str, &str> {
61        map(
62            alt((documentation_comment, line_comment)),
63            Option::unwrap_or_default,
64        )(input)
65    }
66
67    /// ```text
68    /// DocumentationComment = "///" *NotNL NL
69    /// ```
70    pub fn documentation_comment(input: &str) -> IResult<&str, Option<&str>> {
71        delimited(tag("///"), opt(not_line_ending), line_ending)(input)
72    }
73
74    /// ```text
75    /// LineComment = "//" [(%x09 / %x20-2E / %x30-10FFF) *NotNL] NL ; First character after "//" can't be "/"
76    /// ```
77    pub fn line_comment(input: &str) -> IResult<&str, Option<&str>> {
78        delimited(tag("//"), opt(not_line_ending), line_ending)(input)
79    }
80}
81
82pub mod whitespace {
83    use nom::{
84        branch::alt,
85        character::complete::{char, line_ending, multispace1, space0},
86        combinator::{opt, recognize},
87        multi::many1,
88        sequence::delimited,
89        IResult,
90    };
91
92    /// ```text
93    /// WS = 1*(SP / NL / Comment / Comma) ; whitespace
94    /// ```
95    pub fn ws(input: &str) -> IResult<&str, Vec<&str>> {
96        many1(alt((multispace1, super::comment::comment, comma)))(input)
97    }
98
99    /// ```text
100    /// Comma = ","
101    /// ```
102    pub fn comma(input: &str) -> IResult<&str, &str> {
103        recognize(char(','))(input)
104    }
105
106    /// ```text
107    /// BR = [SP] 1*(Comment / NL) [WS]; line break followed by whitespace
108    /// ```
109    pub fn br(input: &str) -> IResult<&str, Vec<&str>> {
110        delimited(
111            space0,
112            many1(alt((super::comment::comment, line_ending))),
113            opt(ws),
114        )(input)
115    }
116}
117
118pub mod control {
119    use nom::{
120        character::complete::{char, space0},
121        combinator::cut,
122        multi::many0,
123        sequence::{delimited, preceded, separated_pair, terminated},
124        IResult,
125    };
126
127    use crate::{
128        node_values::{node_object_key, node_value, NodeValue},
129        whitespace::br,
130    };
131
132    /// ```text
133    /// ControlSection = *(ControlStatement)
134    /// ```
135    pub fn control_section(input: &str) -> IResult<&str, Vec<(&str, NodeValue<'_>)>> {
136        many0(control_statement)(input)
137    }
138
139    /// ```text
140    /// ControlStatement = "$" NodeObjectKey [SP] ":" [SP] NodeValue BR
141    /// ```
142    pub fn control_statement(input: &str) -> IResult<&str, (&str, NodeValue<'_>)> {
143        preceded(
144            char('$'),
145            cut(terminated(
146                separated_pair(
147                    node_object_key,
148                    delimited(space0, char(':'), space0),
149                    node_value,
150                ),
151                br,
152            )),
153        )(input)
154    }
155}
156
157pub mod metadata {
158    use nom::{
159        bytes::complete::tag,
160        character::complete::{char, space0, space1},
161        combinator::cut,
162        multi::many0,
163        sequence::{delimited, preceded, separated_pair, terminated, tuple},
164        IResult,
165    };
166
167    use crate::{
168        node_values::{node_object_key, node_value, NodeValue},
169        whitespace::br,
170    };
171
172    /// ```text
173    /// MetadataSection = *(MetadataStatement)
174    /// ```
175    pub fn metadata_section(input: &str) -> IResult<&str, Vec<(&str, NodeValue<'_>)>> {
176        many0(metadata_statement)(input)
177    }
178
179    /// ```text
180    /// MetadataStatement = %s"metadata" SP NodeObjectKey [SP] "=" [SP] NodeValue BR
181    /// ```
182    pub fn metadata_statement(input: &str) -> IResult<&str, (&str, NodeValue<'_>)> {
183        preceded(
184            tuple((tag("metadata"), space1)),
185            cut(terminated(
186                separated_pair(
187                    node_object_key,
188                    delimited(space0, char('='), space0),
189                    node_value,
190                ),
191                br,
192            )),
193        )(input)
194    }
195}
196
197pub mod node_values {
198    use nom::{
199        branch::alt,
200        bytes::complete::{tag, take_while_m_n},
201        character::complete::{char, line_ending, one_of, space0},
202        combinator::{cut, map, opt, recognize},
203        multi::{many0, many1, separated_list0},
204        number::complete::recognize_float,
205        sequence::{delimited, preceded, separated_pair, terminated, tuple},
206        IResult,
207    };
208
209    use crate::{
210        shape_id::{identifier, shape_id},
211        whitespace::ws,
212    };
213
214    #[derive(Clone, Debug, PartialEq, Eq)]
215    pub enum NodeValue<'a> {
216        Array(Vec<NodeValue<'a>>),
217        Object(Vec<NodeKeyValuePair<'a>>),
218        Number(&'a str),
219        Keyword(Keyword),
220        String(&'a str),
221    }
222
223    #[derive(Clone, Debug, PartialEq, Eq)]
224    pub struct NodeKeyValuePair<'a> {
225        pub key: &'a str,
226        pub value: NodeValue<'a>,
227    }
228
229    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
230    pub enum Keyword {
231        True,
232        False,
233        Null,
234    }
235
236    /// ```text
237    /// NodeValue =
238    ///   NodeArray
239    /// / NodeObject
240    /// / Number
241    /// / NodeKeyword
242    /// / NodeStringValue
243    /// ```
244    pub fn node_value(input: &str) -> IResult<&str, NodeValue<'_>> {
245        alt((
246            map(node_array, NodeValue::Array),
247            map(node_object, NodeValue::Object),
248            map(recognize_float, NodeValue::Number),
249            map(node_keyword, NodeValue::Keyword),
250            map(node_string_value, NodeValue::String),
251        ))(input)
252    }
253
254    /// ```text
255    /// NodeArray = "[" [WS] *(NodeValue [WS]) "]"
256    /// ```
257    pub fn node_array(input: &str) -> IResult<&str, Vec<NodeValue<'_>>> {
258        preceded(
259            char('['),
260            cut(terminated(
261                delimited(opt(ws), separated_list0(ws, node_value), opt(ws)),
262                char(']'),
263            )),
264        )(input)
265    }
266
267    /// ```text
268    /// NodeObject = "{" [WS] [NodeObjectKvp *(WS NodeObjectKvp)] [WS] "}"
269    /// ```
270    pub fn node_object(input: &str) -> IResult<&str, Vec<NodeKeyValuePair<'_>>> {
271        preceded(
272            char('{'),
273            cut(terminated(
274                delimited(opt(ws), separated_list0(ws, node_object_kvp), opt(ws)),
275                char('}'),
276            )),
277        )(input)
278    }
279
280    /// ```text
281    /// NodeObjectKvp = NodeObjectKey [WS] ":" [WS] NodeValue
282    /// ```
283    pub fn node_object_kvp(input: &str) -> IResult<&str, NodeKeyValuePair<'_>> {
284        map(
285            separated_pair(
286                node_object_key,
287                tuple((opt(ws), char(':'), opt(ws))),
288                node_value,
289            ),
290            |(key, value)| NodeKeyValuePair { key, value },
291        )(input)
292    }
293
294    /// ```text
295    /// NodeKeyword = %s"true" / %s"false" / %s"null"
296    /// ```
297    pub fn node_keyword(input: &str) -> IResult<&str, Keyword> {
298        alt((
299            map(tag("true"), |_| Keyword::True),
300            map(tag("false"), |_| Keyword::False),
301            map(tag("null"), |_| Keyword::Null),
302        ))(input)
303    }
304
305    /// ```text
306    /// NodeStringValue = ShapeId / TextBlock / QuotedText
307    /// ```
308    pub fn node_string_value(input: &str) -> IResult<&str, &str> {
309        alt((shape_id, text_block, quoted_text))(input)
310    }
311
312    /// ```text
313    /// TextBlock = ThreeDquotes [SP] NL *TextBlockContent ThreeDquotes
314    /// ```
315    pub fn text_block(input: &str) -> IResult<&str, &str> {
316        delimited(
317            tag(r#"""""#),
318            preceded(
319                tuple((space0, line_ending)),
320                recognize(many0(text_block_content)),
321            ),
322            tag(r#"""""#),
323        )(input)
324    }
325
326    // ```
327    // TextBlockContent = QuotedChar / (1*2DQUOTE 1*QuotedChar)
328    // ```
329    pub fn text_block_content(input: &str) -> IResult<&str, &str> {
330        alt((
331            quoted_char,
332            preceded(many1(tag(r#""""#)), recognize(many1(quoted_char))),
333        ))(input)
334    }
335
336    /// ```text
337    /// NodeObjectKey = QuotedText / Identifier
338    /// ```
339    pub fn node_object_key(input: &str) -> IResult<&str, &str> {
340        alt((quoted_text, identifier))(input)
341    }
342
343    /// ```text
344    /// QuotedText = DQUOTE *QuotedChar DQUOTE
345    /// ```
346    pub fn quoted_text(input: &str) -> IResult<&str, &str> {
347        preceded(
348            char('"'),
349            cut(terminated(recognize(many0(quoted_char)), char('"'))),
350        )(input)
351    }
352
353    /// ```text
354    /// QuotedChar =
355    ///     %x09        ; tab
356    ///   / %x20-21     ; space - "!"
357    ///   / %x23-5B     ; "#" - "["
358    ///   / %x5D-10FFFF ; "]"+
359    ///   / EscapedChar
360    ///   / NL
361    /// ```
362    pub fn quoted_char(input: &str) -> IResult<&str, &str> {
363        alt((
364            take_while_m_n(1, 1, |c| matches!(c, '\t' | ' '..='!' | '#'..='[' | ']'..)),
365            escaped_char,
366            line_ending,
367        ))(input)
368    }
369
370    /// ```text
371    /// EscapedChar =
372    ///     Escape (Escape / DQUOTE / %s"b" / %s"f"
373    ///             / %s"n" / %s"r" / %s"t" / "/"
374    ///             / UnicodeEscape)
375    /// ```
376    pub fn escaped_char(input: &str) -> IResult<&str, &str> {
377        preceded(
378            char('\\'),
379            alt((recognize(one_of(r#"bfnrt\/""#)), unicode_escape)),
380        )(input)
381    }
382
383    /// ```text
384    /// UnicodeEscape =     %s"u" Hex Hex Hex Hex
385    /// ```
386    pub fn unicode_escape(input: &str) -> IResult<&str, &str> {
387        preceded(
388            char('u'),
389            take_while_m_n(4, 4, |c: char| c.is_ascii_hexdigit()),
390        )(input)
391    }
392}
393
394pub mod shape_id {
395    use nom::{
396        branch::alt,
397        bytes::complete::{take_while, take_while1},
398        character::complete::{alpha1, alphanumeric1, char},
399        combinator::{opt, recognize},
400        multi::separated_list1,
401        sequence::{preceded, separated_pair, tuple},
402        IResult,
403    };
404
405    /// ```text
406    /// ShapeId = RootShapeId [ShapeIdMember]
407    /// ```
408    pub fn shape_id(input: &str) -> IResult<&str, &str> {
409        recognize(tuple((root_shape_id, opt(shape_id_member))))(input)
410    }
411
412    /// ```text
413    /// RootShapeId = AbsoluteRootShapeId / Identifier
414    /// ```
415    pub fn root_shape_id(input: &str) -> IResult<&str, &str> {
416        alt((absolute_root_shape_id, identifier))(input)
417    }
418
419    /// ```text
420    /// AbsoluteRootShapeId = Namespace "#" Identifier
421    /// ```
422    pub fn absolute_root_shape_id(input: &str) -> IResult<&str, &str> {
423        recognize(separated_pair(namespace, char('#'), identifier))(input)
424    }
425
426    /// ```text
427    /// Namespace = Identifier *("." Identifier)
428    /// ```
429    pub fn namespace(input: &str) -> IResult<&str, &str> {
430        recognize(separated_list1(char('.'), identifier))(input)
431    }
432
433    /// ```text
434    /// IdentifierStart *IdentifierChars
435    /// ```
436    pub fn identifier(input: &str) -> IResult<&str, &str> {
437        recognize(tuple((identifier_start, take_while(is_identifier_char))))(input)
438    }
439
440    /// ```text
441    /// IdentifierStart = (1*"_" (ALPHA / DIGIT)) / ALPHA
442    /// ```
443    pub fn identifier_start(input: &str) -> IResult<&str, &str> {
444        alt((preceded(take_while1(|v| v == '_'), alphanumeric1), alpha1))(input)
445    }
446
447    /// ```text
448    /// IdentifierChars = ALPHA / DIGIT / "_"
449    /// ```
450    #[must_use]
451    pub fn is_identifier_char(c: char) -> bool {
452        c.is_ascii_alphanumeric() || c == '_'
453    }
454
455    /// ```text
456    /// ShapeIdMember = "$" Identifier
457    /// ```
458    pub fn shape_id_member(input: &str) -> IResult<&str, &str> {
459        preceded(char('$'), identifier)(input)
460    }
461}
462
463pub mod shapes {
464    use nom::{
465        branch::alt,
466        bytes::complete::tag,
467        character::complete::{char, space0, space1},
468        combinator::{cut, map, opt},
469        multi::{many0, separated_list0, separated_list1},
470        sequence::{delimited, preceded, separated_pair, tuple},
471        IResult,
472    };
473
474    use crate::{
475        node_values::{node_object, node_value, NodeKeyValuePair, NodeValue},
476        shape_id::{absolute_root_shape_id, identifier, namespace, shape_id},
477        traits::{apply_statement, trait_statements, ApplyStatement, Trait},
478        whitespace::{br, comma, ws},
479    };
480
481    #[derive(Debug, Clone, PartialEq, Eq)]
482    pub struct ShapeSection<'a> {
483        pub namespace: &'a str,
484        pub uses: Vec<&'a str>,
485        pub shapes: Vec<ShapeOrApply<'a>>,
486    }
487
488    #[derive(Debug, Clone, PartialEq, Eq)]
489    pub enum ShapeOrApply<'a> {
490        Shape(ShapeWithTraits<'a>),
491        Apply(ApplyStatement<'a>),
492    }
493
494    #[derive(Debug, Clone, PartialEq, Eq)]
495    pub struct ShapeWithTraits<'a> {
496        pub traits: Vec<Trait<'a>>,
497        pub shape: Shape<'a>,
498    }
499
500    #[derive(Debug, Clone, PartialEq, Eq)]
501    pub enum Shape<'a> {
502        Simple(SimpleShape<'a>),
503        Enum(EnumShape<'a>),
504        Aggregate(AggregateShape<'a>),
505        Entity(EntityShape<'a>),
506        Operation(OperationShape<'a>),
507    }
508
509    #[derive(Debug, Clone, PartialEq, Eq)]
510    pub struct SimpleShape<'a> {
511        pub type_name: SimpleTypeName,
512        pub identifier: &'a str,
513        pub mixins: Vec<&'a str>,
514    }
515
516    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
517    pub enum SimpleTypeName {
518        Blob,
519        Boolean,
520        Document,
521        String,
522        Byte,
523        Short,
524        Integer,
525        Long,
526        Float,
527        Double,
528        BigInteger,
529        BigDecimal,
530        Timestamp,
531    }
532
533    #[derive(Debug, Clone, PartialEq, Eq)]
534    pub struct EnumShape<'a> {
535        pub type_name: EnumTypeName,
536        pub identifier: &'a str,
537        pub mixins: Vec<&'a str>,
538        pub members: Vec<EnumShapeMember<'a>>,
539    }
540
541    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
542    pub enum EnumTypeName {
543        Enum,
544        IntEnum,
545    }
546
547    #[derive(Debug, Clone, PartialEq, Eq)]
548    pub struct EnumShapeMember<'a> {
549        pub traits: Vec<Trait<'a>>,
550        pub identifier: &'a str,
551        pub value: Option<NodeValue<'a>>,
552    }
553
554    #[derive(Debug, Clone, PartialEq, Eq)]
555    pub struct AggregateShape<'a> {
556        pub type_name: AggregateTypeName,
557        pub identifier: &'a str,
558        pub for_resource: Option<&'a str>,
559        pub mixins: Vec<&'a str>,
560        pub members: Vec<ShapeMember<'a>>,
561    }
562
563    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
564    pub enum AggregateTypeName {
565        Structure,
566        Union,
567        Map,
568        List,
569    }
570
571    #[derive(Debug, Clone, PartialEq, Eq)]
572    pub struct ShapeMember<'a> {
573        pub traits: Vec<Trait<'a>>,
574        pub identifier: &'a str,
575        pub shape_id: Option<&'a str>,
576        pub value: Option<NodeValue<'a>>,
577    }
578
579    #[derive(Debug, Clone, PartialEq, Eq)]
580    pub struct EntityShape<'a> {
581        pub type_name: EntityTypeName,
582        pub identifier: &'a str,
583        pub mixins: Vec<&'a str>,
584        pub nodes: Vec<NodeKeyValuePair<'a>>,
585    }
586
587    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
588    pub enum EntityTypeName {
589        Resource,
590        Service,
591    }
592
593    #[derive(Debug, Clone, PartialEq, Eq)]
594    pub struct OperationShape<'a> {
595        pub identifier: &'a str,
596        pub mixins: Vec<&'a str>,
597        pub body: Vec<OperationProperty<'a>>,
598    }
599
600    #[derive(Debug, Clone, PartialEq, Eq)]
601    pub enum OperationProperty<'a> {
602        Input(OperationPropertyShape<'a>),
603        Output(OperationPropertyShape<'a>),
604        Errors(Vec<&'a str>),
605    }
606
607    #[derive(Debug, Clone, PartialEq, Eq)]
608    pub enum OperationPropertyShape<'a> {
609        Explicit(&'a str),
610        Inline(InlineAggregateShape<'a>),
611    }
612
613    #[derive(Debug, Clone, PartialEq, Eq)]
614    pub struct InlineAggregateShape<'a> {
615        pub traits: Vec<Trait<'a>>,
616        pub for_resource: Option<&'a str>,
617        pub mixins: Vec<&'a str>,
618        pub members: Vec<ShapeMember<'a>>,
619    }
620
621    /// ```text
622    /// ShapeSection = [NamespaceStatement UseSection [ShapeStatements]]
623    /// ```
624    pub fn shape_section(input: &str) -> IResult<&str, Option<ShapeSection<'_>>> {
625        opt(map(
626            tuple((namespace_statement, use_section, shape_statements)),
627            |(namespace, uses, shapes)| ShapeSection {
628                namespace,
629                uses,
630                shapes,
631            },
632        ))(input)
633    }
634
635    /// ```text
636    /// NamespaceStatement = %s"namespace" SP Namespace BR
637    /// ```
638    pub fn namespace_statement(input: &str) -> IResult<&str, &str> {
639        preceded(tag("namespace"), cut(delimited(space1, namespace, br)))(input)
640    }
641
642    /// ```text
643    /// UseSection = *(UseStatement)
644    /// ```
645    pub fn use_section(input: &str) -> IResult<&str, Vec<&str>> {
646        many0(use_statement)(input)
647    }
648
649    /// ```text
650    /// UseStatement = %s"use" SP AbsoluteRootShapeId BR
651    /// ```
652    pub fn use_statement(input: &str) -> IResult<&str, &str> {
653        preceded(
654            tag("use"),
655            cut(delimited(space1, absolute_root_shape_id, br)),
656        )(input)
657    }
658
659    /// ```text
660    /// ShapeOrApplyStatement *(BR ShapeOrApplyStatement)
661    /// ```
662    pub fn shape_statements(input: &str) -> IResult<&str, Vec<ShapeOrApply<'_>>> {
663        separated_list0(br, shape_or_apply_statement)(input)
664    }
665
666    /// ```text
667    /// ShapeOrApplyStatement = ShapeStatement / ApplyStatement
668    /// ```
669    pub fn shape_or_apply_statement(input: &str) -> IResult<&str, ShapeOrApply<'_>> {
670        alt((
671            map(shape_statement, ShapeOrApply::Shape),
672            map(apply_statement, ShapeOrApply::Apply),
673        ))(input)
674    }
675
676    /// ```text
677    /// ShapeStatement = TraitStatements Shape
678    /// ```
679    pub fn shape_statement(input: &str) -> IResult<&str, ShapeWithTraits<'_>> {
680        map(tuple((trait_statements, shape)), |(traits, shape)| {
681            ShapeWithTraits { traits, shape }
682        })(input)
683    }
684
685    /// ```text
686    /// Shape =
687    ///     SimpleShape
688    ///   / EnumShape
689    ///   / AggregateShape
690    ///   / EntityShape
691    ///   / OperationShape
692    /// ```
693    pub fn shape(input: &str) -> IResult<&str, Shape<'_>> {
694        alt((
695            map(simple_shape, Shape::Simple),
696            map(enum_shape, Shape::Enum),
697            map(aggregate_shape, Shape::Aggregate),
698            map(entity_shape, Shape::Entity),
699            map(operation_shape, Shape::Operation),
700        ))(input)
701    }
702
703    /// ```text
704    /// SimpleShape = SimpleTypeName SP Identifier [Mixins]
705    /// ```
706    pub fn simple_shape(input: &str) -> IResult<&str, SimpleShape<'_>> {
707        map(
708            tuple((
709                simple_type_name,
710                cut(preceded(space1, identifier)),
711                opt(mixins),
712            )),
713            |(type_name, identifier, mixins)| SimpleShape {
714                type_name,
715                identifier,
716                mixins: mixins.unwrap_or_default(),
717            },
718        )(input)
719    }
720
721    /// ```text
722    /// SimpleTypeName =
723    ///   %s"blob" / %s"boolean" / %s"document" / %s"string"
724    /// / %s"byte" / %s"short" / %s"integer" / %s"long"
725    /// / %s"float" / %s"double" / %s"bigInteger"
726    /// / %s"bigDecimal" / %s"timestamp"
727    /// ```
728    pub fn simple_type_name(input: &str) -> IResult<&str, SimpleTypeName> {
729        alt((
730            map(tag("blob"), |_| SimpleTypeName::Blob),
731            map(tag("boolean"), |_| SimpleTypeName::Boolean),
732            map(tag("document"), |_| SimpleTypeName::Document),
733            map(tag("string"), |_| SimpleTypeName::String),
734            map(tag("byte"), |_| SimpleTypeName::Byte),
735            map(tag("short"), |_| SimpleTypeName::Short),
736            map(tag("integer"), |_| SimpleTypeName::Integer),
737            map(tag("long"), |_| SimpleTypeName::Long),
738            map(tag("float"), |_| SimpleTypeName::Float),
739            map(tag("double"), |_| SimpleTypeName::Double),
740            map(tag("bigInteger"), |_| SimpleTypeName::BigInteger),
741            map(tag("bigDecimal"), |_| SimpleTypeName::BigDecimal),
742            map(tag("timestamp"), |_| SimpleTypeName::Timestamp),
743        ))(input)
744    }
745
746    /// ```text
747    /// Mixins = [SP] %s"with" [WS] "[" [WS] 1*(ShapeId [WS]) "]"
748    /// ```
749    pub fn mixins(input: &str) -> IResult<&str, Vec<&str>> {
750        preceded(
751            space0,
752            preceded(
753                tag("with"),
754                cut(preceded(
755                    opt(ws),
756                    delimited(char('['), separated_list1(ws, shape_id), char(']')),
757                )),
758            ),
759        )(input)
760    }
761
762    /// ```text
763    /// EnumShape = EnumTypeName SP Identifier [Mixins] [WS] EnumShapeMembers
764    /// ```
765    pub fn enum_shape(input: &str) -> IResult<&str, EnumShape<'_>> {
766        map(
767            tuple((
768                enum_type_name,
769                cut(preceded(space1, identifier)),
770                opt(mixins),
771                cut(preceded(opt(ws), enum_shape_members)),
772            )),
773            |(type_name, identifier, mixins, members)| EnumShape {
774                type_name,
775                identifier,
776                mixins: mixins.unwrap_or_default(),
777                members,
778            },
779        )(input)
780    }
781
782    /// ```text
783    /// EnumTypeName = %s"enum" / %s"intEnum"
784    /// ```
785    pub fn enum_type_name(input: &str) -> IResult<&str, EnumTypeName> {
786        alt((
787            map(tag("enum"), |_| EnumTypeName::Enum),
788            map(tag("intEnum"), |_| EnumTypeName::IntEnum),
789        ))(input)
790    }
791
792    /// ```text
793    /// EnumShapeMembers = "{" [WS] 1*(EnumShapeMember [WS]) "}"
794    /// ```
795    pub fn enum_shape_members(input: &str) -> IResult<&str, Vec<EnumShapeMember<'_>>> {
796        delimited(
797            char('{'),
798            delimited(opt(ws), separated_list0(ws, enum_shape_member), opt(ws)),
799            char('}'),
800        )(input)
801    }
802
803    /// ```text
804    /// EnumShapeMember = TraitStatements Identifier [ValueAssignment]
805    /// ```
806    pub fn enum_shape_member(input: &str) -> IResult<&str, EnumShapeMember<'_>> {
807        map(
808            tuple((trait_statements, identifier, opt(value_assignment))),
809            |(traits, identifier, value)| EnumShapeMember {
810                traits,
811                identifier,
812                value,
813            },
814        )(input)
815    }
816
817    /// ```text
818    /// ValueAssignment = [SP] "=" [SP] NodeValue [SP] [Comma] BR
819    /// ```
820    pub fn value_assignment(input: &str) -> IResult<&str, NodeValue<'_>> {
821        delimited(
822            tuple((space0, char('='), space0)),
823            node_value,
824            opt(tuple((space0, comma))),
825        )(input)
826    }
827
828    /// ```text
829    /// AggregateShape =
830    ///     AggregateTypeName SP Identifier [ForResource] [Mixins]
831    ///      [WS] ShapeMembers
832    /// ```
833    pub fn aggregate_shape(input: &str) -> IResult<&str, AggregateShape<'_>> {
834        map(
835            tuple((
836                aggregate_shape_name,
837                cut(preceded(space1, identifier)),
838                opt(for_resource),
839                opt(mixins),
840                cut(preceded(opt(ws), shape_members)),
841            )),
842            |(type_name, identifier, for_resource, mixins, members)| AggregateShape {
843                type_name,
844                identifier,
845                for_resource,
846                mixins: mixins.unwrap_or_default(),
847                members,
848            },
849        )(input)
850    }
851
852    /// ```text
853    /// AggregateTypeName = %s"list" / %s"map" / %s"union" / %s"structure"
854    /// ```
855    pub fn aggregate_shape_name(input: &str) -> IResult<&str, AggregateTypeName> {
856        alt((
857            map(tag("list"), |_| AggregateTypeName::List),
858            map(tag("map"), |_| AggregateTypeName::Map),
859            map(tag("union"), |_| AggregateTypeName::Union),
860            map(tag("structure"), |_| AggregateTypeName::Structure),
861        ))(input)
862    }
863
864    /// ```text
865    /// ForResource = SP %s"for" SP ShapeId
866    /// ```
867    pub fn for_resource(input: &str) -> IResult<&str, &str> {
868        preceded(tuple((space1, tag("for"), space1)), shape_id)(input)
869    }
870
871    /// ```text
872    /// ShapeMembers = "{" [WS] *(ShapeMember [WS]) "}"
873    /// ```
874    pub fn shape_members(input: &str) -> IResult<&str, Vec<ShapeMember<'_>>> {
875        delimited(
876            tuple((char('{'), opt(ws))),
877            separated_list0(ws, shape_member),
878            tuple((opt(ws), char('}'))),
879        )(input)
880    }
881
882    /// ```text
883    /// ShapeMember = TraitStatements (ExplicitShapeMember / ElidedShapeMember) [ValueAssignment]
884    /// ```
885    pub fn shape_member(input: &str) -> IResult<&str, ShapeMember<'_>> {
886        map(
887            tuple((
888                trait_statements,
889                alt((
890                    map(explicit_shape_member, |(identifier, shape_name)| {
891                        (identifier, Some(shape_name))
892                    }),
893                    map(elided_shape_member, |identifier| (identifier, None)),
894                )),
895                opt(value_assignment),
896            )),
897            |(traits, (identifier, shape_id), value)| ShapeMember {
898                traits,
899                identifier,
900                shape_id,
901                value,
902            },
903        )(input)
904    }
905
906    /// ```text
907    /// ExplicitShapeMember = Identifier [SP] ":" [SP] ShapeId
908    /// ```
909    pub fn explicit_shape_member(input: &str) -> IResult<&str, (&str, &str)> {
910        separated_pair(
911            identifier,
912            tuple((space0, char(':'), space0)),
913            cut(shape_id),
914        )(input)
915    }
916
917    /// ```text
918    /// ElidedShapeMember = "$" Identifier
919    /// ```
920    pub fn elided_shape_member(input: &str) -> IResult<&str, &str> {
921        preceded(char('$'), cut(identifier))(input)
922    }
923
924    /// ```text
925    /// EntityShape = EntityTypeName SP Identifier [Mixins] [WS] NodeObject
926    /// ```
927    pub fn entity_shape(input: &str) -> IResult<&str, EntityShape<'_>> {
928        map(
929            tuple((
930                separated_pair(entity_type_name, cut(space1), cut(identifier)),
931                opt(mixins),
932                cut(preceded(opt(ws), node_object)),
933            )),
934            |((type_name, identifier), mixins, nodes)| EntityShape {
935                type_name,
936                identifier,
937                mixins: mixins.unwrap_or_default(),
938                nodes,
939            },
940        )(input)
941    }
942
943    /// ```text
944    /// EntityTypeName = %s"service" / %s"resource"
945    /// ```
946    pub fn entity_type_name(input: &str) -> IResult<&str, EntityTypeName> {
947        alt((
948            map(tag("service"), |_| EntityTypeName::Service),
949            map(tag("resource"), |_| EntityTypeName::Resource),
950        ))(input)
951    }
952
953    /// ```text
954    /// OperationShape = %s"operation" SP Identifier [Mixins] [WS] OperationBody
955    /// ```
956    pub fn operation_shape(input: &str) -> IResult<&str, OperationShape<'_>> {
957        map(
958            preceded(
959                tag("operation"),
960                cut(tuple((
961                    preceded(space1, identifier),
962                    opt(mixins),
963                    preceded(opt(ws), operation_body),
964                ))),
965            ),
966            |(identifier, mixins, body)| OperationShape {
967                identifier,
968                mixins: mixins.unwrap_or_default(),
969                body,
970            },
971        )(input)
972    }
973
974    /// ```text
975    /// OperationBody = "{" [WS] *(OperationProperty [WS]) "}"
976    /// ```
977    pub fn operation_body(input: &str) -> IResult<&str, Vec<OperationProperty<'_>>> {
978        delimited(
979            tuple((char('{'), opt(ws))),
980            separated_list0(ws, operation_property),
981            tuple((opt(ws), char('}'))),
982        )(input)
983    }
984
985    /// ```text
986    /// OperationProperty = OperationInput / OperationOutput / OperationErrors
987    /// ```
988    pub fn operation_property(input: &str) -> IResult<&str, OperationProperty<'_>> {
989        alt((
990            map(operation_input, OperationProperty::Input),
991            map(operation_output, OperationProperty::Output),
992            map(operation_errors, OperationProperty::Errors),
993        ))(input)
994    }
995
996    /// ```text
997    /// OperationInput = %s"input" [WS] (InlineAggregateShape / (":" [WS] ShapeId))
998    /// ```
999    pub fn operation_input(input: &str) -> IResult<&str, OperationPropertyShape<'_>> {
1000        preceded(tuple((tag("input"), opt(ws))), inline_or_explicit_shape)(input)
1001    }
1002
1003    /// ```text
1004    /// OperationOutput = %s"output" [WS] (InlineAggregateShape / (":" [WS] ShapeId))
1005    /// ```
1006    pub fn operation_output(input: &str) -> IResult<&str, OperationPropertyShape<'_>> {
1007        preceded(tuple((tag("output"), opt(ws))), inline_or_explicit_shape)(input)
1008    }
1009
1010    /// ```text
1011    /// OperationErrors = %s"errors" [WS] ":" [WS] "[" [WS] *(ShapeId [WS]) "]"
1012    /// ```
1013    pub fn operation_errors(input: &str) -> IResult<&str, Vec<&str>> {
1014        preceded(
1015            tuple((tag("errors"), opt(ws))),
1016            cut(preceded(
1017                tuple((char(':'), opt(ws))),
1018                delimited(
1019                    tuple((char('['), opt(ws))),
1020                    separated_list0(ws, shape_id),
1021                    tuple((opt(ws), char(']'))),
1022                ),
1023            )),
1024        )(input)
1025    }
1026
1027    pub fn inline_or_explicit_shape(input: &str) -> IResult<&str, OperationPropertyShape<'_>> {
1028        alt((
1029            map(inline_aggregate_shape, OperationPropertyShape::Inline),
1030            map(
1031                preceded(tag(":"), cut(preceded(opt(ws), shape_id))),
1032                OperationPropertyShape::Explicit,
1033            ),
1034        ))(input)
1035    }
1036
1037    /// ```text
1038    /// InlineAggregateShape = ":=" [WS] TraitStatements [ForResource] [Mixins] [WS] ShapeMembers
1039    /// ```
1040    pub fn inline_aggregate_shape(input: &str) -> IResult<&str, InlineAggregateShape<'_>> {
1041        map(
1042            preceded(
1043                tag(":="),
1044                cut(preceded(
1045                    opt(ws),
1046                    tuple((
1047                        trait_statements,
1048                        opt(for_resource),
1049                        opt(mixins),
1050                        preceded(opt(ws), shape_members),
1051                    )),
1052                )),
1053            ),
1054            |(traits, for_resource, mixins, members)| InlineAggregateShape {
1055                traits,
1056                for_resource,
1057                mixins: mixins.unwrap_or_default(),
1058                members,
1059            },
1060        )(input)
1061    }
1062}
1063
1064pub mod traits {
1065    use nom::{
1066        branch::alt,
1067        bytes::complete::tag,
1068        character::complete::{char, space1},
1069        combinator::{cut, map, opt},
1070        multi::{separated_list0, separated_list1},
1071        sequence::{delimited, preceded, terminated, tuple},
1072        IResult,
1073    };
1074
1075    use crate::{
1076        node_values::{node_object_kvp, node_value, NodeKeyValuePair, NodeValue},
1077        shape_id::shape_id,
1078        whitespace::ws,
1079    };
1080
1081    #[derive(Debug, Clone, PartialEq, Eq)]
1082    pub struct Trait<'a> {
1083        pub shape_id: &'a str,
1084        pub body: Option<TraitBody<'a>>,
1085    }
1086
1087    #[derive(Debug, Clone, PartialEq, Eq)]
1088    pub enum TraitBody<'a> {
1089        Structure(Vec<NodeKeyValuePair<'a>>),
1090        Node(NodeValue<'a>),
1091    }
1092
1093    #[derive(Debug, Clone, PartialEq, Eq)]
1094    pub struct ApplyStatement<'a> {
1095        pub shape_id: &'a str,
1096        pub traits: Vec<Trait<'a>>,
1097    }
1098
1099    /// ```text
1100    /// TraitStatements = *(Trait [WS])
1101    /// ```
1102    pub fn trait_statements(input: &str) -> IResult<&str, Vec<Trait<'_>>> {
1103        terminated(separated_list0(ws, trait_), opt(ws))(input)
1104    }
1105
1106    /// ```text
1107    /// Trait = "@" ShapeId [TraitBody]
1108    /// ```
1109    pub fn trait_(input: &str) -> IResult<&str, Trait<'_>> {
1110        map(
1111            preceded(char('@'), cut(tuple((shape_id, opt(trait_body))))),
1112            |(shape_id, body)| Trait {
1113                shape_id,
1114                body: body.flatten(),
1115            },
1116        )(input)
1117    }
1118
1119    /// ```text
1120    /// TraitBody = "(" [WS] [TraitStructure / TraitNode] ")"
1121    /// ```
1122    pub fn trait_body(input: &str) -> IResult<&str, Option<TraitBody<'_>>> {
1123        preceded(
1124            char('('),
1125            cut(terminated(
1126                preceded(
1127                    opt(ws),
1128                    opt(alt((
1129                        map(trait_structure, TraitBody::Structure),
1130                        map(trait_node, TraitBody::Node),
1131                    ))),
1132                ),
1133                char(')'),
1134            )),
1135        )(input)
1136    }
1137
1138    /// ```text
1139    /// TraitStructure = 1*(NodeObjectKvp [WS])
1140    /// ```
1141    pub fn trait_structure(input: &str) -> IResult<&str, Vec<NodeKeyValuePair<'_>>> {
1142        separated_list1(ws, node_object_kvp)(input)
1143    }
1144
1145    /// ```text
1146    /// TraitNode = NodeValue [WS]
1147    /// ```
1148    pub fn trait_node(input: &str) -> IResult<&str, NodeValue<'_>> {
1149        terminated(node_value, opt(ws))(input)
1150    }
1151
1152    /// ```text
1153    /// ApplyStatement = ApplyStatementSingular / ApplyStatementBlock
1154    /// ApplyStatementSingular = %s"apply" SP ShapeId WS Trait
1155    /// ApplyStatementBlock = %s"apply" SP ShapeId WS "{" [WS] TraitStatements "}"
1156    /// ```
1157    pub fn apply_statement(input: &str) -> IResult<&str, ApplyStatement<'_>> {
1158        let apply_statement_singular = map(trait_, |v| vec![v]);
1159        let apply_statement_block = delimited(
1160            char('{'),
1161            delimited(opt(ws), trait_statements, opt(ws)),
1162            char('}'),
1163        );
1164
1165        map(
1166            preceded(
1167                tag("apply"),
1168                cut(tuple((
1169                    delimited(space1, shape_id, ws),
1170                    alt((apply_statement_singular, apply_statement_block)),
1171                ))),
1172            ),
1173            |(shape_id, traits)| ApplyStatement { shape_id, traits },
1174        )(input)
1175    }
1176}
1177
1178#[cfg(test)]
1179#[allow(clippy::needless_raw_string_hashes)]
1180mod test {
1181    use insta::{assert_debug_snapshot, glob};
1182
1183    #[test]
1184    fn integration() {
1185        glob!("../fixtures", "*.smithy", |path| {
1186            let input = std::fs::read_to_string(path).unwrap();
1187
1188            let (rest, ast) = match crate::parse_ast(&input) {
1189                Ok(v) => v,
1190                Err(e) => panic!("Failed to parse {}: {e}", path.display()),
1191            };
1192            assert_eq!(rest, "");
1193
1194            assert_debug_snapshot!(ast);
1195        });
1196    }
1197
1198    mod control_section {
1199        use crate::{control::control_section, node_values::NodeValue};
1200
1201        #[test]
1202        fn smoke() {
1203            let (remaining, res) = control_section("$version: \"2.0\"\n$hello: 123\n").unwrap();
1204
1205            assert_eq!(remaining, "");
1206            assert_eq!(
1207                res,
1208                &[
1209                    ("version", NodeValue::String("2.0")),
1210                    ("hello", NodeValue::Number("123")),
1211                ]
1212            );
1213        }
1214    }
1215
1216    mod metadata_section {
1217        use crate::{metadata::metadata_section, node_values::NodeValue};
1218
1219        #[test]
1220        fn smoke() {
1221            let (remaining, res) = metadata_section(
1222                "metadata \"foo\" = [\"baz\", \"bar\"]\nmetadata \"qux\" = \"test\"\n",
1223            )
1224            .unwrap();
1225
1226            assert_eq!(remaining, "");
1227            assert_eq!(
1228                res,
1229                &[
1230                    (
1231                        "foo",
1232                        NodeValue::Array(vec![NodeValue::String("baz"), NodeValue::String("bar")])
1233                    ),
1234                    ("qux", NodeValue::String("test")),
1235                ]
1236            );
1237        }
1238    }
1239
1240    mod simple_shape {
1241        use crate::{
1242            node_values::{NodeKeyValuePair, NodeValue},
1243            shapes::{shape_section, ShapeOrApply, SimpleShape, SimpleTypeName},
1244            traits::{Trait, TraitBody},
1245        };
1246
1247        #[test]
1248        fn smoke() {
1249            let (remaining, res) = shape_section("namespace com.foo // This is also a comment\n\n// Another comment\nstring MyString").unwrap();
1250
1251            assert_eq!(remaining, "");
1252            assert_eq!(
1253                res,
1254                Some(crate::shapes::ShapeSection {
1255                    namespace: "com.foo",
1256                    uses: vec![],
1257                    shapes: vec![ShapeOrApply::Shape(crate::shapes::ShapeWithTraits {
1258                        traits: vec![],
1259                        shape: crate::shapes::Shape::Simple(SimpleShape {
1260                            type_name: SimpleTypeName::String,
1261                            identifier: "MyString",
1262                            mixins: vec![]
1263                        })
1264                    })]
1265                })
1266            );
1267        }
1268
1269        #[test]
1270        fn with_mixin() {
1271            let (remaining, res) =
1272                shape_section("namespace com.foo\n\nstring MyString with [IdBearer]").unwrap();
1273
1274            assert_eq!(remaining, "");
1275            assert_eq!(
1276                res,
1277                Some(crate::shapes::ShapeSection {
1278                    namespace: "com.foo",
1279                    uses: vec![],
1280                    shapes: vec![ShapeOrApply::Shape(crate::shapes::ShapeWithTraits {
1281                        traits: vec![],
1282                        shape: crate::shapes::Shape::Simple(SimpleShape {
1283                            type_name: SimpleTypeName::String,
1284                            identifier: "MyString",
1285                            mixins: vec!["IdBearer"]
1286                        })
1287                    })]
1288                })
1289            );
1290        }
1291
1292        #[test]
1293        fn with_multiple_mixins() {
1294            let (remaining, res) =
1295                shape_section("namespace com.foo\n\nstring MyString with [IdBearer, Abc]").unwrap();
1296
1297            assert_eq!(remaining, "");
1298            assert_eq!(
1299                res,
1300                Some(crate::shapes::ShapeSection {
1301                    namespace: "com.foo",
1302                    uses: vec![],
1303                    shapes: vec![ShapeOrApply::Shape(crate::shapes::ShapeWithTraits {
1304                        traits: vec![],
1305                        shape: crate::shapes::Shape::Simple(SimpleShape {
1306                            type_name: SimpleTypeName::String,
1307                            identifier: "MyString",
1308                            mixins: vec!["IdBearer", "Abc"]
1309                        })
1310                    })]
1311                })
1312            );
1313        }
1314
1315        #[test]
1316        fn with_simple_trait() {
1317            let (remaining, res) =
1318                shape_section("namespace com.foo\n\n@myTrait\nstring MyString").unwrap();
1319
1320            assert_eq!(remaining, "");
1321            assert_eq!(
1322                res,
1323                Some(crate::shapes::ShapeSection {
1324                    namespace: "com.foo",
1325                    uses: vec![],
1326                    shapes: vec![ShapeOrApply::Shape(crate::shapes::ShapeWithTraits {
1327                        traits: vec![Trait {
1328                            shape_id: "myTrait",
1329                            body: None,
1330                        }],
1331                        shape: crate::shapes::Shape::Simple(SimpleShape {
1332                            type_name: SimpleTypeName::String,
1333                            identifier: "MyString",
1334                            mixins: vec![]
1335                        })
1336                    })]
1337                })
1338            );
1339        }
1340
1341        #[test]
1342        fn with_kv_trait() {
1343            let (remaining, res) = shape_section("namespace com.foo \n\n@myTrait(key: \"value\", otherKey: \"otherValue\")\nstring MyString").unwrap();
1344
1345            assert_eq!(remaining, "");
1346            assert_eq!(
1347                res,
1348                Some(crate::shapes::ShapeSection {
1349                    namespace: "com.foo",
1350                    uses: vec![],
1351                    shapes: vec![ShapeOrApply::Shape(crate::shapes::ShapeWithTraits {
1352                        traits: vec![Trait {
1353                            shape_id: "myTrait",
1354                            body: Some(TraitBody::Structure(vec![
1355                                NodeKeyValuePair {
1356                                    key: "key",
1357                                    value: NodeValue::String("value"),
1358                                },
1359                                NodeKeyValuePair {
1360                                    key: "otherKey",
1361                                    value: NodeValue::String("otherValue"),
1362                                }
1363                            ])),
1364                        }],
1365                        shape: crate::shapes::Shape::Simple(SimpleShape {
1366                            type_name: SimpleTypeName::String,
1367                            identifier: "MyString",
1368                            mixins: vec![]
1369                        })
1370                    })]
1371                })
1372            );
1373        }
1374
1375        #[test]
1376        fn with_node_trait() {
1377            let (remaining, res) =
1378                shape_section("namespace com.foo\n@myTrait(123)\nstring MyString").unwrap();
1379
1380            assert_eq!(remaining, "");
1381            assert_eq!(
1382                res,
1383                Some(crate::shapes::ShapeSection {
1384                    namespace: "com.foo",
1385                    uses: vec![],
1386                    shapes: vec![ShapeOrApply::Shape(crate::shapes::ShapeWithTraits {
1387                        traits: vec![Trait {
1388                            shape_id: "myTrait",
1389                            body: Some(TraitBody::Node(NodeValue::Number("123"))),
1390                        }],
1391                        shape: crate::shapes::Shape::Simple(SimpleShape {
1392                            type_name: SimpleTypeName::String,
1393                            identifier: "MyString",
1394                            mixins: vec![]
1395                        })
1396                    })]
1397                })
1398            );
1399        }
1400
1401        #[test]
1402        fn with_multiple_traits() {
1403            let (remaining, res) =
1404                shape_section("namespace com.foo\n@myTrait\n@myOtherTrait\nstring MyString")
1405                    .unwrap();
1406
1407            assert_eq!(remaining, "");
1408            assert_eq!(
1409                res,
1410                Some(crate::shapes::ShapeSection {
1411                    namespace: "com.foo",
1412                    uses: vec![],
1413                    shapes: vec![ShapeOrApply::Shape(crate::shapes::ShapeWithTraits {
1414                        traits: vec![
1415                            Trait {
1416                                shape_id: "myTrait",
1417                                body: None,
1418                            },
1419                            Trait {
1420                                shape_id: "myOtherTrait",
1421                                body: None,
1422                            }
1423                        ],
1424                        shape: crate::shapes::Shape::Simple(SimpleShape {
1425                            type_name: SimpleTypeName::String,
1426                            identifier: "MyString",
1427                            mixins: vec![]
1428                        })
1429                    })]
1430                })
1431            );
1432        }
1433    }
1434
1435    mod enum_shape {
1436        use crate::{
1437            node_values::NodeValue,
1438            shapes::{
1439                shape_section, EnumShape, EnumShapeMember, EnumTypeName, Shape, ShapeOrApply,
1440                ShapeWithTraits,
1441            },
1442            traits::Trait,
1443        };
1444
1445        #[test]
1446        fn simple() {
1447            let (remaining, res) = shape_section(
1448                r#"namespace smithy.example
1449
1450enum Suit {
1451    DIAMOND
1452    CLUB
1453    HEART
1454    SPADE
1455}"#,
1456            )
1457            .unwrap();
1458
1459            assert_eq!(remaining, "");
1460            assert_eq!(
1461                res,
1462                Some(crate::shapes::ShapeSection {
1463                    namespace: "smithy.example",
1464                    uses: vec![],
1465                    shapes: vec![ShapeOrApply::Shape(ShapeWithTraits {
1466                        traits: vec![],
1467                        shape: Shape::Enum(EnumShape {
1468                            type_name: EnumTypeName::Enum,
1469                            identifier: "Suit",
1470                            mixins: vec![],
1471                            members: vec![
1472                                EnumShapeMember {
1473                                    traits: vec![],
1474                                    identifier: "DIAMOND",
1475                                    value: None,
1476                                },
1477                                EnumShapeMember {
1478                                    traits: vec![],
1479                                    identifier: "CLUB",
1480                                    value: None,
1481                                },
1482                                EnumShapeMember {
1483                                    traits: vec![],
1484                                    identifier: "HEART",
1485                                    value: None,
1486                                },
1487                                EnumShapeMember {
1488                                    traits: vec![],
1489                                    identifier: "SPADE",
1490                                    value: None,
1491                                },
1492                            ]
1493                        })
1494                    })]
1495                })
1496            );
1497        }
1498
1499        #[test]
1500        fn with_value() {
1501            let (remaining, res) = shape_section(
1502                r#"namespace smithy.example
1503
1504enum Suit {
1505    @deprecated
1506    DIAMOND = "diamond"
1507
1508    CLUB = "club" HEART = "heart"
1509    @deprecated
1510    SPADE = "spade"
1511}"#,
1512            )
1513            .unwrap();
1514
1515            assert_eq!(remaining, "");
1516            assert_eq!(
1517                res,
1518                Some(crate::shapes::ShapeSection {
1519                    namespace: "smithy.example",
1520                    uses: vec![],
1521                    shapes: vec![ShapeOrApply::Shape(ShapeWithTraits {
1522                        traits: vec![],
1523                        shape: Shape::Enum(EnumShape {
1524                            type_name: EnumTypeName::Enum,
1525                            identifier: "Suit",
1526                            mixins: vec![],
1527                            members: vec![
1528                                EnumShapeMember {
1529                                    traits: vec![Trait {
1530                                        shape_id: "deprecated",
1531                                        body: None,
1532                                    }],
1533                                    identifier: "DIAMOND",
1534                                    value: Some(NodeValue::String("diamond")),
1535                                },
1536                                EnumShapeMember {
1537                                    traits: vec![],
1538                                    identifier: "CLUB",
1539                                    value: Some(NodeValue::String("club")),
1540                                },
1541                                EnumShapeMember {
1542                                    traits: vec![],
1543                                    identifier: "HEART",
1544                                    value: Some(NodeValue::String("heart")),
1545                                },
1546                                EnumShapeMember {
1547                                    traits: vec![Trait {
1548                                        shape_id: "deprecated",
1549                                        body: None,
1550                                    }],
1551                                    identifier: "SPADE",
1552                                    value: Some(NodeValue::String("spade")),
1553                                },
1554                            ]
1555                        })
1556                    })]
1557                })
1558            );
1559        }
1560    }
1561
1562    mod apply_statement {
1563        use crate::{
1564            node_values::{NodeKeyValuePair, NodeValue},
1565            shapes::{shape_section, ShapeOrApply},
1566            traits::{Trait, TraitBody},
1567        };
1568
1569        #[test]
1570        fn single() {
1571            let (remaining, res) = shape_section(
1572                "namespace com.foo\napply MyString @documentation(\"This is my string!\")",
1573            )
1574            .unwrap();
1575
1576            assert_eq!(remaining, "");
1577            assert_eq!(
1578                res,
1579                Some(crate::shapes::ShapeSection {
1580                    namespace: "com.foo",
1581                    uses: vec![],
1582                    shapes: vec![ShapeOrApply::Apply(crate::traits::ApplyStatement {
1583                        shape_id: "MyString",
1584                        traits: vec![Trait {
1585                            shape_id: "documentation",
1586                            body: Some(TraitBody::Node(NodeValue::String("This is my string!"))),
1587                        }],
1588                    })]
1589                })
1590            );
1591        }
1592
1593        #[test]
1594        fn multiple() {
1595            let (remaining, res) = shape_section(
1596                "namespace com.foo\napply MyString {\n    @documentation(\"This is my string!\")\n    @length(min: 1, max: 10)\n}",
1597            )
1598            .unwrap();
1599
1600            assert_eq!(remaining, "");
1601            assert_eq!(
1602                res,
1603                Some(crate::shapes::ShapeSection {
1604                    namespace: "com.foo",
1605                    uses: vec![],
1606                    shapes: vec![ShapeOrApply::Apply(crate::traits::ApplyStatement {
1607                        shape_id: "MyString",
1608                        traits: vec![
1609                            Trait {
1610                                shape_id: "documentation",
1611                                body: Some(TraitBody::Node(NodeValue::String(
1612                                    "This is my string!"
1613                                ))),
1614                            },
1615                            Trait {
1616                                shape_id: "length",
1617                                body: Some(TraitBody::Structure(vec![
1618                                    NodeKeyValuePair {
1619                                        key: "min",
1620                                        value: NodeValue::Number("1")
1621                                    },
1622                                    NodeKeyValuePair {
1623                                        key: "max",
1624                                        value: NodeValue::Number("10"),
1625                                    },
1626                                ])),
1627                            }
1628                        ],
1629                    })]
1630                })
1631            );
1632        }
1633    }
1634
1635    mod aggregate {
1636        use crate::shapes::{
1637            shape_section, AggregateShape, AggregateTypeName, Shape, ShapeMember, ShapeOrApply,
1638            ShapeSection, ShapeWithTraits,
1639        };
1640
1641        #[test]
1642        fn structure() {
1643            let (remaining, res) = shape_section(
1644                r#"namespace smithy.example
1645
1646structure MyStructure for Test {
1647    a: MyString
1648    b: smithy.example#MyString
1649    $c
1650}"#,
1651            )
1652            .unwrap();
1653
1654            assert_eq!(remaining, "");
1655            assert_eq!(
1656                res,
1657                Some(ShapeSection {
1658                    namespace: "smithy.example",
1659                    uses: vec![],
1660                    shapes: vec![ShapeOrApply::Shape(ShapeWithTraits {
1661                        traits: vec![],
1662                        shape: Shape::Aggregate(AggregateShape {
1663                            type_name: AggregateTypeName::Structure,
1664                            identifier: "MyStructure",
1665                            for_resource: Some("Test"),
1666                            mixins: vec![],
1667                            members: vec![
1668                                ShapeMember {
1669                                    traits: vec![],
1670                                    identifier: "a",
1671                                    shape_id: Some("MyString"),
1672                                    value: None,
1673                                },
1674                                ShapeMember {
1675                                    traits: vec![],
1676                                    identifier: "b",
1677                                    shape_id: Some("smithy.example#MyString"),
1678                                    value: None,
1679                                },
1680                                ShapeMember {
1681                                    traits: vec![],
1682                                    identifier: "c",
1683                                    shape_id: None,
1684                                    value: None,
1685                                },
1686                            ],
1687                        }),
1688                    })],
1689                })
1690            );
1691        }
1692    }
1693
1694    mod entity {
1695        use crate::{
1696            node_values::{NodeKeyValuePair, NodeValue},
1697            shapes::{
1698                shape_section, EntityShape, EntityTypeName, Shape, ShapeOrApply, ShapeSection,
1699                ShapeWithTraits,
1700            },
1701        };
1702
1703        #[test]
1704        fn service() {
1705            let (remaining, res) = shape_section(
1706                r#"namespace smithy.example
1707
1708service ModelRepository {
1709    version: "2020-07-13",
1710    resources: [Model],
1711    operations: [PingService]
1712}"#,
1713            )
1714            .unwrap();
1715
1716            assert_eq!(remaining, "");
1717            assert_eq!(
1718                res,
1719                Some(ShapeSection {
1720                    namespace: "smithy.example",
1721                    uses: vec![],
1722                    shapes: vec![ShapeOrApply::Shape(ShapeWithTraits {
1723                        traits: vec![],
1724                        shape: Shape::Entity(EntityShape {
1725                            type_name: EntityTypeName::Service,
1726                            identifier: "ModelRepository",
1727                            mixins: vec![],
1728                            nodes: vec![
1729                                NodeKeyValuePair {
1730                                    key: "version",
1731                                    value: NodeValue::String("2020-07-13"),
1732                                },
1733                                NodeKeyValuePair {
1734                                    key: "resources",
1735                                    value: NodeValue::Array(vec![NodeValue::String("Model")]),
1736                                },
1737                                NodeKeyValuePair {
1738                                    key: "operations",
1739                                    value: NodeValue::Array(vec![NodeValue::String("PingService")]),
1740                                },
1741                            ],
1742                        },),
1743                    },),],
1744                },)
1745            );
1746        }
1747    }
1748
1749    mod operation {
1750        use crate::shapes::{
1751            shape_section, InlineAggregateShape, OperationProperty, OperationPropertyShape,
1752            OperationShape, Shape, ShapeMember, ShapeOrApply, ShapeSection, ShapeWithTraits,
1753        };
1754
1755        #[test]
1756        fn smoke() {
1757            let (remaining, res) = shape_section(
1758                r#"namespace smithy.example
1759
1760operation PingService {
1761    input: PingServiceInput,
1762    output := {
1763        username: String
1764        userId: String
1765    }
1766    errors: [UnavailableError, BadRequestError]
1767}"#,
1768            )
1769            .unwrap();
1770
1771            assert_eq!(remaining, "");
1772            assert_eq!(
1773                res,
1774                Some(ShapeSection {
1775                    namespace: "smithy.example",
1776                    uses: vec![],
1777                    shapes: vec![ShapeOrApply::Shape(ShapeWithTraits {
1778                        traits: vec![],
1779                        shape: Shape::Operation(OperationShape {
1780                            identifier: "PingService",
1781                            mixins: vec![],
1782                            body: vec![
1783                                OperationProperty::Input(OperationPropertyShape::Explicit(
1784                                    "PingServiceInput",
1785                                )),
1786                                OperationProperty::Output(OperationPropertyShape::Inline(
1787                                    InlineAggregateShape {
1788                                        traits: vec![],
1789                                        for_resource: None,
1790                                        mixins: vec![],
1791                                        members: vec![
1792                                            ShapeMember {
1793                                                traits: vec![],
1794                                                identifier: "username",
1795                                                shape_id: Some("String"),
1796                                                value: None,
1797                                            },
1798                                            ShapeMember {
1799                                                traits: vec![],
1800                                                identifier: "userId",
1801                                                shape_id: Some("String"),
1802                                                value: None,
1803                                            }
1804                                        ]
1805                                    }
1806                                )),
1807                                OperationProperty::Errors(vec![
1808                                    "UnavailableError",
1809                                    "BadRequestError"
1810                                ]),
1811                            ],
1812                        }),
1813                    })],
1814                })
1815            );
1816        }
1817    }
1818}