1use nom::{
2 bytes::complete::tag,
3 character::complete::char,
4 combinator::{cut, map, opt},
5 error::context,
6 multi::separated_list0,
7 sequence::{pair, preceded, separated_pair, terminated},
8 IResult,
9};
10
11use crate::common::FilePosition;
12use crate::idl::common::{
13 parse_field_separator, parse_identifier, parse_identifier_with_generics, trailing_comma, ws,
14 ws1, Span,
15};
16use crate::idl::field_option::{parse_field_options, FieldOption};
17use crate::idl::r#type::{parse_type, Type};
18
19#[cfg(test)]
20use crate::idl::common::assert_parse;
21#[cfg(test)]
22use nom::Slice;
23
24#[derive(Debug, PartialEq)]
25pub struct Struct {
26 pub name: String,
27 pub generics: Vec<String>,
28 pub fields: Vec<Field>,
29 pub position: FilePosition,
30}
31
32#[derive(Debug, PartialEq)]
33pub struct Field {
34 pub name: String,
35 pub type_: Type,
36 pub optional: bool,
37 pub options: Vec<FieldOption>,
38 pub position: FilePosition,
39}
40
41pub fn parse_struct(input: Span) -> IResult<Span, Struct> {
42 map(
43 pair(
44 preceded(tag("struct"), preceded(ws1, parse_identifier_with_generics)),
45 parse_fields,
46 ),
47 |((name, generics), fields)| Struct {
48 name,
49 generics,
50 fields,
51 position: input.into(),
52 },
53 )(input)
54}
55
56fn parse_fields(input: Span) -> IResult<Span, Vec<Field>> {
57 context(
58 "fields",
59 preceded(
60 preceded(ws, char('{')),
61 cut(terminated(
62 separated_list0(parse_field_separator, preceded(ws, parse_field)),
63 preceded(trailing_comma, preceded(ws, char('}'))),
64 )),
65 ),
66 )(input)
67}
68
69fn parse_field(input: Span) -> IResult<Span, Field> {
70 map(
71 separated_pair(
72 pair(parse_identifier, opt(preceded(ws, char('?')))),
73 preceded(ws, char(':')),
74 pair(parse_type, opt(parse_field_options)),
75 ),
76 |((name, optional), (type_, options))| Field {
77 name,
78 position: input.into(),
79 optional: optional != None,
80 type_,
81 options: if let Some(options) = options {
82 options
83 } else {
84 vec![]
85 },
86 },
87 )(input)
88}
89
90#[test]
91fn test_parse_field() {
92 use crate::idl::r#type::TypeRef;
93 let contents = ["foo:FooType", "foo: FooType", "foo : FooType"];
94 for content in contents.iter() {
95 assert_parse(
96 parse_field(Span::new(content)),
97 Field {
98 name: "foo".to_string(),
99 position: FilePosition { line: 1, column: 1 },
100 type_: Type::Ref(TypeRef {
101 abs: false,
102 ns: vec![],
103 name: "FooType".to_string(),
104 generics: vec![],
105 }),
106 optional: false,
107 options: vec![],
108 },
109 );
110 }
111}
112
113#[test]
114fn test_parse_field_optional() {
115 use crate::idl::r#type::TypeRef;
116 let contents = [
117 "foo?:FooType",
118 "foo? :FooType",
119 "foo ?:FooType",
120 "foo ? :FooType",
121 ];
122 for content in contents.iter() {
123 assert_parse(
124 parse_field(Span::new(content)),
125 Field {
126 name: "foo".to_string(),
127 position: FilePosition { line: 1, column: 1 },
128 type_: Type::Ref(TypeRef {
129 abs: false,
130 ns: vec![],
131 name: "FooType".to_string(),
132 generics: vec![],
133 }),
134 optional: true,
135 options: vec![],
136 },
137 );
138 }
139}
140
141#[test]
142fn test_parse_field_with_options() {
143 use crate::idl::r#type::TypeRef;
144 use crate::idl::value::Value;
145 let contents = [
146 "name:String(length=2..50)",
147 "name :String(length=2..50)",
148 "name: String(length=2..50)",
149 "name:String (length=2..50)",
150 "name:String( length=2..50)",
151 "name:String(length =2..50)",
152 "name:String(length= 2..50)",
153 "name:String(length=2..50 )",
158 ];
159 for content in contents.iter() {
160 assert_parse(
161 parse_field(Span::new(content)),
162 Field {
163 name: "name".to_string(),
164 position: FilePosition { line: 1, column: 1 },
165 type_: Type::Ref(TypeRef {
166 abs: false,
167 ns: vec![],
168 name: "String".to_string(),
169 generics: vec![],
170 }),
171 optional: false,
172 options: vec![FieldOption {
173 name: "length".to_string(),
174 value: Value::Range(Some(2), Some(50)),
175 }],
176 },
177 );
178 }
179}
180
181#[test]
182fn test_parse_array_field_with_options() {
183 use crate::idl::r#type::TypeRef;
184 use crate::idl::value::Value;
185 let contents = [
186 "items:[String](length=0..32)",
187 "items :[String](length=0..32)",
188 "items: [String](length=0..32)",
189 "items:[String] (length=0..32)",
190 "items:[String]( length=0..32)",
191 "items:[String](length =0..32)",
192 "items:[String](length= 0..32)",
193 "items:[String](length=0..32 )",
194 ];
195 for content in contents.iter() {
196 assert_parse(
197 parse_field(Span::new(content)),
198 Field {
199 name: "items".to_string(),
200 position: FilePosition { line: 1, column: 1 },
201 type_: Type::Array(Box::new(Type::Ref(TypeRef {
202 abs: false,
203 ns: vec![],
204 name: "String".to_string(),
205 generics: vec![],
206 }))),
207 optional: false,
208 options: vec![FieldOption {
209 name: "length".to_string(),
210 value: Value::Range(Some(0), Some(32)),
211 }],
212 },
213 );
214 }
215}
216
217#[test]
218fn test_parse_fields_0() {
219 let contents = ["{}", "{ }", "{,}", "{ ,}", "{, }"];
220 for content in contents.iter() {
221 assert_parse(parse_fields(Span::new(content)), vec![])
222 }
223}
224
225#[test]
226fn test_parse_fields_1() {
227 use crate::idl::r#type::TypeRef;
228 let content = "{foo: Foo}";
229 assert_parse(
230 parse_fields(Span::new(content)),
231 vec![Field {
232 name: "foo".to_owned(),
233 position: FilePosition { line: 1, column: 2 },
234 type_: Type::Ref(TypeRef {
235 abs: false,
236 ns: vec![],
237 name: "Foo".to_owned(),
238 generics: vec![],
239 }),
240 optional: false,
241 options: vec![],
242 }],
243 );
244}
245
246#[test]
247fn test_parse_fields_1_ws_variants() {
248 let contents = ["{foo: Foo}", "{foo:Foo }", "{ foo:Foo}", "{foo:Foo,}"];
249 for content in contents.iter() {
250 let (_, f) = parse_fields(Span::new(content)).unwrap();
251 assert_eq!(f.len(), 1);
252 }
253}
254
255#[test]
256fn test_parse_fields_2() {
257 use crate::idl::r#type::TypeRef;
258 let content = "{ foo: Foo, bar: Bar }";
259 assert_parse(
260 parse_fields(Span::new(content)),
261 vec![
262 Field {
263 name: "foo".to_owned(),
264 position: FilePosition { line: 1, column: 3 },
265 type_: Type::Ref(TypeRef {
266 abs: false,
267 ns: vec![],
268 name: "Foo".to_owned(),
269 generics: vec![],
270 }),
271 optional: false,
272 options: vec![],
273 },
274 Field {
275 name: "bar".to_owned(),
276 position: FilePosition {
277 line: 1,
278 column: 13,
279 },
280 type_: Type::Ref(TypeRef {
281 abs: false,
282 ns: vec![],
283 name: "Bar".to_owned(),
284 generics: vec![],
285 }),
286 optional: false,
287 options: vec![],
288 },
289 ],
290 );
291}
292
293#[test]
294fn test_parse_fields_2_ws_variants() {
295 let contents = [
296 "{foo:Foo,bar:Bar}",
297 "{ foo:Foo,bar:Bar}",
298 "{foo :Foo,bar:Bar}",
299 "{foo: Foo,bar:Bar}",
300 "{foo:Foo ,bar:Bar}",
301 "{foo:Foo, bar:Bar}",
302 "{foo:Foo,bar :Bar}",
303 "{foo:Foo,bar: Bar}",
304 "{foo:Foo,bar:Bar }",
305 "{foo:Foo,bar:Bar,}",
307 ];
308 for content in contents.iter() {
309 let (_, f) = parse_fields(Span::new(content)).unwrap();
310 assert_eq!(f.len(), 2);
311 }
312}
313
314#[test]
315fn test_parse_struct() {
316 let contents = [
317 "struct Pinger{}",
318 "struct Pinger {}",
319 "struct Pinger{ }",
320 "struct Pinger { }",
321 ];
322 for content in contents.iter() {
323 assert_parse(
324 parse_struct(Span::new(content)),
325 Struct {
326 name: "Pinger".to_string(),
327 position: FilePosition { line: 1, column: 1 },
328 generics: vec![],
329 fields: vec![],
330 },
331 );
332 }
333}
334
335#[test]
336fn test_parse_struct_field_options() {
337 use crate::idl::r#type::TypeRef;
338 use crate::idl::value::Value;
339 let contents = ["struct Person { name: [String] (length=1..50) }"];
340 for content in contents.iter() {
341 assert_parse(
342 parse_struct(Span::new(content)),
343 Struct {
344 name: "Person".to_string(),
345 position: FilePosition { line: 1, column: 1 },
346 generics: vec![],
347 fields: vec![Field {
348 name: "name".to_string(),
349 position: FilePosition {
350 line: 1,
351 column: 17,
352 },
353 type_: Type::Array(Box::new(Type::Ref(TypeRef {
354 abs: false,
355 ns: vec![],
356 name: "String".to_string(),
357 generics: vec![],
358 }))),
359 optional: false,
360 options: vec![FieldOption {
361 name: "length".to_string(),
362 value: Value::Range(Some(1), Some(50)),
363 }],
364 }],
365 },
366 );
367 }
368}
369
370#[test]
371fn test_parse_struct_invalid() {
372 use nom::error::ErrorKind;
373 let input = Span::new("struct 123fail{}");
374 assert_eq!(
375 parse_struct(input),
376 Err(nom::Err::Error(nom::error::Error {
378 input: input.slice(7..),
379 code: ErrorKind::TakeWhile1
380 }))
381 )
382}
383
384#[test]
385fn test_parse_struct_with_fields() {
386 use crate::idl::r#type::TypeRef;
387 let contents = ["struct Person { name: String, age: Integer }"];
388 for content in contents.iter() {
389 assert_parse(
390 parse_struct(Span::new(content)),
391 Struct {
392 name: "Person".to_string(),
393 position: FilePosition { line: 1, column: 1 },
394 generics: vec![],
395 fields: vec![
396 Field {
397 name: "name".to_string(),
398 position: FilePosition {
399 line: 1,
400 column: 17,
401 },
402 type_: Type::Ref(TypeRef {
403 abs: false,
404 ns: vec![],
405 name: "String".to_string(),
406 generics: vec![],
407 }),
408 optional: false,
409 options: vec![],
410 },
411 Field {
412 name: "age".to_string(),
413 position: FilePosition {
414 line: 1,
415 column: 31,
416 },
417 type_: Type::Ref(TypeRef {
418 abs: false,
419 ns: vec![],
420 name: "Integer".to_string(),
421 generics: vec![],
422 }),
423 optional: false,
424 options: vec![],
425 },
426 ],
427 },
428 )
429 }
430}
431
432#[test]
433fn test_parse_struct_with_fields_ws_variants() {
434 let contents = vec![
435 "struct Person{name:String,age:Integer}",
436 "struct Person {name:String,age:Integer}",
437 "struct Person{ name:String,age:Integer}",
438 "struct Person{name :String,age:Integer}",
439 "struct Person{name: String,age:Integer}",
440 "struct Person{name:String ,age:Integer}",
441 "struct Person{name:String, age:Integer}",
442 "struct Person{name:String,age :Integer}",
443 "struct Person{name:String,age: Integer}",
444 "struct Person{name:String,age:Integer }",
445 ];
446 for content in contents.iter() {
447 let (_, s) = parse_struct(Span::new(content)).unwrap();
448 assert_eq!(s.name, "Person");
449 assert_eq!(s.fields.len(), 2);
450 }
451}
452
453#[test]
454fn test_parse_struct_with_generics() {
455 use crate::idl::r#type::TypeRef;
456 let content = "struct Wrapper<T> { value:T }";
457 assert_parse(
458 parse_struct(Span::new(content)),
459 Struct {
460 name: "Wrapper".to_string(),
461 position: FilePosition { line: 1, column: 1 },
462 generics: vec!["T".to_string()],
463 fields: vec![Field {
464 name: "value".to_string(),
465 position: FilePosition {
466 line: 1,
467 column: 21,
468 },
469 type_: Type::Ref(TypeRef {
470 abs: false,
471 ns: vec![],
472 name: "T".to_string(),
473 generics: vec![],
474 }),
475 optional: false,
476 options: vec![],
477 }],
478 },
479 );
480}
481
482#[test]
483fn test_parse_struct_with_generics_ws_variants() {
484 let contents = vec![
485 "struct Wrapper<T>{value:T}",
486 "struct Wrapper <T>{value:T}",
487 "struct Wrapper< T>{value:T}",
488 "struct Wrapper<T >{value:T}",
489 "struct Wrapper<T> {value:T}",
490 "struct Wrapper<T>{ value:T}",
491 "struct Wrapper<T>{value :T}",
492 "struct Wrapper<T>{value: T}",
493 "struct Wrapper<T>{value:T }",
494 "struct Wrapper<T,>{value:T}",
495 "struct Wrapper<T,>{value:T,}",
496 ];
497 for content in contents.iter() {
498 let (_, s) = parse_struct(Span::new(content)).unwrap();
499 assert_eq!(s.name, "Wrapper");
500 assert_eq!(s.fields.len(), 1);
501 }
502}