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#[derive(Debug, Clone)]
15pub struct Ast<'a> {
16 pub control: Vec<(&'a str, NodeValue<'a>)>,
18 pub metadata: Vec<(&'a str, NodeValue<'a>)>,
20 pub shapes: Option<ShapeSection<'a>>,
22}
23
24pub fn parse_ast(input: &str) -> IResult<&str, Ast<'_>> {
26 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 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 pub fn documentation_comment(input: &str) -> IResult<&str, Option<&str>> {
71 delimited(tag("///"), opt(not_line_ending), line_ending)(input)
72 }
73
74 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 pub fn ws(input: &str) -> IResult<&str, Vec<&str>> {
96 many1(alt((multispace1, super::comment::comment, comma)))(input)
97 }
98
99 pub fn comma(input: &str) -> IResult<&str, &str> {
103 recognize(char(','))(input)
104 }
105
106 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 pub fn control_section(input: &str) -> IResult<&str, Vec<(&str, NodeValue<'_>)>> {
136 many0(control_statement)(input)
137 }
138
139 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 pub fn metadata_section(input: &str) -> IResult<&str, Vec<(&str, NodeValue<'_>)>> {
176 many0(metadata_statement)(input)
177 }
178
179 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 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 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 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 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 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 pub fn node_string_value(input: &str) -> IResult<&str, &str> {
309 alt((shape_id, text_block, quoted_text))(input)
310 }
311
312 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 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 pub fn node_object_key(input: &str) -> IResult<&str, &str> {
340 alt((quoted_text, identifier))(input)
341 }
342
343 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 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 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 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 pub fn shape_id(input: &str) -> IResult<&str, &str> {
409 recognize(tuple((root_shape_id, opt(shape_id_member))))(input)
410 }
411
412 pub fn root_shape_id(input: &str) -> IResult<&str, &str> {
416 alt((absolute_root_shape_id, identifier))(input)
417 }
418
419 pub fn absolute_root_shape_id(input: &str) -> IResult<&str, &str> {
423 recognize(separated_pair(namespace, char('#'), identifier))(input)
424 }
425
426 pub fn namespace(input: &str) -> IResult<&str, &str> {
430 recognize(separated_list1(char('.'), identifier))(input)
431 }
432
433 pub fn identifier(input: &str) -> IResult<&str, &str> {
437 recognize(tuple((identifier_start, take_while(is_identifier_char))))(input)
438 }
439
440 pub fn identifier_start(input: &str) -> IResult<&str, &str> {
444 alt((preceded(take_while1(|v| v == '_'), alphanumeric1), alpha1))(input)
445 }
446
447 #[must_use]
451 pub fn is_identifier_char(c: char) -> bool {
452 c.is_ascii_alphanumeric() || c == '_'
453 }
454
455 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 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 pub fn namespace_statement(input: &str) -> IResult<&str, &str> {
639 preceded(tag("namespace"), cut(delimited(space1, namespace, br)))(input)
640 }
641
642 pub fn use_section(input: &str) -> IResult<&str, Vec<&str>> {
646 many0(use_statement)(input)
647 }
648
649 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 pub fn shape_statements(input: &str) -> IResult<&str, Vec<ShapeOrApply<'_>>> {
663 separated_list0(br, shape_or_apply_statement)(input)
664 }
665
666 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 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 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 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 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 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 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 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 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 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 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 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 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 pub fn for_resource(input: &str) -> IResult<&str, &str> {
868 preceded(tuple((space1, tag("for"), space1)), shape_id)(input)
869 }
870
871 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 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 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 pub fn elided_shape_member(input: &str) -> IResult<&str, &str> {
921 preceded(char('$'), cut(identifier))(input)
922 }
923
924 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 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 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 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 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 pub fn operation_input(input: &str) -> IResult<&str, OperationPropertyShape<'_>> {
1000 preceded(tuple((tag("input"), opt(ws))), inline_or_explicit_shape)(input)
1001 }
1002
1003 pub fn operation_output(input: &str) -> IResult<&str, OperationPropertyShape<'_>> {
1007 preceded(tuple((tag("output"), opt(ws))), inline_or_explicit_shape)(input)
1008 }
1009
1010 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 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 pub fn trait_statements(input: &str) -> IResult<&str, Vec<Trait<'_>>> {
1103 terminated(separated_list0(ws, trait_), opt(ws))(input)
1104 }
1105
1106 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 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 pub fn trait_structure(input: &str) -> IResult<&str, Vec<NodeKeyValuePair<'_>>> {
1142 separated_list1(ws, node_object_kvp)(input)
1143 }
1144
1145 pub fn trait_node(input: &str) -> IResult<&str, NodeValue<'_>> {
1149 terminated(node_value, opt(ws))(input)
1150 }
1151
1152 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}