webwire_cli/idl/
namespace.rs

1use nom::{
2    branch::alt,
3    bytes::complete::tag,
4    character::complete::char,
5    combinator::{cut, map},
6    multi::separated_list0,
7    sequence::{pair, preceded, terminated},
8    IResult,
9};
10
11use crate::common::FilePosition;
12use crate::idl::common::{parse_identifier, ws, ws1, Span};
13use crate::idl::fieldset::{parse_fieldset, Fieldset};
14use crate::idl::r#enum::{parse_enum, Enum};
15use crate::idl::r#struct::{parse_struct, Struct};
16use crate::idl::service::{parse_service, Service};
17
18#[cfg(test)]
19use crate::idl::common::assert_parse;
20
21#[derive(Debug, PartialEq)]
22pub enum NamespacePart {
23    Enum(Enum),
24    Struct(Struct),
25    Fieldset(Fieldset),
26    Service(Service),
27    Namespace(Namespace),
28}
29
30impl NamespacePart {
31    pub fn name(&self) -> &str {
32        match self {
33            Self::Enum(part) => &part.name,
34            Self::Struct(part) => &part.name,
35            Self::Fieldset(part) => &part.name,
36            Self::Service(part) => &part.name,
37            Self::Namespace(part) => &part.name,
38        }
39    }
40    pub fn position(&self) -> &FilePosition {
41        match self {
42            Self::Enum(part) => &part.position,
43            Self::Struct(part) => &part.position,
44            Self::Fieldset(part) => &part.position,
45            Self::Service(part) => &part.position,
46            Self::Namespace(part) => &part.position,
47        }
48    }
49}
50
51#[derive(Debug, PartialEq)]
52pub struct Namespace {
53    pub name: String,
54    pub parts: Vec<NamespacePart>,
55    pub position: FilePosition,
56}
57
58pub fn parse_namespace_part(input: Span) -> IResult<Span, NamespacePart> {
59    alt((
60        map(parse_enum, NamespacePart::Enum),
61        map(parse_fieldset, NamespacePart::Fieldset),
62        map(parse_struct, NamespacePart::Struct),
63        map(parse_service, NamespacePart::Service),
64        map(parse_namespace, NamespacePart::Namespace),
65    ))(input)
66}
67
68pub fn parse_namespace_content(input: Span) -> IResult<Span, Vec<NamespacePart>> {
69    preceded(
70        ws,
71        terminated(separated_list0(ws1, parse_namespace_part), ws),
72    )(input)
73}
74
75pub fn parse_namespace(input: Span) -> IResult<Span, Namespace> {
76    map(
77        preceded(
78            ws,
79            preceded(
80                terminated(tag("namespace"), ws1),
81                cut(pair(
82                    parse_identifier,
83                    preceded(
84                        preceded(ws, char('{')),
85                        terminated(parse_namespace_content, preceded(ws, char('}'))),
86                    ),
87                )),
88            ),
89        ),
90        |(name, parts)| Namespace {
91            name,
92            parts,
93            position: input.into(),
94        },
95    )(input)
96}
97
98#[test]
99fn test_parse_namespace() {
100    use crate::idl::field_option::FieldOption;
101    use crate::idl::method::Method;
102    use crate::idl::r#struct::Field;
103    use crate::idl::r#type::{Type, TypeRef};
104    use crate::idl::value::Value;
105    let content = "
106        namespace test {
107            struct Person {
108                name: String (length=1..50),
109                age: Integer
110            }
111            struct Group {
112                name: String
113            }
114            service Pinger {
115                ping: None -> None,
116                get_version: None -> String
117            }
118        }";
119    assert_parse(
120        parse_namespace(Span::new(content)),
121        Namespace {
122            name: "test".to_string(),
123            position: FilePosition { line: 1, column: 1 },
124            parts: vec![
125                NamespacePart::Struct(Struct {
126                    name: "Person".to_string(),
127                    position: FilePosition {
128                        line: 3,
129                        column: 13,
130                    },
131                    generics: vec![],
132                    fields: vec![
133                        Field {
134                            name: "name".to_string(),
135                            position: FilePosition {
136                                line: 4,
137                                column: 17,
138                            },
139                            type_: Type::Ref(TypeRef {
140                                abs: false,
141                                ns: vec![],
142                                name: "String".to_string(),
143                                generics: vec![],
144                            }),
145                            optional: false,
146                            options: vec![FieldOption {
147                                name: "length".to_string(),
148                                value: Value::Range(Some(1), Some(50)),
149                            }],
150                        },
151                        Field {
152                            name: "age".to_string(),
153                            position: FilePosition {
154                                line: 5,
155                                column: 17,
156                            },
157                            type_: Type::Ref(TypeRef {
158                                abs: false,
159                                ns: vec![],
160                                name: "Integer".to_string(),
161                                generics: vec![],
162                            }),
163                            optional: false,
164                            options: vec![],
165                        },
166                    ],
167                }),
168                NamespacePart::Struct(Struct {
169                    name: "Group".to_string(),
170                    position: FilePosition {
171                        line: 7,
172                        column: 13,
173                    },
174                    generics: vec![],
175                    fields: vec![Field {
176                        name: "name".to_string(),
177                        position: FilePosition {
178                            line: 8,
179                            column: 17,
180                        },
181                        type_: Type::Ref(TypeRef {
182                            abs: false,
183                            ns: vec![],
184                            name: "String".to_string(),
185                            generics: vec![],
186                        }),
187                        optional: false,
188                        options: vec![],
189                    }],
190                }),
191                NamespacePart::Service(Service {
192                    name: "Pinger".to_string(),
193                    position: FilePosition {
194                        line: 10,
195                        column: 13,
196                    },
197                    methods: vec![
198                        Method {
199                            name: "ping".to_string(),
200                            input: None,
201                            output: None,
202                        },
203                        Method {
204                            name: "get_version".to_string(),
205                            input: None,
206                            output: Some(Type::Ref(TypeRef {
207                                abs: false,
208                                ns: vec![],
209                                name: "String".to_string(),
210                                generics: vec![],
211                            })),
212                        },
213                    ],
214                }),
215            ],
216        },
217    )
218}