1use nom::{
2 branch::alt,
3 combinator::map,
4 multi::separated_list0,
5 sequence::{preceded, terminated},
6 IResult,
7};
8
9use crate::common::FilePosition;
10use crate::idl::common::Span;
11use crate::idl::errors::ParseError;
12use crate::idl::namespace::Namespace;
13
14use super::{
15 common::{ws, ws1},
16 include::{parse_include, Include},
17 namespace::parse_namespace_part,
18 NamespacePart,
19};
20
21#[derive(Debug, PartialEq)]
22pub struct Document {
23 pub includes: Vec<Include>,
24 pub ns: Namespace,
25}
26
27pub enum DocumentPart {
28 Include(Include),
29 NamespacePart(NamespacePart),
30}
31
32pub fn parse_document(input: &str) -> Result<Document, ParseError> {
33 let span = Span::new(input);
34 let result = parse_document_content(span);
35 match result {
36 Ok((span, parts)) if span.fragment() == &"" => {
37 let mut includes: Vec<Include> = Vec::new();
38 let mut ns_parts: Vec<NamespacePart> = Vec::new();
39 for part in parts {
40 match part {
41 DocumentPart::Include(part) => includes.push(part),
42 DocumentPart::NamespacePart(part) => ns_parts.push(part),
43 }
44 }
45 Ok(Document {
46 includes,
47 ns: Namespace {
48 name: String::default(),
49 position: FilePosition { line: 1, column: 1 },
50 parts: ns_parts,
51 },
52 })
53 }
54 Ok((garbage, _)) => Err(ParseError::TrailingGarbage(garbage)),
55 Err(error) => Err(ParseError::Nom(error)),
56 }
57}
58
59fn parse_document_part(input: Span) -> IResult<Span, DocumentPart> {
60 alt((
61 map(parse_include, DocumentPart::Include),
62 map(parse_namespace_part, DocumentPart::NamespacePart),
63 ))(input)
64}
65
66pub fn parse_document_content(input: Span) -> IResult<Span, Vec<DocumentPart>> {
67 preceded(
68 ws,
69 terminated(separated_list0(ws1, parse_document_part), ws),
70 )(input)
71}
72
73#[test]
74fn test_parse_document() {
75 use crate::idl::field_option::FieldOption;
76 use crate::idl::method::Method;
77 use crate::idl::namespace::{Namespace, NamespacePart};
78 use crate::idl::r#struct::{Field, Struct};
79 use crate::idl::r#type::{Type, TypeRef};
80 use crate::idl::service::Service;
81 use crate::idl::value::Value;
82 let content = "
83 include common.ww;
84 struct Person {
85 name: String (length=1..50),
86 age: Integer,
87 }
88 struct Group {
89 name: String,
90 }
91 service Pinger {
92 ping: None -> None,
93 get_version: None -> String,
94 }
95 ";
96 assert_eq!(
97 parse_document(content),
98 Ok(Document {
99 includes: vec![Include {
100 filename: "common.ww".to_string(),
101 position: FilePosition {
102 line: 2,
103 column: 17
104 },
105 }],
106 ns: Namespace {
107 name: "".to_string(),
108 position: FilePosition { line: 1, column: 1 },
109 parts: vec![
110 NamespacePart::Struct(Struct {
111 name: "Person".to_string(),
112 position: FilePosition { line: 3, column: 9 },
113 generics: vec![],
114 fields: vec![
115 Field {
116 name: "name".to_string(),
117 position: FilePosition {
118 line: 4,
119 column: 13
120 },
121 type_: Type::Ref(TypeRef {
122 abs: false,
123 ns: vec![],
124 name: "String".to_string(),
125 generics: vec![]
126 }),
127 optional: false,
128 options: vec![FieldOption {
129 name: "length".to_string(),
130 value: Value::Range(Some(1), Some(50))
131 }],
132 },
133 Field {
134 name: "age".to_string(),
135 position: FilePosition {
136 line: 5,
137 column: 13
138 },
139 type_: Type::Ref(TypeRef {
140 abs: false,
141 name: "Integer".to_string(),
142 ns: vec![],
143 generics: vec![],
144 }),
145 optional: false,
146 options: vec![],
147 },
148 ],
149 }),
150 NamespacePart::Struct(Struct {
151 name: "Group".to_string(),
152 position: FilePosition { line: 7, column: 9 },
153 generics: vec![],
154 fields: vec![Field {
155 name: "name".to_string(),
156 position: FilePosition {
157 line: 8,
158 column: 13
159 },
160 type_: Type::Ref(TypeRef {
161 abs: false,
162 name: "String".to_string(),
163 ns: vec![],
164 generics: vec![]
165 }),
166 optional: false,
167 options: vec![],
168 }],
169 }),
170 NamespacePart::Service(Service {
171 name: "Pinger".to_string(),
172 position: FilePosition {
173 line: 10,
174 column: 9
175 },
176 methods: vec![
177 Method {
178 name: "ping".to_string(),
179 input: None,
180 output: None,
181 },
182 Method {
183 name: "get_version".to_string(),
184 input: None,
185 output: Some(Type::Ref(TypeRef {
186 abs: false,
187 ns: vec![],
188 name: "String".to_string(),
189 generics: vec![]
190 })),
191 },
192 ],
193 }),
194 ],
195 },
196 })
197 )
198}