1#[cfg(test)]
2use crate::idl::common::assert_parse;
3use crate::idl::common::{parse_field_separator, parse_identifier, trailing_comma, ws, Span};
4use nom::{
5 branch::alt,
6 bytes::complete::tag,
7 character::complete::char,
8 combinator::{cut, map, opt},
9 error::context,
10 multi::{many0, separated_list0},
11 sequence::{preceded, separated_pair, terminated, tuple},
12 IResult,
13};
14
15#[derive(Debug, PartialEq)]
16pub enum Type {
17 Ref(TypeRef),
18 Array(Box<Type>),
19 Map(Box<Type>, Box<Type>),
20}
21
22#[derive(Debug, PartialEq)]
23pub struct TypeRef {
24 pub abs: bool,
25 pub ns: Vec<String>,
26 pub name: String,
27 pub generics: Vec<Type>,
28}
29
30pub fn parse_none(input: Span) -> IResult<Span, Option<Type>> {
31 map(tag("None"), |_| None)(input)
32}
33
34pub fn parse_type_ref(input: Span) -> IResult<Span, TypeRef> {
35 map(
36 tuple((
37 map(opt(tag("::")), |r| r.is_some()),
38 parse_identifier,
39 many0(preceded(tag("::"), parse_identifier)),
40 parse_generics,
41 )),
42 |(abs, path_first, mut path, generics)| {
43 let (ns, name): (Vec<String>, String) = match path.pop() {
44 Some(name) => {
45 path.insert(0, path_first);
46 (path, name)
47 }
48 None => (path, path_first),
49 };
50 TypeRef {
51 abs,
52 ns,
53 name,
54 generics,
55 }
56 },
57 )(input)
58}
59
60fn parse_generics(input: Span) -> IResult<Span, Vec<Type>> {
61 map(
62 opt(preceded(
63 preceded(ws, char('<')),
64 cut(terminated(
65 separated_list0(parse_field_separator, preceded(ws, parse_type)),
66 preceded(trailing_comma, preceded(ws, char('>'))),
67 )),
68 )),
69 |v| match v {
70 Some(v) => v,
71 None => Vec::with_capacity(0),
72 },
73 )(input)
74}
75
76fn parse_type_array(input: Span) -> IResult<Span, Type> {
77 context(
78 "array",
79 preceded(
80 char('['),
81 cut(terminated(
82 preceded(ws, map(parse_type, |t| Type::Array(Box::new(t)))),
83 preceded(ws, char(']')),
84 )),
85 ),
86 )(input)
87}
88
89fn parse_type_map_inner(input: Span) -> IResult<Span, Type> {
90 map(
91 separated_pair(
92 preceded(ws, parse_type),
93 cut(preceded(ws, char(':'))),
94 preceded(ws, parse_type),
95 ),
96 |t| Type::Map(Box::new(t.0), Box::new(t.1)),
97 )(input)
98}
99
100fn parse_type_map(input: Span) -> IResult<Span, Type> {
101 context(
102 "map",
103 preceded(
104 char('{'),
105 cut(terminated(
106 preceded(ws, parse_type_map_inner),
107 preceded(ws, char('}')),
108 )),
109 ),
110 )(input)
111}
112
113pub fn parse_opt_type(input: Span) -> IResult<Span, Option<Type>> {
114 preceded(ws, alt((parse_none, map(parse_type, Some))))(input)
115}
116
117pub fn parse_type(input: Span) -> IResult<Span, Type> {
118 preceded(
119 ws,
120 alt((
121 map(parse_type_ref, Type::Ref),
122 parse_type_array,
123 parse_type_map,
124 )),
125 )(input)
126}
127
128#[test]
129fn test_parse_none() {
130 assert_parse(parse_opt_type(Span::new("None")), None);
131}
132
133#[test]
134fn test_parse_type_ref_rel_without_ns() {
135 assert_parse(
136 parse_type(Span::new("T")),
137 Type::Ref(TypeRef {
138 abs: false,
139 ns: vec![],
140 name: "T".to_string(),
141 generics: vec![],
142 }),
143 );
144}
145
146#[test]
147fn test_parse_type_ref_abs_without_ns() {
148 assert_parse(
149 parse_type(Span::new("::T")),
150 Type::Ref(TypeRef {
151 abs: true,
152 ns: vec![],
153 name: "T".to_string(),
154 generics: vec![],
155 }),
156 );
157}
158
159#[test]
160fn test_parse_type_ref_rel_with_ns() {
161 assert_parse(
162 parse_type(Span::new("ns::T")),
163 Type::Ref(TypeRef {
164 abs: false,
165 ns: vec!["ns".to_string()],
166 name: "T".to_string(),
167 generics: vec![],
168 }),
169 );
170}
171
172#[test]
173fn test_parse_type_ref_abs_with_ns() {
174 assert_parse(
175 parse_type(Span::new("::ns::T")),
176 Type::Ref(TypeRef {
177 abs: true,
178 ns: vec!["ns".to_string()],
179 name: "T".to_string(),
180 generics: vec![],
181 }),
182 );
183}
184
185#[test]
186fn test_parse_type_ref_rel_with_ns2() {
187 assert_parse(
188 parse_type(Span::new("ns1::ns2::T")),
189 Type::Ref(TypeRef {
190 abs: false,
191 ns: vec!["ns1".to_string(), "ns2".to_string()],
192 name: "T".to_string(),
193 generics: vec![],
194 }),
195 );
196}
197
198#[test]
199fn test_parse_type_ref_abs_with_ns2() {
200 assert_parse(
201 parse_type(Span::new("::ns1::ns2::T")),
202 Type::Ref(TypeRef {
203 abs: true,
204 ns: vec!["ns1".to_string(), "ns2".to_string()],
205 name: "T".to_string(),
206 generics: vec![],
207 }),
208 );
209}
210
211#[test]
212fn test_parse_type_ref_with_generic_ref() {
213 let contents = [
214 "Foo<UUID>",
215 "Foo <UUID>",
216 "Foo< UUID>",
217 "Foo<UUID >",
218 "Foo<UUID,>",
219 ];
220 for content in contents.iter() {
221 assert_parse(
222 parse_type(Span::new(content)),
223 Type::Ref(TypeRef {
224 abs: false,
225 ns: vec![],
226 name: "Foo".to_string(),
227 generics: vec![Type::Ref(TypeRef {
228 abs: false,
229 name: "UUID".to_string(),
230 ns: vec![],
231 generics: vec![],
232 })],
233 }),
234 );
235 }
236}
237
238#[test]
239fn test_parse_type_ref_with_generic_generic() {
240 let contents = [
241 "Foo<Bar<UUID>>",
242 "Foo <Bar<UUID>>",
243 "Foo< Bar<UUID>>",
244 "Foo<Bar <UUID>>",
245 "Foo<Bar< UUID>>",
246 "Foo<Bar<UUID >>",
247 "Foo<Bar<UUID> >",
248 "Foo<Bar<UUID,>,>",
249 ];
250 for content in contents.iter() {
251 assert_parse(
252 parse_type(Span::new(content)),
253 Type::Ref(TypeRef {
254 abs: false,
255 ns: vec![],
256 name: "Foo".to_string(),
257 generics: vec![Type::Ref(TypeRef {
258 abs: false,
259 ns: vec![],
260 name: "Bar".to_string(),
261 generics: vec![Type::Ref(TypeRef {
262 abs: false,
263 ns: vec![],
264 name: "UUID".to_string(),
265 generics: vec![],
266 })],
267 })],
268 }),
269 );
270 }
271}
272
273#[test]
274fn test_parse_type_array() {
275 let contents = ["[UUID]", "[ UUID]", "[UUID ]", "[ UUID ]"];
276 for content in contents.iter() {
277 assert_parse(
278 parse_type(Span::new(content)),
279 Type::Array(Box::new(Type::Ref(TypeRef {
280 abs: false,
281 ns: vec![],
282 name: "UUID".to_string(),
283 generics: vec![],
284 }))),
285 );
286 }
287}
288
289#[test]
290fn test_parse_type_map() {
291 let contents = [
292 "{UUID:String}",
293 "{ UUID:String}",
294 "{UUID:String }",
295 "{UUID :String}",
296 "{UUID: String}",
297 "{ UUID : String }",
298 ];
299 for content in contents.iter() {
300 assert_parse(
301 parse_type(Span::new(content)),
302 Type::Map(
303 Box::new(Type::Ref(TypeRef {
304 abs: false,
305 ns: vec![],
306 name: "UUID".to_string(),
307 generics: vec![],
308 })),
309 Box::new(Type::Ref(TypeRef {
310 abs: false,
311 ns: vec![],
312 name: "String".to_string(),
313 generics: vec![],
314 })),
315 ),
316 );
317 }
318}