1use crate::span::{Position, Span, Spanned};
6use crate::error::{RonErrorKind, RonParseError};
7use super::{RonValue, RonStruct};
8
9#[derive(Debug)]
10struct Parser<'a> {
11 source: &'a str,
12 bytes: &'a [u8],
13 offset: usize,
14 line: usize,
15 column: usize,
16}
17
18impl<'a> Parser<'a> {
19 fn new(source: &'a str) -> Self {
20 Self { source, bytes: source.as_bytes(), offset: 0, line: 1, column: 1 }
21 }
22
23 fn position(&self) -> Position {
24 Position { offset: self.offset, line: self.line, column: self.column }
25 }
26
27 fn peek(&self) -> Option<u8> {
28 self.bytes.get(self.offset).copied()
29 }
30
31 fn advance(&mut self) {
32 if let Some(byte) = self.peek() {
33 if byte == b'\n'{
34 self.column = 1;
35 self.line += 1;
36 } else {
37 self.column += 1;
38 }
39 self.offset += 1;
40 }
41 }
42
43 fn skip_whitespace(&mut self) {
44 loop {
45 match self.peek() {
46 Some(b' ' | b'\t' | b'\n' | b'\r') => self.advance(),
47 Some(b'/') if self.bytes.get(self.offset + 1) == Some(&b'/') => {
48 while self.peek().is_some_and(|b| b != b'\n') {
49 self.advance();
50 }
51 }
52 _ => break,
53 }
54 }
55 }
56
57 fn expect_char(&mut self, expected: u8) -> Result<(), RonParseError> {
58 let start = self.position();
59 match self.peek() {
60 Some(b) if b == expected => {
61 self.advance();
62 Ok(())
63 },
64 Some(b) => {
65 self.advance();
66 let end = self.position();
67 Err(RonParseError {
68 span: Span {
69 start,
70 end
71 },
72 kind: RonErrorKind::UnexpectedToken {
73 expected: format!("'{}'", expected as char),
74 found: format!("'{}'", b as char)
75 }
76 })
77 },
78 None => {
79 Err(RonParseError {
80 span: Span {
81 start,
82 end: start
83 },
84 kind: RonErrorKind::UnexpectedToken {
85 expected: format!("'{}'", expected as char),
86 found: "end of input".to_string()
87 }
88 })
89 }
90 }
91 }
92
93 fn parse_identifier(&mut self) -> Result<Spanned<String>, RonParseError> {
94 let start = self.position();
95
96 match self.peek() {
98 Some(b) if b.is_ascii_alphabetic() || b == b'_' => {},
99 Some(b) => {
100 self.advance();
101 let end = self.position();
102 return Err(RonParseError {
103 span: Span { start, end },
104 kind: RonErrorKind::UnexpectedToken {
105 expected: "identifier".to_string(),
106 found: format!("'{}'", b as char),
107 },
108 });
109 },
110 None => {
111 return Err(RonParseError {
112 span: Span { start, end: start },
113 kind: RonErrorKind::UnexpectedToken {
114 expected: "identifier".to_string(),
115 found: "end of input".to_string(),
116 },
117 });
118 },
119 }
120
121 while self.peek().is_some_and(|b| b.is_ascii_alphanumeric() || b == b'_') {
123 self.advance();
124 }
125
126 let end = self.position();
128 Ok(Spanned {
129 value: self.source[start.offset..end.offset].to_string(),
130 span: Span { start, end },
131 })
132 }
133
134 #[allow(clippy::too_many_lines)]
135 fn parse_value(&mut self) -> Result<Spanned<RonValue>, RonParseError> {
136 self.skip_whitespace();
137 let start = self.position();
138
139 match self.peek() {
140 Some(b'"') => {
141 self.advance(); let mut content = String::new();
143 loop {
144 match self.peek() {
145 Some(b'"') => {
146 self.advance(); break;
148 }
149 Some(b'\\') => {
153 self.advance(); match self.peek() {
155 Some(b'n') => { content.push('\n'); self.advance(); }
156 Some(b't') => { content.push('\t'); self.advance(); }
157 Some(b'\\') => { content.push('\\'); self.advance(); }
158 Some(b'"') => { content.push('"'); self.advance(); }
159 Some(b) => { content.push(b as char); self.advance(); }
160 None => {
161 return Err(RonParseError {
162 span: Span { start, end: self.position() },
163 kind: RonErrorKind::UnterminatedString,
164 });
165 }
166 }
167 }
168 Some(b) => {
169 content.push(b as char);
170 self.advance();
171 }
172 None => {
173 return Err(RonParseError {
174 span: Span { start, end: self.position() },
175 kind: RonErrorKind::UnterminatedString,
176 });
177 }
178 }
179 }
180 let end = self.position();
181 Ok(Spanned {
182 value: RonValue::String(content),
183 span: Span { start, end },
184 })
185 },
186 Some(b) if b.is_ascii_digit() || b == b'-' => {
187 if b == b'-' {
188 self.advance();
189 }
190
191 let mut has_dot = false;
192
193 loop {
194 match self.peek() {
195 Some(b) if b.is_ascii_digit() => {self.advance();},
196 Some(b'.') if !has_dot => {
197 has_dot = true;
198 self.advance();
199 },
200 Some(_) | None => {break;}
201 }
202 }
203
204 let end = self.position();
205 let number_str = &self.source[start.offset..end.offset];
206 if has_dot {
207 let num_float = number_str.parse::<f64>();
208 if let Ok(num) = num_float {
209 Ok(Spanned {
210 value: RonValue::Float(num),
211 span: Span { start, end },
212 })
213 } else {
214 Err(RonParseError {
215 span: Span { start, end },
216 kind: RonErrorKind::InvalidNumber { text: number_str.to_string() }
217 })
218 }
219 } else {
220 let num_int = number_str.parse::<i64>();
221 if let Ok(num) = num_int {
222 Ok(Spanned {
223 value: RonValue::Integer(num),
224 span: Span { start, end },
225 })
226 } else {
227 Err(RonParseError {
228 span: Span { start, end },
229 kind: RonErrorKind::InvalidNumber { text: number_str.to_string() }
230 })
231 }
232 }
233 },
234 Some(b) if b.is_ascii_alphabetic() => {
235 let identifier = self.parse_identifier()?;
236 let word = identifier.value.as_str();
237 let identifier_span = identifier.span;
238 match word {
239 "true" => {
240 Ok(Spanned { value: RonValue::Bool(true), span: identifier_span })
241 },
242 "false" => {
243 Ok(Spanned { value: RonValue::Bool(false), span: identifier_span })
244 }
245 "None" => {
246 Ok(Spanned { value: RonValue::Option(None), span: identifier_span })
247 }
248 "Some" => {
249 self.skip_whitespace();
250 self.expect_char(b'(')?;
251 let inner = self.parse_value()?;
252 self.expect_char(b')')?;
253 Ok(Spanned {
254 value: RonValue::Option(Some(Box::new(inner))),
255 span: Span { start, end: self.position() }
256 })
257 }
258 _ => {
259 Ok(Spanned {
260 value: RonValue::Identifier(word.to_string()),
261 span: identifier_span
262 })
263 }
264 }
265 },
266 Some(b'[') => {
267 self.advance();
268 let mut elements = Vec::new();
269 loop {
270 self.skip_whitespace();
271 if let Some(b']') = self.peek() {
272 break;
273 }
274 let value = self.parse_value()?;
275 elements.push(value);
276 self.skip_whitespace();
277 if let Some(b',') = self.peek() {
278 self.advance();
279 }
280 }
281 self.expect_char(b']')?;
282 Ok(Spanned {
283 value: RonValue::List(elements),
284 span: Span { start, end: self.position() }
285 })
286 },
287 Some(b'{') => {
288 self.advance();
289 let mut entries: Vec<(Spanned<RonValue>, Spanned<RonValue>)> = Vec::new();
290 loop {
291 self.skip_whitespace();
292 if let Some(b'}') = self.peek() {
293 break;
294 }
295 let key = self.parse_value()?;
296 self.skip_whitespace();
297 self.expect_char(b':')?;
298 self.skip_whitespace();
299 let value = self.parse_value()?;
300 entries.push((key, value));
301 self.skip_whitespace();
302 if let Some(b',') = self.peek() {
303 self.advance();
304 }
305 }
306 self.expect_char(b'}')?;
307 Ok(Spanned {
308 value: RonValue::Map(entries),
309 span: Span { start, end: self.position() },
310 })
311 },
312 Some(b'(') => {
313 self.advance();
314 self.skip_whitespace();
315
316 if self.peek() == Some(b')') {
318 let close_span_start = self.position();
319 self.expect_char(b')')?;
320 let close_span = Span { start: close_span_start, end: self.position() };
321 return Ok(Spanned {
322 value: RonValue::Struct(RonStruct { fields: Vec::new(), close_span }),
323 span: Span { start, end: self.position() },
324 });
325 }
326
327 let probe = (self.offset, self.line, self.column);
329 let is_struct = if let Ok(_id) = self.parse_identifier() {
330 self.skip_whitespace();
331
332 self.peek() == Some(b':')
333 } else {
334 false
335 };
336 self.offset = probe.0;
338 self.line = probe.1;
339 self.column = probe.2;
340
341 if is_struct {
342 let mut fields: Vec<(Spanned<String>, Spanned<RonValue>)> = Vec::new();
344 loop {
345 self.skip_whitespace();
346 if let Some(b')') = self.peek() {
347 break;
348 }
349 let field = self.parse_identifier()?;
350 self.skip_whitespace();
351 self.expect_char(b':')?;
352 self.skip_whitespace();
353 let value = self.parse_value()?;
354 fields.push((field, value));
355 self.skip_whitespace();
356 match self.peek() {
357 Some(b',') => self.advance(),
358 Some(_) => {}
359 None => {
360 return Err(RonParseError {
361 span: Span { start, end: self.position() },
362 kind: RonErrorKind::UnexpectedToken {
363 expected: "character".to_string(),
364 found: "end of file".to_string(),
365 },
366 });
367 }
368 }
369 }
370 let close_span_start = self.position();
371 self.expect_char(b')')?;
372 let close_span = Span { start: close_span_start, end: self.position() };
373 Ok(Spanned {
374 value: RonValue::Struct(RonStruct { fields, close_span }),
375 span: Span { start, end: self.position() },
376 })
377 } else {
378 let mut elements = Vec::new();
380 loop {
381 self.skip_whitespace();
382 if self.peek() == Some(b')') {
383 break;
384 }
385 let value = self.parse_value()?;
386 elements.push(value);
387 self.skip_whitespace();
388 if self.peek() == Some(b',') {
389 self.advance();
390 }
391 }
392 self.expect_char(b')')?;
393 Ok(Spanned {
394 value: RonValue::Tuple(elements),
395 span: Span { start, end: self.position() },
396 })
397 }
398 }
399 Some(b) => {
400 self.advance();
401 let end = self.position();
402 Err(RonParseError {
403 span: Span { start, end },
404 kind: RonErrorKind::UnexpectedToken {
405 expected: "value".to_string(),
406 found: format!("{}", b as char)
407 }
408 })
409 },
410 None => {
411 Err(RonParseError {
412 span: Span { start, end: start },
413 kind: RonErrorKind::UnexpectedToken {
414 expected: "value".to_string(),
415 found: "end of file".to_string()
416 }
417 })
418 }
419 }
420 }
421}
422
423pub fn parse_ron(source: &str) -> Result<Spanned<RonValue>, RonParseError> {
429 let mut parser = Parser::new(source);
430 parser.parse_value()
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436
437 fn parser(source: &str) -> Parser<'_> {
438 Parser::new(source)
439 }
440
441 #[test]
447 fn string_simple() {
448 let mut p = parser("\"hello\"");
449 let v = p.parse_value().unwrap();
450 assert_eq!(v.value, RonValue::String("hello".to_string()));
451 }
452
453 #[test]
455 fn string_empty() {
456 let mut p = parser("\"\"");
457 let v = p.parse_value().unwrap();
458 assert_eq!(v.value, RonValue::String("".to_string()));
459 }
460
461 #[test]
463 fn string_with_spaces() {
464 let mut p = parser("\"Ashborn Hound\"");
465 let v = p.parse_value().unwrap();
466 assert_eq!(v.value, RonValue::String("Ashborn Hound".to_string()));
467 }
468
469 #[test]
471 fn string_escaped_quote() {
472 let mut p = parser("\"say \\\"hi\\\"\"");
473 let v = p.parse_value().unwrap();
474 assert_eq!(v.value, RonValue::String("say \"hi\"".to_string()));
475 }
476
477 #[test]
479 fn string_escaped_backslash() {
480 let mut p = parser("\"a\\\\b\"");
481 let v = p.parse_value().unwrap();
482 assert_eq!(v.value, RonValue::String("a\\b".to_string()));
483 }
484
485 #[test]
487 fn string_escaped_newline() {
488 let mut p = parser("\"line1\\nline2\"");
489 let v = p.parse_value().unwrap();
490 assert_eq!(v.value, RonValue::String("line1\nline2".to_string()));
491 }
492
493 #[test]
495 fn string_escaped_tab() {
496 let mut p = parser("\"col1\\tcol2\"");
497 let v = p.parse_value().unwrap();
498 assert_eq!(v.value, RonValue::String("col1\tcol2".to_string()));
499 }
500
501 #[test]
503 fn string_unterminated() {
504 let mut p = parser("\"hello");
505 let err = p.parse_value().unwrap_err();
506 assert_eq!(err.kind, RonErrorKind::UnterminatedString);
507 }
508
509 #[test]
515 fn integer_positive() {
516 let mut p = parser("42");
517 let v = p.parse_value().unwrap();
518 assert_eq!(v.value, RonValue::Integer(42));
519 }
520
521 #[test]
523 fn integer_zero() {
524 let mut p = parser("0");
525 let v = p.parse_value().unwrap();
526 assert_eq!(v.value, RonValue::Integer(0));
527 }
528
529 #[test]
531 fn integer_negative() {
532 let mut p = parser("-7");
533 let v = p.parse_value().unwrap();
534 assert_eq!(v.value, RonValue::Integer(-7));
535 }
536
537 #[test]
543 fn float_simple() {
544 let mut p = parser("3.14");
545 let v = p.parse_value().unwrap();
546 assert_eq!(v.value, RonValue::Float(3.14));
547 }
548
549 #[test]
551 fn float_negative() {
552 let mut p = parser("-0.5");
553 let v = p.parse_value().unwrap();
554 assert_eq!(v.value, RonValue::Float(-0.5));
555 }
556
557 #[test]
559 fn float_one_point_zero() {
560 let mut p = parser("1.0");
561 let v = p.parse_value().unwrap();
562 assert_eq!(v.value, RonValue::Float(1.0));
563 }
564
565 #[test]
571 fn bool_true() {
572 let mut p = parser("true");
573 let v = p.parse_value().unwrap();
574 assert_eq!(v.value, RonValue::Bool(true));
575 }
576
577 #[test]
579 fn bool_false() {
580 let mut p = parser("false");
581 let v = p.parse_value().unwrap();
582 assert_eq!(v.value, RonValue::Bool(false));
583 }
584
585 #[test]
591 fn option_none() {
592 let mut p = parser("None");
593 let v = p.parse_value().unwrap();
594 assert_eq!(v.value, RonValue::Option(None));
595 }
596
597 #[test]
599 fn option_some_integer() {
600 let mut p = parser("Some(5)");
601 let v = p.parse_value().unwrap();
602 if let RonValue::Option(Some(inner)) = &v.value {
603 assert_eq!(inner.value, RonValue::Integer(5));
604 } else {
605 panic!("expected Option(Some(...))");
606 }
607 }
608
609 #[test]
611 fn option_some_string() {
612 let mut p = parser("Some(\"hi\")");
613 let v = p.parse_value().unwrap();
614 if let RonValue::Option(Some(inner)) = &v.value {
615 assert_eq!(inner.value, RonValue::String("hi".to_string()));
616 } else {
617 panic!("expected Option(Some(...))");
618 }
619 }
620
621 #[test]
627 fn identifier_bare() {
628 let mut p = parser("Creature");
629 let v = p.parse_value().unwrap();
630 assert_eq!(v.value, RonValue::Identifier("Creature".to_string()));
631 }
632
633 #[test]
635 fn identifier_another() {
636 let mut p = parser("Sentinels");
637 let v = p.parse_value().unwrap();
638 assert_eq!(v.value, RonValue::Identifier("Sentinels".to_string()));
639 }
640
641 #[test]
647 fn list_empty() {
648 let mut p = parser("[]");
649 let v = p.parse_value().unwrap();
650 if let RonValue::List(elems) = &v.value {
651 assert!(elems.is_empty());
652 } else {
653 panic!("expected List");
654 }
655 }
656
657 #[test]
659 fn list_single_element() {
660 let mut p = parser("[Creature]");
661 let v = p.parse_value().unwrap();
662 if let RonValue::List(elems) = &v.value {
663 assert_eq!(elems.len(), 1);
664 assert_eq!(elems[0].value, RonValue::Identifier("Creature".to_string()));
665 } else {
666 panic!("expected List");
667 }
668 }
669
670 #[test]
672 fn list_multiple_elements() {
673 let mut p = parser("[Creature, Trap, Artifact]");
674 let v = p.parse_value().unwrap();
675 if let RonValue::List(elems) = &v.value {
676 assert_eq!(elems.len(), 3);
677 } else {
678 panic!("expected List");
679 }
680 }
681
682 #[test]
684 fn list_trailing_comma() {
685 let mut p = parser("[Creature, Trap,]");
686 let v = p.parse_value().unwrap();
687 if let RonValue::List(elems) = &v.value {
688 assert_eq!(elems.len(), 2);
689 } else {
690 panic!("expected List");
691 }
692 }
693
694 #[test]
696 fn list_of_strings() {
697 let mut p = parser("[\"Vigilance\", \"Haste\"]");
698 let v = p.parse_value().unwrap();
699 if let RonValue::List(elems) = &v.value {
700 assert_eq!(elems.len(), 2);
701 assert_eq!(elems[0].value, RonValue::String("Vigilance".to_string()));
702 assert_eq!(elems[1].value, RonValue::String("Haste".to_string()));
703 } else {
704 panic!("expected List");
705 }
706 }
707
708 #[test]
714 fn struct_empty() {
715 let mut p = parser("()");
716 let v = p.parse_value().unwrap();
717 if let RonValue::Struct(s) = &v.value {
718 assert!(s.fields.is_empty());
719 } else {
720 panic!("expected Struct");
721 }
722 }
723
724 #[test]
726 fn struct_single_field() {
727 let mut p = parser("(name: \"Ashborn Hound\")");
728 let v = p.parse_value().unwrap();
729 if let RonValue::Struct(s) = &v.value {
730 assert_eq!(s.fields.len(), 1);
731 assert_eq!(s.fields[0].0.value, "name");
732 assert_eq!(s.fields[0].1.value, RonValue::String("Ashborn Hound".to_string()));
733 } else {
734 panic!("expected Struct");
735 }
736 }
737
738 #[test]
740 fn struct_multiple_fields() {
741 let mut p = parser("(name: \"foo\", age: 5)");
742 let v = p.parse_value().unwrap();
743 if let RonValue::Struct(s) = &v.value {
744 assert_eq!(s.fields.len(), 2);
745 } else {
746 panic!("expected Struct");
747 }
748 }
749
750 #[test]
752 fn struct_trailing_comma() {
753 let mut p = parser("(name: \"foo\",)");
754 let v = p.parse_value().unwrap();
755 if let RonValue::Struct(s) = &v.value {
756 assert_eq!(s.fields.len(), 1);
757 } else {
758 panic!("expected Struct");
759 }
760 }
761
762 #[test]
764 fn struct_close_span_captured() {
765 let mut p = parser("(x: 1)");
766 let v = p.parse_value().unwrap();
767 if let RonValue::Struct(s) = &v.value {
768 assert_eq!(s.close_span.start.offset, 5);
769 assert_eq!(s.close_span.end.offset, 6);
770 } else {
771 panic!("expected Struct");
772 }
773 }
774
775 #[test]
777 fn struct_nested() {
778 let mut p = parser("(cost: (generic: 2, sigil: 1))");
779 let v = p.parse_value().unwrap();
780 if let RonValue::Struct(s) = &v.value {
781 assert_eq!(s.fields.len(), 1);
782 assert_eq!(s.fields[0].0.value, "cost");
783 if let RonValue::Struct(inner) = &s.fields[0].1.value {
784 assert_eq!(inner.fields.len(), 2);
785 } else {
786 panic!("expected nested Struct");
787 }
788 } else {
789 panic!("expected Struct");
790 }
791 }
792
793 #[test]
799 fn whitespace_leading() {
800 let mut p = parser(" 42");
801 let v = p.parse_value().unwrap();
802 assert_eq!(v.value, RonValue::Integer(42));
803 }
804
805 #[test]
807 fn comment_before_value() {
808 let mut p = parser("// comment\n42");
809 let v = p.parse_value().unwrap();
810 assert_eq!(v.value, RonValue::Integer(42));
811 }
812
813 #[test]
819 fn span_starts_after_whitespace() {
820 let mut p = parser(" 42");
821 let v = p.parse_value().unwrap();
822 assert_eq!(v.span.start.offset, 2);
823 }
824
825 #[test]
827 fn span_covers_string() {
828 let mut p = parser("\"hello\"");
829 let v = p.parse_value().unwrap();
830 assert_eq!(v.span.start.offset, 0);
831 assert_eq!(v.span.end.offset, 7);
832 }
833
834 #[test]
840 fn error_empty_input() {
841 let mut p = parser("");
842 let err = p.parse_value().unwrap_err();
843 match err.kind {
844 RonErrorKind::UnexpectedToken { found, .. } => {
845 assert_eq!(found, "end of file");
846 }
847 other => panic!("expected UnexpectedToken, got {:?}", other),
848 }
849 }
850
851 #[test]
853 fn error_unexpected_char() {
854 let mut p = parser("@");
855 assert!(p.parse_value().is_err());
856 }
857
858 #[test]
864 fn ron_full_struct() {
865 let source = r#"(
866 name: "Ashborn Hound",
867 card_types: [Creature],
868 legendary: false,
869 power: Some(1),
870 toughness: None,
871 keywords: [],
872 flavor_text: "placeholder",
873 )"#;
874 let v = parse_ron(source).unwrap();
875 if let RonValue::Struct(s) = &v.value {
876 assert_eq!(s.fields.len(), 7);
877 assert_eq!(s.fields[0].0.value, "name");
878 assert_eq!(s.fields[0].1.value, RonValue::String("Ashborn Hound".to_string()));
879 } else {
880 panic!("expected Struct");
881 }
882 }
883
884 #[test]
890 fn map_empty() {
891 let mut p = parser("{}");
892 let v = p.parse_value().unwrap();
893 if let RonValue::Map(entries) = &v.value {
894 assert!(entries.is_empty());
895 } else {
896 panic!("expected Map");
897 }
898 }
899
900 #[test]
902 fn map_string_keys() {
903 let mut p = parser("{\"str\": 5, \"dex\": 3}");
904 let v = p.parse_value().unwrap();
905 if let RonValue::Map(entries) = &v.value {
906 assert_eq!(entries.len(), 2);
907 assert_eq!(entries[0].0.value, RonValue::String("str".to_string()));
908 assert_eq!(entries[0].1.value, RonValue::Integer(5));
909 } else {
910 panic!("expected Map");
911 }
912 }
913
914 #[test]
916 fn map_integer_keys() {
917 let mut p = parser("{1: \"one\", 2: \"two\"}");
918 let v = p.parse_value().unwrap();
919 if let RonValue::Map(entries) = &v.value {
920 assert_eq!(entries.len(), 2);
921 assert_eq!(entries[0].0.value, RonValue::Integer(1));
922 } else {
923 panic!("expected Map");
924 }
925 }
926
927 #[test]
929 fn map_trailing_comma() {
930 let mut p = parser("{\"a\": 1,}");
931 let v = p.parse_value().unwrap();
932 if let RonValue::Map(entries) = &v.value {
933 assert_eq!(entries.len(), 1);
934 } else {
935 panic!("expected Map");
936 }
937 }
938
939 #[test]
945 fn tuple_two_elements() {
946 let mut p = parser("(1.0, 2.5)");
947 let v = p.parse_value().unwrap();
948 if let RonValue::Tuple(elems) = &v.value {
949 assert_eq!(elems.len(), 2);
950 assert_eq!(elems[0].value, RonValue::Float(1.0));
951 assert_eq!(elems[1].value, RonValue::Float(2.5));
952 } else {
953 panic!("expected Tuple, got {:?}", v.value);
954 }
955 }
956
957 #[test]
959 fn tuple_mixed_types() {
960 let mut p = parser("(\"hello\", 42, true)");
961 let v = p.parse_value().unwrap();
962 if let RonValue::Tuple(elems) = &v.value {
963 assert_eq!(elems.len(), 3);
964 assert_eq!(elems[0].value, RonValue::String("hello".to_string()));
965 assert_eq!(elems[1].value, RonValue::Integer(42));
966 assert_eq!(elems[2].value, RonValue::Bool(true));
967 } else {
968 panic!("expected Tuple, got {:?}", v.value);
969 }
970 }
971
972 #[test]
974 fn struct_still_parses() {
975 let mut p = parser("(name: \"foo\", age: 5)");
976 let v = p.parse_value().unwrap();
977 if let RonValue::Struct(s) = &v.value {
978 assert_eq!(s.fields.len(), 2);
979 } else {
980 panic!("expected Struct, got {:?}", v.value);
981 }
982 }
983
984 #[test]
986 fn empty_parens_is_struct() {
987 let mut p = parser("()");
988 let v = p.parse_value().unwrap();
989 assert!(matches!(v.value, RonValue::Struct(_)));
990 }
991
992 #[test]
994 fn tuple_single_element_trailing_comma() {
995 let mut p = parser("(42,)");
996 let v = p.parse_value().unwrap();
997 if let RonValue::Tuple(elems) = &v.value {
998 assert_eq!(elems.len(), 1);
999 } else {
1000 panic!("expected Tuple, got {:?}", v.value);
1001 }
1002 }
1003}