1use chumsky::{IterParser, Parser as ChumskyParser};
2
3use crate::{
4 impl_block_properties_parser,
5 parser::{
6 close_block, key_value, key_value_boolean, key_value_numeric, open_block, InternalParser,
7 TokenError, TokenSource,
8 },
9 types::Color,
10 Parser,
11};
12
13#[derive(Default, Debug, Clone)]
15pub struct EditorData<'src> {
16 pub color: Color,
17 pub visgroupshown: bool,
18 pub visgroupautoshown: bool,
19 pub groupid: Option<u32>,
20 pub comments: Option<&'src str>,
21 pub logicalpos: Option<&'src str>,
22}
23
24#[derive(Debug, Clone)]
26enum EditorDataProperty<'src> {
27 Color(Color),
28 VisGroupShown(bool),
29 VisGroupAutoShown(bool),
30 GroupId(u32),
31 Comments(&'src str),
32 LogicalPos(&'src str),
33}
34
35impl<'src> Parser<'src> for EditorData<'src> {}
37
38impl<'src> InternalParser<'src> for EditorData<'src> {
54 fn parser<I>() -> impl ChumskyParser<'src, I, Self, TokenError<'src>>
55 where
56 I: TokenSource<'src>,
57 {
58 impl_block_properties_parser! {
59 property_list: EditorDataProperty = {
60 p_color = Color::parser() => EditorDataProperty::Color,
61 p_visgroupshown = key_value_boolean("visgroupshown") => EditorDataProperty::VisGroupShown,
62 p_visgroupautoshown = key_value_boolean("visgroupautoshown") => EditorDataProperty::VisGroupAutoShown,
63 p_groupid = key_value_numeric("groupid") => EditorDataProperty::GroupId,
64 p_comments = key_value("comments") => |s: &str| EditorDataProperty::Comments(s),
65 p_logicalpos = key_value("logicalpos") => |s: &str| EditorDataProperty::LogicalPos(s),
66 }
67 }
68
69 open_block("editor")
70 .ignore_then(
71 property_list
72 .repeated()
73 .collect::<Vec<EditorDataProperty>>(),
74 )
75 .then_ignore(close_block())
76 .map(|properties: Vec<EditorDataProperty>| {
77 let mut editor = EditorData::default();
78 for prop in properties {
79 match prop {
80 EditorDataProperty::Color(val) => editor.color = val,
81 EditorDataProperty::VisGroupShown(val) => editor.visgroupshown = val,
82 EditorDataProperty::VisGroupAutoShown(val) => {
83 editor.visgroupautoshown = val
84 }
85 EditorDataProperty::GroupId(val) => editor.groupid = Some(val),
86 EditorDataProperty::Comments(val) => editor.comments = Some(val),
87 EditorDataProperty::LogicalPos(val) => editor.logicalpos = Some(val),
88 }
89 }
90 editor
91 })
92 .boxed()
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use crate::util::lex;
100
101 #[test]
102 fn test_editor_complete_valid() {
103 let input = r#"
104 editor
105 {
106 "color" "0 111 152"
107 "visgroupshown" "1"
108 "visgroupautoshown" "1"
109 "logicalpos" "[0 10000]"
110 "comments" "Test comment"
111 }
112 "#;
113
114 let stream = lex(input);
115 let result = EditorData::parse(stream);
116 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
117
118 let editor = result.unwrap();
119 assert_eq!(editor.color.r, 0);
120 assert_eq!(editor.color.g, 111);
121 assert_eq!(editor.color.b, 152);
122 assert_eq!(editor.visgroupshown, true);
123 assert_eq!(editor.visgroupautoshown, true);
124 assert_eq!(editor.logicalpos, Some("[0 10000]"));
125 assert_eq!(editor.comments, Some("Test comment"));
126 }
127
128 #[test]
129 fn test_editor_minimal() {
130 let input = r#"
131 editor
132 {
133 "color" "255 0 0"
134 "visgroupshown" "1"
135 "visgroupautoshown" "1"
136 }
137 "#;
138
139 let stream = lex(input);
140 let result = EditorData::parse(stream);
141 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
142
143 let editor = result.unwrap();
144 assert_eq!(editor.color.r, 255);
145 assert_eq!(editor.color.g, 0);
146 assert_eq!(editor.color.b, 0);
147 assert_eq!(editor.visgroupshown, true);
148 assert_eq!(editor.visgroupautoshown, true);
149 assert_eq!(editor.logicalpos, None);
150 assert_eq!(editor.comments, None);
151 }
152
153 #[test]
154 fn test_editor_properties_out_of_order() {
155 let input = r#"
156 editor
157 {
158 "logicalpos" "[0 5000]"
159 "visgroupautoshown" "0"
160 "color" "100 200 50"
161 "comments" "Out of order test"
162 "visgroupshown" "0"
163 }
164 "#;
165
166 let stream = lex(input);
167 let result = EditorData::parse(stream);
168 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
169
170 let editor = result.unwrap();
171 assert_eq!(editor.color.r, 100);
172 assert_eq!(editor.color.g, 200);
173 assert_eq!(editor.color.b, 50);
174 assert_eq!(editor.visgroupshown, false);
175 assert_eq!(editor.visgroupautoshown, false);
176 assert_eq!(editor.logicalpos, Some("[0 5000]"));
177 assert_eq!(editor.comments, Some("Out of order test"));
178 }
179
180 #[test]
181 fn test_editor_with_logicalpos_only() {
182 let input = r#"
183 editor
184 {
185 "color" "128 128 128"
186 "visgroupshown" "1"
187 "visgroupautoshown" "1"
188 "logicalpos" "[0 0]"
189 }
190 "#;
191
192 let stream = lex(input);
193 let result = EditorData::parse(stream);
194 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
195
196 let editor = result.unwrap();
197 assert_eq!(editor.logicalpos, Some("[0 0]"));
198 assert_eq!(editor.comments, None);
199 }
200
201 #[test]
202 fn test_editor_with_comments_only() {
203 let input = r#"
204 editor
205 {
206 "color" "64 64 64"
207 "visgroupshown" "1"
208 "visgroupautoshown" "1"
209 "comments" "This brush needs work"
210 }
211 "#;
212
213 let stream = lex(input);
214 let result = EditorData::parse(stream);
215 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
216
217 let editor = result.unwrap();
218 assert_eq!(editor.comments, Some("This brush needs work"));
219 assert_eq!(editor.logicalpos, None);
220 }
221
222 #[test]
223 fn test_editor_empty_block() {
224 let input = r#"
225 editor
226 {
227 }
228 "#;
229
230 let stream = lex(input);
231 let result = EditorData::parse(stream);
232 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
233
234 let editor = result.unwrap();
235 let default = EditorData::default();
236 assert_eq!(editor.color.r, default.color.r);
237 assert_eq!(editor.visgroupshown, default.visgroupshown);
238 assert_eq!(editor.comments, None);
239 }
240
241 #[test]
242 fn test_editor_invalid_color() {
243 let input = r#"
244 editor
245 {
246 "color" "invalid color"
247 "visgroupshown" "1"
248 "visgroupautoshown" "1"
249 }
250 "#;
251
252 let stream = lex(input);
253 let result = EditorData::parse(stream);
254 assert!(result.is_err(), "Parser should fail on invalid color");
255 }
256
257 #[test]
258 fn test_editor_invalid_visgroupshown() {
259 let input = r#"
260 editor
261 {
262 "color" "255 255 255"
263 "visgroupshown" "not_a_bool"
264 "visgroupautoshown" "1"
265 }
266 "#;
267
268 let stream = lex(input);
269 let result = EditorData::parse(stream);
270 assert!(
271 result.is_err(),
272 "Parser should fail on invalid visgroupshown"
273 );
274 }
275
276 #[test]
277 fn test_editor_invalid_block_name() {
278 let input = r#"
279 wrongname
280 {
281 "color" "255 255 255"
282 "visgroupshown" "1"
283 "visgroupautoshown" "1"
284 }
285 "#;
286
287 let stream = lex(input);
288 let result = EditorData::parse(stream);
289 assert!(result.is_err(), "Parser should fail on invalid block name");
290 }
291
292 #[test]
293 fn test_editor_missing_closing_brace() {
294 let input = r#"
295 editor
296 {
297 "color" "255 255 255"
298 "visgroupshown" "1"
299 "visgroupautoshown" "1"
300 "#;
301
302 let stream = lex(input);
303 let result = EditorData::parse(stream);
304 assert!(
305 result.is_err(),
306 "Parser should fail on missing closing brace"
307 );
308 }
309
310 #[test]
311 fn test_editor_multiline_comments() {
312 let input = r#"
313 editor
314 {
315 "color" "50 100 150"
316 "visgroupshown" "1"
317 "visgroupautoshown" "1"
318 "comments" "This is a very long comment that describes the purpose of this brush in detail"
319 }
320 "#;
321
322 let stream = lex(input);
323 let result = EditorData::parse(stream);
324 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
325
326 let editor = result.unwrap();
327 assert_eq!(
328 editor.comments,
329 Some("This is a very long comment that describes the purpose of this brush in detail")
330 );
331 }
332
333 #[test]
334 fn test_editor_duplicate_properties_last_wins() {
335 let input = r#"
336 editor
337 {
338 "color" "100 100 100"
339 "visgroupshown" "0"
340 "visgroupshown" "1"
341 "color" "200 200 200"
342 }
343 "#;
344
345 let stream = lex(input);
346 let result = EditorData::parse(stream);
347 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
348
349 let editor = result.unwrap();
350 assert_eq!(editor.color.r, 200);
351 assert_eq!(editor.color.g, 200);
352 assert_eq!(editor.color.b, 200);
353 assert_eq!(editor.visgroupshown, true);
354 }
355}