webwire_cli/idl/
type.rs

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}