1use nom::{
2 bytes::complete::tag,
3 character::complete::char,
4 combinator::{cut, map, opt},
5 error::context,
6 multi::separated_list0,
7 sequence::{pair, preceded, terminated, tuple},
8 IResult,
9};
10
11use crate::common::FilePosition;
12use crate::idl::common::{
13 parse_field_separator, parse_identifier, parse_identifier_with_generics, trailing_comma, ws,
14 ws1, Span,
15};
16use crate::idl::r#type::{parse_type, Type};
17
18#[cfg(test)]
19use crate::idl::common::assert_parse;
20
21use super::{r#type::parse_type_ref, TypeRef};
22
23#[derive(Debug, PartialEq)]
24pub struct Enum {
25 pub name: String,
26 pub generics: Vec<String>,
27 pub extends: Option<TypeRef>,
28 pub variants: Vec<EnumVariant>,
29 pub position: FilePosition,
30}
31
32#[derive(Debug, PartialEq)]
33pub struct EnumVariant {
34 pub name: String,
35 pub value_type: Option<Type>,
36}
37
38pub fn parse_enum(input: Span) -> IResult<Span, Enum> {
39 map(
40 tuple((
41 preceded(terminated(tag("enum"), ws1), parse_identifier_with_generics),
42 parse_enum_extends,
43 parse_enum_variants,
44 )),
45 |((name, generics), extends, variants)| Enum {
46 name,
47 generics,
48 extends,
49 variants,
50 position: input.into(),
51 },
52 )(input)
53}
54
55fn parse_enum_extends(input: Span) -> IResult<Span, Option<TypeRef>> {
56 context(
57 "enum_extends",
58 opt(preceded(
59 terminated(preceded(ws1, tag("extends")), ws1),
60 parse_type_ref,
61 )),
62 )(input)
63}
64
65fn parse_enum_variants(input: Span) -> IResult<Span, Vec<EnumVariant>> {
66 context(
67 "enum_variants",
68 preceded(
69 preceded(ws, char('{')),
70 cut(terminated(
71 separated_list0(parse_field_separator, preceded(ws, parse_enum_variant)),
72 preceded(trailing_comma, preceded(ws, char('}'))),
73 )),
74 ),
75 )(input)
76}
77
78fn parse_enum_variant(input: Span) -> IResult<Span, EnumVariant> {
79 context(
80 "enum_variant",
81 map(
82 pair(
83 parse_identifier,
84 opt(preceded(
85 preceded(ws, char('(')),
86 cut(terminated(
87 parse_type,
88 preceded(trailing_comma, preceded(ws, char(')'))),
89 )),
90 )),
91 ),
92 |(name, value_type)| EnumVariant { name, value_type },
93 ),
94 )(input)
95}
96
97#[test]
98fn test_parse_enum_0() {
99 let contents = [
100 "enum Nothing{}",
102 "enum Nothing {}",
104 "enum Nothing { }",
106 ];
107 for content in contents.iter() {
108 assert_parse(
109 parse_enum(Span::new(content)),
110 Enum {
111 name: "Nothing".to_string(),
112 generics: vec![],
113 position: FilePosition { line: 1, column: 1 },
114 extends: None,
115 variants: vec![],
116 },
117 )
118 }
119}
120
121#[test]
122fn test_parse_enum_1() {
123 let contents = [
124 "enum OneThing{Thing}",
126 "enum OneThing {Thing}",
128 "enum OneThing{ Thing}",
129 "enum OneThing{Thing }",
130 "enum OneThing { Thing }",
131 ];
132 for content in contents.iter() {
133 assert_parse(
134 parse_enum(Span::new(content)),
135 Enum {
136 name: "OneThing".to_string(),
137 generics: vec![],
138 position: FilePosition { line: 1, column: 1 },
139 extends: None,
140 variants: vec![EnumVariant {
141 name: "Thing".to_string(),
142 value_type: None,
143 }],
144 },
145 )
146 }
147}
148
149#[test]
150fn test_parse_enum_2() {
151 let contents = [
152 "enum Direction{Left,Right}",
154 "enum Direction { Left, Right }",
156 "enum Direction {Left,Right}",
158 "enum Direction{ Left,Right}",
159 "enum Direction{Left ,Right}",
160 "enum Direction{Left, Right}",
161 "enum Direction{Left,Right }",
162 ];
163 for content in contents.iter() {
164 assert_parse(
165 parse_enum(Span::new(content)),
166 Enum {
167 name: "Direction".to_string(),
168 generics: vec![],
169 position: FilePosition { line: 1, column: 1 },
170 extends: None,
171 variants: vec![
172 EnumVariant {
173 name: "Left".to_string(),
174 value_type: None,
175 },
176 EnumVariant {
177 name: "Right".to_string(),
178 value_type: None,
179 },
180 ],
181 },
182 )
183 }
184}
185
186#[test]
187fn test_parse_enum_with_value() {
188 use crate::idl::r#type::TypeRef;
189 let contents = [
190 "enum Value{S(String),I(Integer)}",
192 "enum Value { S(String), I(Integer) }",
194 "enum Value {S(String),I(Integer)}",
196 "enum Value{ S(String),I(Integer)}",
197 "enum Value{S (String),I(Integer)}",
198 "enum Value{S( String),I(Integer)}",
199 "enum Value{S(String ),I(Integer)}",
200 "enum Value{S(String) ,I(Integer)}",
201 "enum Value{S(String), I(Integer)}",
202 "enum Value{S(String),I (Integer)}",
203 "enum Value{S(String),I( Integer)}",
204 "enum Value{S(String),I(Integer )}",
205 "enum Value{S(String),I(Integer) }",
206 ];
207 for content in contents.iter() {
208 assert_parse(
209 parse_enum(Span::new(content)),
210 Enum {
211 name: "Value".to_string(),
212 generics: vec![],
213 position: FilePosition { line: 1, column: 1 },
214 extends: None,
215 variants: vec![
216 EnumVariant {
217 name: "S".to_string(),
218 value_type: Some(Type::Ref(TypeRef {
219 abs: false,
220 ns: vec![],
221 name: "String".to_string(),
222 generics: vec![],
223 })),
224 },
225 EnumVariant {
226 name: "I".to_string(),
227 value_type: Some(Type::Ref(TypeRef {
228 abs: false,
229 ns: vec![],
230 name: "Integer".to_string(),
231 generics: vec![],
232 })),
233 },
234 ],
235 },
236 )
237 }
238}
239
240#[test]
241fn test_parse_enum_extends() {
242 use crate::idl::r#type::TypeRef;
243 let contents = [
244 "enum GetError extends GenericError{}",
246 "enum GetError extends GenericError {}",
248 "enum GetError extends GenericError{ }",
250 ];
251 for content in contents.iter() {
252 assert_parse(
253 parse_enum(Span::new(content)),
254 Enum {
255 name: "GetError".to_string(),
256 generics: vec![],
257 position: FilePosition { line: 1, column: 1 },
258 extends: Some(TypeRef {
259 abs: false,
260 ns: vec![],
261 name: "GenericError".to_string(),
262 generics: vec![],
263 }),
264 variants: vec![],
265 },
266 )
267 }
268}