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 self.skip_whitespace();
261 if self.peek() == Some(b'(') {
262 self.advance(); self.skip_whitespace();
264 let inner = self.parse_value()?;
265 self.skip_whitespace();
266 self.expect_char(b')')?;
267 Ok(Spanned {
268 value: RonValue::EnumVariant(word.to_string(), Box::new(inner)),
269 span: Span { start, end: self.position() },
270 })
271 } else {
272 Ok(Spanned {
273 value: RonValue::Identifier(word.to_string()),
274 span: identifier_span,
275 })
276 }
277 }
278 }
279 },
280 Some(b'[') => {
281 self.advance();
282 let mut elements = Vec::new();
283 loop {
284 self.skip_whitespace();
285 if let Some(b']') = self.peek() {
286 break;
287 }
288 let value = self.parse_value()?;
289 elements.push(value);
290 self.skip_whitespace();
291 if let Some(b',') = self.peek() {
292 self.advance();
293 }
294 }
295 self.expect_char(b']')?;
296 Ok(Spanned {
297 value: RonValue::List(elements),
298 span: Span { start, end: self.position() }
299 })
300 },
301 Some(b'{') => {
302 self.advance();
303 let mut entries: Vec<(Spanned<RonValue>, Spanned<RonValue>)> = Vec::new();
304 loop {
305 self.skip_whitespace();
306 if let Some(b'}') = self.peek() {
307 break;
308 }
309 let key = self.parse_value()?;
310 self.skip_whitespace();
311 self.expect_char(b':')?;
312 self.skip_whitespace();
313 let value = self.parse_value()?;
314 entries.push((key, value));
315 self.skip_whitespace();
316 if let Some(b',') = self.peek() {
317 self.advance();
318 }
319 }
320 self.expect_char(b'}')?;
321 Ok(Spanned {
322 value: RonValue::Map(entries),
323 span: Span { start, end: self.position() },
324 })
325 },
326 Some(b'(') => {
327 self.advance();
328 self.skip_whitespace();
329
330 if self.peek() == Some(b')') {
332 let close_span_start = self.position();
333 self.expect_char(b')')?;
334 let close_span = Span { start: close_span_start, end: self.position() };
335 return Ok(Spanned {
336 value: RonValue::Struct(RonStruct { fields: Vec::new(), close_span }),
337 span: Span { start, end: self.position() },
338 });
339 }
340
341 let probe = (self.offset, self.line, self.column);
343 let is_struct = if let Ok(_id) = self.parse_identifier() {
344 self.skip_whitespace();
345
346 self.peek() == Some(b':')
347 } else {
348 false
349 };
350 self.offset = probe.0;
352 self.line = probe.1;
353 self.column = probe.2;
354
355 if is_struct {
356 let mut fields: Vec<(Spanned<String>, Spanned<RonValue>)> = Vec::new();
358 loop {
359 self.skip_whitespace();
360 if let Some(b')') = self.peek() {
361 break;
362 }
363 let field = self.parse_identifier()?;
364 self.skip_whitespace();
365 self.expect_char(b':')?;
366 self.skip_whitespace();
367 let value = self.parse_value()?;
368 fields.push((field, value));
369 self.skip_whitespace();
370 match self.peek() {
371 Some(b',') => self.advance(),
372 Some(_) => {}
373 None => {
374 return Err(RonParseError {
375 span: Span { start, end: self.position() },
376 kind: RonErrorKind::UnexpectedToken {
377 expected: "character".to_string(),
378 found: "end of file".to_string(),
379 },
380 });
381 }
382 }
383 }
384 let close_span_start = self.position();
385 self.expect_char(b')')?;
386 let close_span = Span { start: close_span_start, end: self.position() };
387 Ok(Spanned {
388 value: RonValue::Struct(RonStruct { fields, close_span }),
389 span: Span { start, end: self.position() },
390 })
391 } else {
392 let mut elements = Vec::new();
394 loop {
395 self.skip_whitespace();
396 if self.peek() == Some(b')') {
397 break;
398 }
399 let value = self.parse_value()?;
400 elements.push(value);
401 self.skip_whitespace();
402 if self.peek() == Some(b',') {
403 self.advance();
404 }
405 }
406 self.expect_char(b')')?;
407 Ok(Spanned {
408 value: RonValue::Tuple(elements),
409 span: Span { start, end: self.position() },
410 })
411 }
412 }
413 Some(b) => {
414 self.advance();
415 let end = self.position();
416 Err(RonParseError {
417 span: Span { start, end },
418 kind: RonErrorKind::UnexpectedToken {
419 expected: "value".to_string(),
420 found: format!("{}", b as char)
421 }
422 })
423 },
424 None => {
425 Err(RonParseError {
426 span: Span { start, end: start },
427 kind: RonErrorKind::UnexpectedToken {
428 expected: "value".to_string(),
429 found: "end of file".to_string()
430 }
431 })
432 }
433 }
434 }
435}
436
437pub fn parse_ron(source: &str) -> Result<Spanned<RonValue>, RonParseError> {
443 let mut parser = Parser::new(source);
444 parser.parse_value()
445}
446
447#[cfg(test)]
448mod tests {
449 use super::*;
450
451 fn parser(source: &str) -> Parser<'_> {
452 Parser::new(source)
453 }
454
455 #[test]
461 fn string_simple() {
462 let mut p = parser("\"hello\"");
463 let v = p.parse_value().unwrap();
464 assert_eq!(v.value, RonValue::String("hello".to_string()));
465 }
466
467 #[test]
469 fn string_empty() {
470 let mut p = parser("\"\"");
471 let v = p.parse_value().unwrap();
472 assert_eq!(v.value, RonValue::String("".to_string()));
473 }
474
475 #[test]
477 fn string_with_spaces() {
478 let mut p = parser("\"Ashborn Hound\"");
479 let v = p.parse_value().unwrap();
480 assert_eq!(v.value, RonValue::String("Ashborn Hound".to_string()));
481 }
482
483 #[test]
485 fn string_escaped_quote() {
486 let mut p = parser("\"say \\\"hi\\\"\"");
487 let v = p.parse_value().unwrap();
488 assert_eq!(v.value, RonValue::String("say \"hi\"".to_string()));
489 }
490
491 #[test]
493 fn string_escaped_backslash() {
494 let mut p = parser("\"a\\\\b\"");
495 let v = p.parse_value().unwrap();
496 assert_eq!(v.value, RonValue::String("a\\b".to_string()));
497 }
498
499 #[test]
501 fn string_escaped_newline() {
502 let mut p = parser("\"line1\\nline2\"");
503 let v = p.parse_value().unwrap();
504 assert_eq!(v.value, RonValue::String("line1\nline2".to_string()));
505 }
506
507 #[test]
509 fn string_escaped_tab() {
510 let mut p = parser("\"col1\\tcol2\"");
511 let v = p.parse_value().unwrap();
512 assert_eq!(v.value, RonValue::String("col1\tcol2".to_string()));
513 }
514
515 #[test]
517 fn string_unterminated() {
518 let mut p = parser("\"hello");
519 let err = p.parse_value().unwrap_err();
520 assert_eq!(err.kind, RonErrorKind::UnterminatedString);
521 }
522
523 #[test]
529 fn integer_positive() {
530 let mut p = parser("42");
531 let v = p.parse_value().unwrap();
532 assert_eq!(v.value, RonValue::Integer(42));
533 }
534
535 #[test]
537 fn integer_zero() {
538 let mut p = parser("0");
539 let v = p.parse_value().unwrap();
540 assert_eq!(v.value, RonValue::Integer(0));
541 }
542
543 #[test]
545 fn integer_negative() {
546 let mut p = parser("-7");
547 let v = p.parse_value().unwrap();
548 assert_eq!(v.value, RonValue::Integer(-7));
549 }
550
551 #[test]
557 fn float_simple() {
558 let mut p = parser("3.14");
559 let v = p.parse_value().unwrap();
560 assert_eq!(v.value, RonValue::Float(3.14));
561 }
562
563 #[test]
565 fn float_negative() {
566 let mut p = parser("-0.5");
567 let v = p.parse_value().unwrap();
568 assert_eq!(v.value, RonValue::Float(-0.5));
569 }
570
571 #[test]
573 fn float_one_point_zero() {
574 let mut p = parser("1.0");
575 let v = p.parse_value().unwrap();
576 assert_eq!(v.value, RonValue::Float(1.0));
577 }
578
579 #[test]
585 fn bool_true() {
586 let mut p = parser("true");
587 let v = p.parse_value().unwrap();
588 assert_eq!(v.value, RonValue::Bool(true));
589 }
590
591 #[test]
593 fn bool_false() {
594 let mut p = parser("false");
595 let v = p.parse_value().unwrap();
596 assert_eq!(v.value, RonValue::Bool(false));
597 }
598
599 #[test]
605 fn option_none() {
606 let mut p = parser("None");
607 let v = p.parse_value().unwrap();
608 assert_eq!(v.value, RonValue::Option(None));
609 }
610
611 #[test]
613 fn option_some_integer() {
614 let mut p = parser("Some(5)");
615 let v = p.parse_value().unwrap();
616 if let RonValue::Option(Some(inner)) = &v.value {
617 assert_eq!(inner.value, RonValue::Integer(5));
618 } else {
619 panic!("expected Option(Some(...))");
620 }
621 }
622
623 #[test]
625 fn option_some_string() {
626 let mut p = parser("Some(\"hi\")");
627 let v = p.parse_value().unwrap();
628 if let RonValue::Option(Some(inner)) = &v.value {
629 assert_eq!(inner.value, RonValue::String("hi".to_string()));
630 } else {
631 panic!("expected Option(Some(...))");
632 }
633 }
634
635 #[test]
641 fn identifier_bare() {
642 let mut p = parser("Creature");
643 let v = p.parse_value().unwrap();
644 assert_eq!(v.value, RonValue::Identifier("Creature".to_string()));
645 }
646
647 #[test]
649 fn identifier_another() {
650 let mut p = parser("Sentinels");
651 let v = p.parse_value().unwrap();
652 assert_eq!(v.value, RonValue::Identifier("Sentinels".to_string()));
653 }
654
655 #[test]
661 fn list_empty() {
662 let mut p = parser("[]");
663 let v = p.parse_value().unwrap();
664 if let RonValue::List(elems) = &v.value {
665 assert!(elems.is_empty());
666 } else {
667 panic!("expected List");
668 }
669 }
670
671 #[test]
673 fn list_single_element() {
674 let mut p = parser("[Creature]");
675 let v = p.parse_value().unwrap();
676 if let RonValue::List(elems) = &v.value {
677 assert_eq!(elems.len(), 1);
678 assert_eq!(elems[0].value, RonValue::Identifier("Creature".to_string()));
679 } else {
680 panic!("expected List");
681 }
682 }
683
684 #[test]
686 fn list_multiple_elements() {
687 let mut p = parser("[Creature, Trap, Artifact]");
688 let v = p.parse_value().unwrap();
689 if let RonValue::List(elems) = &v.value {
690 assert_eq!(elems.len(), 3);
691 } else {
692 panic!("expected List");
693 }
694 }
695
696 #[test]
698 fn list_trailing_comma() {
699 let mut p = parser("[Creature, Trap,]");
700 let v = p.parse_value().unwrap();
701 if let RonValue::List(elems) = &v.value {
702 assert_eq!(elems.len(), 2);
703 } else {
704 panic!("expected List");
705 }
706 }
707
708 #[test]
710 fn list_of_strings() {
711 let mut p = parser("[\"Vigilance\", \"Haste\"]");
712 let v = p.parse_value().unwrap();
713 if let RonValue::List(elems) = &v.value {
714 assert_eq!(elems.len(), 2);
715 assert_eq!(elems[0].value, RonValue::String("Vigilance".to_string()));
716 assert_eq!(elems[1].value, RonValue::String("Haste".to_string()));
717 } else {
718 panic!("expected List");
719 }
720 }
721
722 #[test]
728 fn struct_empty() {
729 let mut p = parser("()");
730 let v = p.parse_value().unwrap();
731 if let RonValue::Struct(s) = &v.value {
732 assert!(s.fields.is_empty());
733 } else {
734 panic!("expected Struct");
735 }
736 }
737
738 #[test]
740 fn struct_single_field() {
741 let mut p = parser("(name: \"Ashborn Hound\")");
742 let v = p.parse_value().unwrap();
743 if let RonValue::Struct(s) = &v.value {
744 assert_eq!(s.fields.len(), 1);
745 assert_eq!(s.fields[0].0.value, "name");
746 assert_eq!(s.fields[0].1.value, RonValue::String("Ashborn Hound".to_string()));
747 } else {
748 panic!("expected Struct");
749 }
750 }
751
752 #[test]
754 fn struct_multiple_fields() {
755 let mut p = parser("(name: \"foo\", age: 5)");
756 let v = p.parse_value().unwrap();
757 if let RonValue::Struct(s) = &v.value {
758 assert_eq!(s.fields.len(), 2);
759 } else {
760 panic!("expected Struct");
761 }
762 }
763
764 #[test]
766 fn struct_trailing_comma() {
767 let mut p = parser("(name: \"foo\",)");
768 let v = p.parse_value().unwrap();
769 if let RonValue::Struct(s) = &v.value {
770 assert_eq!(s.fields.len(), 1);
771 } else {
772 panic!("expected Struct");
773 }
774 }
775
776 #[test]
778 fn struct_close_span_captured() {
779 let mut p = parser("(x: 1)");
780 let v = p.parse_value().unwrap();
781 if let RonValue::Struct(s) = &v.value {
782 assert_eq!(s.close_span.start.offset, 5);
783 assert_eq!(s.close_span.end.offset, 6);
784 } else {
785 panic!("expected Struct");
786 }
787 }
788
789 #[test]
791 fn struct_nested() {
792 let mut p = parser("(cost: (generic: 2, sigil: 1))");
793 let v = p.parse_value().unwrap();
794 if let RonValue::Struct(s) = &v.value {
795 assert_eq!(s.fields.len(), 1);
796 assert_eq!(s.fields[0].0.value, "cost");
797 if let RonValue::Struct(inner) = &s.fields[0].1.value {
798 assert_eq!(inner.fields.len(), 2);
799 } else {
800 panic!("expected nested Struct");
801 }
802 } else {
803 panic!("expected Struct");
804 }
805 }
806
807 #[test]
813 fn whitespace_leading() {
814 let mut p = parser(" 42");
815 let v = p.parse_value().unwrap();
816 assert_eq!(v.value, RonValue::Integer(42));
817 }
818
819 #[test]
821 fn comment_before_value() {
822 let mut p = parser("// comment\n42");
823 let v = p.parse_value().unwrap();
824 assert_eq!(v.value, RonValue::Integer(42));
825 }
826
827 #[test]
833 fn span_starts_after_whitespace() {
834 let mut p = parser(" 42");
835 let v = p.parse_value().unwrap();
836 assert_eq!(v.span.start.offset, 2);
837 }
838
839 #[test]
841 fn span_covers_string() {
842 let mut p = parser("\"hello\"");
843 let v = p.parse_value().unwrap();
844 assert_eq!(v.span.start.offset, 0);
845 assert_eq!(v.span.end.offset, 7);
846 }
847
848 #[test]
854 fn error_empty_input() {
855 let mut p = parser("");
856 let err = p.parse_value().unwrap_err();
857 match err.kind {
858 RonErrorKind::UnexpectedToken { found, .. } => {
859 assert_eq!(found, "end of file");
860 }
861 other => panic!("expected UnexpectedToken, got {:?}", other),
862 }
863 }
864
865 #[test]
867 fn error_unexpected_char() {
868 let mut p = parser("@");
869 assert!(p.parse_value().is_err());
870 }
871
872 #[test]
878 fn ron_full_struct() {
879 let source = r#"(
880 name: "Ashborn Hound",
881 card_types: [Creature],
882 legendary: false,
883 power: Some(1),
884 toughness: None,
885 keywords: [],
886 flavor_text: "placeholder",
887 )"#;
888 let v = parse_ron(source).unwrap();
889 if let RonValue::Struct(s) = &v.value {
890 assert_eq!(s.fields.len(), 7);
891 assert_eq!(s.fields[0].0.value, "name");
892 assert_eq!(s.fields[0].1.value, RonValue::String("Ashborn Hound".to_string()));
893 } else {
894 panic!("expected Struct");
895 }
896 }
897
898 #[test]
904 fn map_empty() {
905 let mut p = parser("{}");
906 let v = p.parse_value().unwrap();
907 if let RonValue::Map(entries) = &v.value {
908 assert!(entries.is_empty());
909 } else {
910 panic!("expected Map");
911 }
912 }
913
914 #[test]
916 fn map_string_keys() {
917 let mut p = parser("{\"str\": 5, \"dex\": 3}");
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::String("str".to_string()));
922 assert_eq!(entries[0].1.value, RonValue::Integer(5));
923 } else {
924 panic!("expected Map");
925 }
926 }
927
928 #[test]
930 fn map_integer_keys() {
931 let mut p = parser("{1: \"one\", 2: \"two\"}");
932 let v = p.parse_value().unwrap();
933 if let RonValue::Map(entries) = &v.value {
934 assert_eq!(entries.len(), 2);
935 assert_eq!(entries[0].0.value, RonValue::Integer(1));
936 } else {
937 panic!("expected Map");
938 }
939 }
940
941 #[test]
943 fn map_trailing_comma() {
944 let mut p = parser("{\"a\": 1,}");
945 let v = p.parse_value().unwrap();
946 if let RonValue::Map(entries) = &v.value {
947 assert_eq!(entries.len(), 1);
948 } else {
949 panic!("expected Map");
950 }
951 }
952
953 #[test]
959 fn tuple_two_elements() {
960 let mut p = parser("(1.0, 2.5)");
961 let v = p.parse_value().unwrap();
962 if let RonValue::Tuple(elems) = &v.value {
963 assert_eq!(elems.len(), 2);
964 assert_eq!(elems[0].value, RonValue::Float(1.0));
965 assert_eq!(elems[1].value, RonValue::Float(2.5));
966 } else {
967 panic!("expected Tuple, got {:?}", v.value);
968 }
969 }
970
971 #[test]
973 fn tuple_mixed_types() {
974 let mut p = parser("(\"hello\", 42, true)");
975 let v = p.parse_value().unwrap();
976 if let RonValue::Tuple(elems) = &v.value {
977 assert_eq!(elems.len(), 3);
978 assert_eq!(elems[0].value, RonValue::String("hello".to_string()));
979 assert_eq!(elems[1].value, RonValue::Integer(42));
980 assert_eq!(elems[2].value, RonValue::Bool(true));
981 } else {
982 panic!("expected Tuple, got {:?}", v.value);
983 }
984 }
985
986 #[test]
988 fn struct_still_parses() {
989 let mut p = parser("(name: \"foo\", age: 5)");
990 let v = p.parse_value().unwrap();
991 if let RonValue::Struct(s) = &v.value {
992 assert_eq!(s.fields.len(), 2);
993 } else {
994 panic!("expected Struct, got {:?}", v.value);
995 }
996 }
997
998 #[test]
1000 fn empty_parens_is_struct() {
1001 let mut p = parser("()");
1002 let v = p.parse_value().unwrap();
1003 assert!(matches!(v.value, RonValue::Struct(_)));
1004 }
1005
1006 #[test]
1008 fn tuple_single_element_trailing_comma() {
1009 let mut p = parser("(42,)");
1010 let v = p.parse_value().unwrap();
1011 if let RonValue::Tuple(elems) = &v.value {
1012 assert_eq!(elems.len(), 1);
1013 } else {
1014 panic!("expected Tuple, got {:?}", v.value);
1015 }
1016 }
1017
1018 #[test]
1024 fn enum_variant_with_integer_data() {
1025 let mut p = parser("Damage(5)");
1026 let v = p.parse_value().unwrap();
1027 if let RonValue::EnumVariant(name, data) = &v.value {
1028 assert_eq!(name, "Damage");
1029 assert_eq!(data.value, RonValue::Integer(5));
1030 } else {
1031 panic!("expected EnumVariant, got {:?}", v.value);
1032 }
1033 }
1034
1035 #[test]
1037 fn enum_variant_with_string_data() {
1038 let mut p = parser("Message(\"hello\")");
1039 let v = p.parse_value().unwrap();
1040 if let RonValue::EnumVariant(name, data) = &v.value {
1041 assert_eq!(name, "Message");
1042 assert_eq!(data.value, RonValue::String("hello".to_string()));
1043 } else {
1044 panic!("expected EnumVariant, got {:?}", v.value);
1045 }
1046 }
1047
1048 #[test]
1050 fn bare_identifier_unchanged() {
1051 let mut p = parser("Creature,");
1052 let v = p.parse_value().unwrap();
1053 assert_eq!(v.value, RonValue::Identifier("Creature".to_string()));
1054 }
1055
1056 #[test]
1058 fn some_not_enum_variant() {
1059 let mut p = parser("Some(5)");
1060 let v = p.parse_value().unwrap();
1061 assert!(matches!(v.value, RonValue::Option(Some(_))));
1062 }
1063}