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}