1use nom::{
2 bytes::complete::tag,
3 character::complete::char,
4 combinator::{cut, map, opt},
5 multi::separated_list0,
6 sequence::{pair, preceded, separated_pair, terminated},
7 IResult,
8};
9
10use crate::common::FilePosition;
11use crate::idl::common::{
12 parse_field_separator, parse_identifier, parse_identifier_with_generics, trailing_comma, ws,
13 ws1, Span,
14};
15use crate::idl::r#type::{parse_type_ref, TypeRef};
16
17#[cfg(test)]
18use crate::idl::common::assert_parse;
19
20#[derive(Clone, Debug, PartialEq)]
21pub struct Field {
22 pub name: String,
23 pub optional: bool,
24}
25
26#[derive(Debug, PartialEq)]
27pub struct Fieldset {
28 pub name: String,
29 pub generics: Vec<String>,
30 pub r#struct: TypeRef,
31 pub fields: Vec<Field>,
32 pub position: FilePosition,
33}
34
35fn parse_field(input: Span) -> IResult<Span, Field> {
36 map(
37 pair(preceded(ws, parse_identifier), preceded(ws, opt(char('?')))),
38 |(name, optional)| Field {
39 name,
40 optional: optional != None,
41 },
42 )(input)
43}
44
45fn parse_fields(input: Span) -> IResult<Span, Vec<Field>> {
46 preceded(
47 preceded(ws, char('{')),
48 cut(terminated(
49 separated_list0(parse_field_separator, parse_field),
50 preceded(trailing_comma, preceded(ws, char('}'))),
51 )),
52 )(input)
53}
54
55pub fn parse_fieldset(input: Span) -> IResult<Span, Fieldset> {
56 map(
57 preceded(
58 terminated(tag("fieldset"), ws1),
59 cut(pair(
60 separated_pair(
61 preceded(ws, parse_identifier_with_generics),
62 preceded(ws, tag("for")),
63 preceded(ws1, parse_type_ref),
64 ),
65 parse_fields,
66 )),
67 ),
68 |(((name, generics), r#struct), fields)| Fieldset {
69 name,
70 generics,
71 r#struct,
72 fields,
73 position: input.into(),
74 },
75 )(input)
76}
77
78#[test]
79fn test_parse_fieldset_0() {
80 let contents = [
81 "fieldset PersonName for Person{}",
83 "fieldset PersonName for Person {}",
85 "fieldset PersonName for Person { }",
87 ];
88 for content in contents.iter() {
89 assert_parse(
90 parse_fieldset(Span::new(content)),
91 Fieldset {
92 name: "PersonName".to_string(),
93 generics: vec![],
94 position: FilePosition { line: 1, column: 1 },
95 r#struct: TypeRef {
96 abs: false,
97 ns: vec![],
98 name: "Person".to_string(),
99 generics: vec![],
100 },
101 fields: vec![],
102 },
103 )
104 }
105}
106
107#[test]
108fn test_parse_fieldset_1() {
109 let contents = [
110 "fieldset PersonName for Person{name}",
112 "fieldset PersonName for Person {name}",
114 "fieldset PersonName for Person{ name}",
115 "fieldset PersonName for Person{name }",
116 ];
117 for content in contents.iter() {
118 assert_parse(
119 parse_fieldset(Span::new(content)),
120 Fieldset {
121 name: "PersonName".to_string(),
122 generics: vec![],
123 position: FilePosition { line: 1, column: 1 },
124 r#struct: TypeRef {
125 abs: false,
126 ns: vec![],
127 name: "Person".to_string(),
128 generics: vec![],
129 },
130 fields: vec![Field {
131 name: "name".to_string(),
132 optional: false,
133 }],
134 },
135 )
136 }
137}
138
139#[test]
140fn test_parse_fieldset_2() {
141 let contents = [
142 "fieldset PersonName for Person{name,age?}",
144 "fieldset PersonName for Person { name, age? }",
146 "fieldset PersonName for Person {name,age?}",
148 "fieldset PersonName for Person{ name,age?}",
149 "fieldset PersonName for Person{name ,age?}",
150 "fieldset PersonName for Person{name, age?}",
151 "fieldset PersonName for Person{name,age ?}",
152 "fieldset PersonName for Person{name,age? }",
153 ];
154 for content in contents.iter() {
155 assert_parse(
156 parse_fieldset(Span::new(content)),
157 Fieldset {
158 name: "PersonName".to_string(),
159 generics: vec![],
160 position: FilePosition { line: 1, column: 1 },
161 r#struct: TypeRef {
162 abs: false,
163 ns: vec![],
164 name: "Person".to_string(),
165 generics: vec![],
166 },
167 fields: vec![
168 Field {
169 name: "name".to_string(),
170 optional: false,
171 },
172 Field {
173 name: "age".to_string(),
174 optional: true,
175 },
176 ],
177 },
178 )
179 }
180}