webwire_cli/idl/
document.rs

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}