1use crate::span::{Position, Span, Spanned};
6use crate::error::{RonErrorKind, RonParseError};
7use super::{RonValue, RonStruct};
8
9#[derive(Debug)]
10pub(crate) struct 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 pub(crate) fn new_at(source: &'a str, offset: usize, position: Position) -> Self {
26 Self { source, bytes: source.as_bytes(), offset, line: position.line, column: position.column }
27 }
28
29 pub(crate) fn current_offset(&self) -> usize {
31 self.offset
32 }
33
34 pub(crate) fn parse_single_value(&mut self) -> Result<Spanned<RonValue>, RonParseError> {
36 self.parse_value()
37 }
38
39 fn position(&self) -> Position {
40 Position { offset: self.offset, line: self.line, column: self.column }
41 }
42
43 fn peek(&self) -> Option<u8> {
44 self.bytes.get(self.offset).copied()
45 }
46
47 fn advance(&mut self) {
48 if let Some(byte) = self.peek() {
49 if byte == b'\n'{
50 self.column = 1;
51 self.line += 1;
52 } else {
53 self.column += 1;
54 }
55 self.offset += 1;
56 }
57 }
58
59 fn skip_whitespace(&mut self) {
60 loop {
61 match self.peek() {
62 Some(b' ' | b'\t' | b'\n' | b'\r') => self.advance(),
63 Some(b'/') if self.bytes.get(self.offset + 1) == Some(&b'/') => {
64 while self.peek().is_some_and(|b| b != b'\n') {
65 self.advance();
66 }
67 }
68 _ => break,
69 }
70 }
71 }
72
73 fn expect_char(&mut self, expected: u8) -> Result<(), RonParseError> {
74 let start = self.position();
75 match self.peek() {
76 Some(b) if b == expected => {
77 self.advance();
78 Ok(())
79 },
80 Some(b) => {
81 self.advance();
82 let end = self.position();
83 Err(RonParseError {
84 span: Span {
85 start,
86 end
87 },
88 kind: RonErrorKind::UnexpectedToken {
89 expected: format!("'{}'", expected as char),
90 found: format!("'{}'", b as char)
91 }
92 })
93 },
94 None => {
95 Err(RonParseError {
96 span: Span {
97 start,
98 end: start
99 },
100 kind: RonErrorKind::UnexpectedToken {
101 expected: format!("'{}'", expected as char),
102 found: "end of input".to_string()
103 }
104 })
105 }
106 }
107 }
108
109 fn parse_identifier(&mut self) -> Result<Spanned<String>, RonParseError> {
110 let start = self.position();
111
112 match self.peek() {
114 Some(b) if b.is_ascii_alphabetic() || b == b'_' => {},
115 Some(b) => {
116 self.advance();
117 let end = self.position();
118 return Err(RonParseError {
119 span: Span { start, end },
120 kind: RonErrorKind::UnexpectedToken {
121 expected: "identifier".to_string(),
122 found: format!("'{}'", b as char),
123 },
124 });
125 },
126 None => {
127 return Err(RonParseError {
128 span: Span { start, end: start },
129 kind: RonErrorKind::UnexpectedToken {
130 expected: "identifier".to_string(),
131 found: "end of input".to_string(),
132 },
133 });
134 },
135 }
136
137 while self.peek().is_some_and(|b| b.is_ascii_alphanumeric() || b == b'_') {
139 self.advance();
140 }
141
142 let end = self.position();
144 Ok(Spanned {
145 value: self.source[start.offset..end.offset].to_string(),
146 span: Span { start, end },
147 })
148 }
149
150 #[allow(clippy::too_many_lines)]
151 fn parse_value(&mut self) -> Result<Spanned<RonValue>, RonParseError> {
152 self.skip_whitespace();
153 let start = self.position();
154
155 match self.peek() {
156 Some(b'"') => {
157 self.advance(); let mut content = String::new();
159 loop {
160 match self.peek() {
161 Some(b'"') => {
162 self.advance(); break;
164 }
165 Some(b'\\') => {
169 self.advance(); match self.peek() {
171 Some(b'n') => { content.push('\n'); self.advance(); }
172 Some(b't') => { content.push('\t'); self.advance(); }
173 Some(b'\\') => { content.push('\\'); self.advance(); }
174 Some(b'"') => { content.push('"'); self.advance(); }
175 Some(b) => { content.push(b as char); self.advance(); }
176 None => {
177 return Err(RonParseError {
178 span: Span { start, end: self.position() },
179 kind: RonErrorKind::UnterminatedString,
180 });
181 }
182 }
183 }
184 Some(b) => {
185 content.push(b as char);
186 self.advance();
187 }
188 None => {
189 return Err(RonParseError {
190 span: Span { start, end: self.position() },
191 kind: RonErrorKind::UnterminatedString,
192 });
193 }
194 }
195 }
196 let end = self.position();
197 Ok(Spanned {
198 value: RonValue::String(content),
199 span: Span { start, end },
200 })
201 },
202 Some(b) if b.is_ascii_digit() || b == b'-' => {
203 if b == b'-' {
204 self.advance();
205 }
206
207 let mut has_dot = false;
208
209 loop {
210 match self.peek() {
211 Some(b) if b.is_ascii_digit() => {self.advance();},
212 Some(b'.') if !has_dot => {
213 has_dot = true;
214 self.advance();
215 },
216 Some(_) | None => {break;}
217 }
218 }
219
220 let end = self.position();
221 let number_str = &self.source[start.offset..end.offset];
222 if has_dot {
223 let num_float = number_str.parse::<f64>();
224 if let Ok(num) = num_float {
225 Ok(Spanned {
226 value: RonValue::Float(num),
227 span: Span { start, end },
228 })
229 } else {
230 Err(RonParseError {
231 span: Span { start, end },
232 kind: RonErrorKind::InvalidNumber { text: number_str.to_string() }
233 })
234 }
235 } else {
236 let num_int = number_str.parse::<i64>();
237 if let Ok(num) = num_int {
238 Ok(Spanned {
239 value: RonValue::Integer(num),
240 span: Span { start, end },
241 })
242 } else {
243 Err(RonParseError {
244 span: Span { start, end },
245 kind: RonErrorKind::InvalidNumber { text: number_str.to_string() }
246 })
247 }
248 }
249 },
250 Some(b) if b.is_ascii_alphabetic() => {
251 let identifier = self.parse_identifier()?;
252 let word = identifier.value.as_str();
253 let identifier_span = identifier.span;
254 match word {
255 "true" => {
256 Ok(Spanned { value: RonValue::Bool(true), span: identifier_span })
257 },
258 "false" => {
259 Ok(Spanned { value: RonValue::Bool(false), span: identifier_span })
260 }
261 "None" => {
262 Ok(Spanned { value: RonValue::Option(None), span: identifier_span })
263 }
264 "Some" => {
265 self.skip_whitespace();
266 self.expect_char(b'(')?;
267 let inner = self.parse_value()?;
268 self.expect_char(b')')?;
269 Ok(Spanned {
270 value: RonValue::Option(Some(Box::new(inner))),
271 span: Span { start, end: self.position() }
272 })
273 }
274 _ => {
275 self.skip_whitespace();
277 if self.peek() == Some(b'(') {
278 self.advance(); self.skip_whitespace();
280 let inner = self.parse_value()?;
281 self.skip_whitespace();
282 self.expect_char(b')')?;
283 Ok(Spanned {
284 value: RonValue::EnumVariant(word.to_string(), Box::new(inner)),
285 span: Span { start, end: self.position() },
286 })
287 } else {
288 Ok(Spanned {
289 value: RonValue::Identifier(word.to_string()),
290 span: identifier_span,
291 })
292 }
293 }
294 }
295 },
296 Some(b'[') => {
297 self.advance();
298 let mut elements = Vec::new();
299 loop {
300 self.skip_whitespace();
301 if let Some(b']') = self.peek() {
302 break;
303 }
304 let value = self.parse_value()?;
305 elements.push(value);
306 self.skip_whitespace();
307 if let Some(b',') = self.peek() {
308 self.advance();
309 }
310 }
311 self.expect_char(b']')?;
312 Ok(Spanned {
313 value: RonValue::List(elements),
314 span: Span { start, end: self.position() }
315 })
316 },
317 Some(b'{') => {
318 self.advance();
319 let mut entries: Vec<(Spanned<RonValue>, Spanned<RonValue>)> = Vec::new();
320 loop {
321 self.skip_whitespace();
322 if let Some(b'}') = self.peek() {
323 break;
324 }
325 let key = self.parse_value()?;
326 self.skip_whitespace();
327 self.expect_char(b':')?;
328 self.skip_whitespace();
329 let value = self.parse_value()?;
330 entries.push((key, value));
331 self.skip_whitespace();
332 if let Some(b',') = self.peek() {
333 self.advance();
334 }
335 }
336 self.expect_char(b'}')?;
337 Ok(Spanned {
338 value: RonValue::Map(entries),
339 span: Span { start, end: self.position() },
340 })
341 },
342 Some(b'(') => {
343 self.advance();
344 self.skip_whitespace();
345
346 if self.peek() == Some(b')') {
348 let close_span_start = self.position();
349 self.expect_char(b')')?;
350 let close_span = Span { start: close_span_start, end: self.position() };
351 return Ok(Spanned {
352 value: RonValue::Struct(RonStruct { fields: Vec::new(), close_span }),
353 span: Span { start, end: self.position() },
354 });
355 }
356
357 let probe = (self.offset, self.line, self.column);
359 let is_struct = if let Ok(_id) = self.parse_identifier() {
360 self.skip_whitespace();
361
362 self.peek() == Some(b':')
363 } else {
364 false
365 };
366 self.offset = probe.0;
368 self.line = probe.1;
369 self.column = probe.2;
370
371 if is_struct {
372 let mut fields: Vec<(Spanned<String>, Spanned<RonValue>)> = Vec::new();
374 loop {
375 self.skip_whitespace();
376 if let Some(b')') = self.peek() {
377 break;
378 }
379 let field = self.parse_identifier()?;
380 self.skip_whitespace();
381 self.expect_char(b':')?;
382 self.skip_whitespace();
383 let value = self.parse_value()?;
384 fields.push((field, value));
385 self.skip_whitespace();
386 match self.peek() {
387 Some(b',') => self.advance(),
388 Some(_) => {}
389 None => {
390 return Err(RonParseError {
391 span: Span { start, end: self.position() },
392 kind: RonErrorKind::UnexpectedToken {
393 expected: "character".to_string(),
394 found: "end of file".to_string(),
395 },
396 });
397 }
398 }
399 }
400 let close_span_start = self.position();
401 self.expect_char(b')')?;
402 let close_span = Span { start: close_span_start, end: self.position() };
403 Ok(Spanned {
404 value: RonValue::Struct(RonStruct { fields, close_span }),
405 span: Span { start, end: self.position() },
406 })
407 } else {
408 let mut elements = Vec::new();
410 loop {
411 self.skip_whitespace();
412 if self.peek() == Some(b')') {
413 break;
414 }
415 let value = self.parse_value()?;
416 elements.push(value);
417 self.skip_whitespace();
418 if self.peek() == Some(b',') {
419 self.advance();
420 }
421 }
422 self.expect_char(b')')?;
423 Ok(Spanned {
424 value: RonValue::Tuple(elements),
425 span: Span { start, end: self.position() },
426 })
427 }
428 }
429 Some(b) => {
430 self.advance();
431 let end = self.position();
432 Err(RonParseError {
433 span: Span { start, end },
434 kind: RonErrorKind::UnexpectedToken {
435 expected: "value".to_string(),
436 found: format!("{}", b as char)
437 }
438 })
439 },
440 None => {
441 Err(RonParseError {
442 span: Span { start, end: start },
443 kind: RonErrorKind::UnexpectedToken {
444 expected: "value".to_string(),
445 found: "end of file".to_string()
446 }
447 })
448 }
449 }
450 }
451}
452
453pub fn parse_ron(source: &str) -> Result<Spanned<RonValue>, RonParseError> {
459 let mut parser = Parser::new(source);
460 parser.parse_value()
461}
462
463#[cfg(test)]
464mod tests {
465 use super::*;
466
467 fn parser(source: &str) -> Parser<'_> {
468 Parser::new(source)
469 }
470
471 #[test]
477 fn string_simple() {
478 let mut p = parser("\"hello\"");
479 let v = p.parse_value().unwrap();
480 assert_eq!(v.value, RonValue::String("hello".to_string()));
481 }
482
483 #[test]
485 fn string_empty() {
486 let mut p = parser("\"\"");
487 let v = p.parse_value().unwrap();
488 assert_eq!(v.value, RonValue::String("".to_string()));
489 }
490
491 #[test]
493 fn string_with_spaces() {
494 let mut p = parser("\"Ashborn Hound\"");
495 let v = p.parse_value().unwrap();
496 assert_eq!(v.value, RonValue::String("Ashborn Hound".to_string()));
497 }
498
499 #[test]
501 fn string_escaped_quote() {
502 let mut p = parser("\"say \\\"hi\\\"\"");
503 let v = p.parse_value().unwrap();
504 assert_eq!(v.value, RonValue::String("say \"hi\"".to_string()));
505 }
506
507 #[test]
509 fn string_escaped_backslash() {
510 let mut p = parser("\"a\\\\b\"");
511 let v = p.parse_value().unwrap();
512 assert_eq!(v.value, RonValue::String("a\\b".to_string()));
513 }
514
515 #[test]
517 fn string_escaped_newline() {
518 let mut p = parser("\"line1\\nline2\"");
519 let v = p.parse_value().unwrap();
520 assert_eq!(v.value, RonValue::String("line1\nline2".to_string()));
521 }
522
523 #[test]
525 fn string_escaped_tab() {
526 let mut p = parser("\"col1\\tcol2\"");
527 let v = p.parse_value().unwrap();
528 assert_eq!(v.value, RonValue::String("col1\tcol2".to_string()));
529 }
530
531 #[test]
533 fn string_unterminated() {
534 let mut p = parser("\"hello");
535 let err = p.parse_value().unwrap_err();
536 assert_eq!(err.kind, RonErrorKind::UnterminatedString);
537 }
538
539 #[test]
545 fn integer_positive() {
546 let mut p = parser("42");
547 let v = p.parse_value().unwrap();
548 assert_eq!(v.value, RonValue::Integer(42));
549 }
550
551 #[test]
553 fn integer_zero() {
554 let mut p = parser("0");
555 let v = p.parse_value().unwrap();
556 assert_eq!(v.value, RonValue::Integer(0));
557 }
558
559 #[test]
561 fn integer_negative() {
562 let mut p = parser("-7");
563 let v = p.parse_value().unwrap();
564 assert_eq!(v.value, RonValue::Integer(-7));
565 }
566
567 #[test]
573 fn float_simple() {
574 let mut p = parser("3.14");
575 let v = p.parse_value().unwrap();
576 assert_eq!(v.value, RonValue::Float(3.14));
577 }
578
579 #[test]
581 fn float_negative() {
582 let mut p = parser("-0.5");
583 let v = p.parse_value().unwrap();
584 assert_eq!(v.value, RonValue::Float(-0.5));
585 }
586
587 #[test]
589 fn float_one_point_zero() {
590 let mut p = parser("1.0");
591 let v = p.parse_value().unwrap();
592 assert_eq!(v.value, RonValue::Float(1.0));
593 }
594
595 #[test]
601 fn bool_true() {
602 let mut p = parser("true");
603 let v = p.parse_value().unwrap();
604 assert_eq!(v.value, RonValue::Bool(true));
605 }
606
607 #[test]
609 fn bool_false() {
610 let mut p = parser("false");
611 let v = p.parse_value().unwrap();
612 assert_eq!(v.value, RonValue::Bool(false));
613 }
614
615 #[test]
621 fn option_none() {
622 let mut p = parser("None");
623 let v = p.parse_value().unwrap();
624 assert_eq!(v.value, RonValue::Option(None));
625 }
626
627 #[test]
629 fn option_some_integer() {
630 let mut p = parser("Some(5)");
631 let v = p.parse_value().unwrap();
632 if let RonValue::Option(Some(inner)) = &v.value {
633 assert_eq!(inner.value, RonValue::Integer(5));
634 } else {
635 panic!("expected Option(Some(...))");
636 }
637 }
638
639 #[test]
641 fn option_some_string() {
642 let mut p = parser("Some(\"hi\")");
643 let v = p.parse_value().unwrap();
644 if let RonValue::Option(Some(inner)) = &v.value {
645 assert_eq!(inner.value, RonValue::String("hi".to_string()));
646 } else {
647 panic!("expected Option(Some(...))");
648 }
649 }
650
651 #[test]
657 fn identifier_bare() {
658 let mut p = parser("Creature");
659 let v = p.parse_value().unwrap();
660 assert_eq!(v.value, RonValue::Identifier("Creature".to_string()));
661 }
662
663 #[test]
665 fn identifier_another() {
666 let mut p = parser("Sentinels");
667 let v = p.parse_value().unwrap();
668 assert_eq!(v.value, RonValue::Identifier("Sentinels".to_string()));
669 }
670
671 #[test]
677 fn list_empty() {
678 let mut p = parser("[]");
679 let v = p.parse_value().unwrap();
680 if let RonValue::List(elems) = &v.value {
681 assert!(elems.is_empty());
682 } else {
683 panic!("expected List");
684 }
685 }
686
687 #[test]
689 fn list_single_element() {
690 let mut p = parser("[Creature]");
691 let v = p.parse_value().unwrap();
692 if let RonValue::List(elems) = &v.value {
693 assert_eq!(elems.len(), 1);
694 assert_eq!(elems[0].value, RonValue::Identifier("Creature".to_string()));
695 } else {
696 panic!("expected List");
697 }
698 }
699
700 #[test]
702 fn list_multiple_elements() {
703 let mut p = parser("[Creature, Trap, Artifact]");
704 let v = p.parse_value().unwrap();
705 if let RonValue::List(elems) = &v.value {
706 assert_eq!(elems.len(), 3);
707 } else {
708 panic!("expected List");
709 }
710 }
711
712 #[test]
714 fn list_trailing_comma() {
715 let mut p = parser("[Creature, Trap,]");
716 let v = p.parse_value().unwrap();
717 if let RonValue::List(elems) = &v.value {
718 assert_eq!(elems.len(), 2);
719 } else {
720 panic!("expected List");
721 }
722 }
723
724 #[test]
726 fn list_of_strings() {
727 let mut p = parser("[\"Vigilance\", \"Haste\"]");
728 let v = p.parse_value().unwrap();
729 if let RonValue::List(elems) = &v.value {
730 assert_eq!(elems.len(), 2);
731 assert_eq!(elems[0].value, RonValue::String("Vigilance".to_string()));
732 assert_eq!(elems[1].value, RonValue::String("Haste".to_string()));
733 } else {
734 panic!("expected List");
735 }
736 }
737
738 #[test]
744 fn struct_empty() {
745 let mut p = parser("()");
746 let v = p.parse_value().unwrap();
747 if let RonValue::Struct(s) = &v.value {
748 assert!(s.fields.is_empty());
749 } else {
750 panic!("expected Struct");
751 }
752 }
753
754 #[test]
756 fn struct_single_field() {
757 let mut p = parser("(name: \"Ashborn Hound\")");
758 let v = p.parse_value().unwrap();
759 if let RonValue::Struct(s) = &v.value {
760 assert_eq!(s.fields.len(), 1);
761 assert_eq!(s.fields[0].0.value, "name");
762 assert_eq!(s.fields[0].1.value, RonValue::String("Ashborn Hound".to_string()));
763 } else {
764 panic!("expected Struct");
765 }
766 }
767
768 #[test]
770 fn struct_multiple_fields() {
771 let mut p = parser("(name: \"foo\", age: 5)");
772 let v = p.parse_value().unwrap();
773 if let RonValue::Struct(s) = &v.value {
774 assert_eq!(s.fields.len(), 2);
775 } else {
776 panic!("expected Struct");
777 }
778 }
779
780 #[test]
782 fn struct_trailing_comma() {
783 let mut p = parser("(name: \"foo\",)");
784 let v = p.parse_value().unwrap();
785 if let RonValue::Struct(s) = &v.value {
786 assert_eq!(s.fields.len(), 1);
787 } else {
788 panic!("expected Struct");
789 }
790 }
791
792 #[test]
794 fn struct_close_span_captured() {
795 let mut p = parser("(x: 1)");
796 let v = p.parse_value().unwrap();
797 if let RonValue::Struct(s) = &v.value {
798 assert_eq!(s.close_span.start.offset, 5);
799 assert_eq!(s.close_span.end.offset, 6);
800 } else {
801 panic!("expected Struct");
802 }
803 }
804
805 #[test]
807 fn struct_nested() {
808 let mut p = parser("(cost: (generic: 2, sigil: 1))");
809 let v = p.parse_value().unwrap();
810 if let RonValue::Struct(s) = &v.value {
811 assert_eq!(s.fields.len(), 1);
812 assert_eq!(s.fields[0].0.value, "cost");
813 if let RonValue::Struct(inner) = &s.fields[0].1.value {
814 assert_eq!(inner.fields.len(), 2);
815 } else {
816 panic!("expected nested Struct");
817 }
818 } else {
819 panic!("expected Struct");
820 }
821 }
822
823 #[test]
829 fn whitespace_leading() {
830 let mut p = parser(" 42");
831 let v = p.parse_value().unwrap();
832 assert_eq!(v.value, RonValue::Integer(42));
833 }
834
835 #[test]
837 fn comment_before_value() {
838 let mut p = parser("// comment\n42");
839 let v = p.parse_value().unwrap();
840 assert_eq!(v.value, RonValue::Integer(42));
841 }
842
843 #[test]
849 fn span_starts_after_whitespace() {
850 let mut p = parser(" 42");
851 let v = p.parse_value().unwrap();
852 assert_eq!(v.span.start.offset, 2);
853 }
854
855 #[test]
857 fn span_covers_string() {
858 let mut p = parser("\"hello\"");
859 let v = p.parse_value().unwrap();
860 assert_eq!(v.span.start.offset, 0);
861 assert_eq!(v.span.end.offset, 7);
862 }
863
864 #[test]
870 fn error_empty_input() {
871 let mut p = parser("");
872 let err = p.parse_value().unwrap_err();
873 match err.kind {
874 RonErrorKind::UnexpectedToken { found, .. } => {
875 assert_eq!(found, "end of file");
876 }
877 other => panic!("expected UnexpectedToken, got {:?}", other),
878 }
879 }
880
881 #[test]
883 fn error_unexpected_char() {
884 let mut p = parser("@");
885 assert!(p.parse_value().is_err());
886 }
887
888 #[test]
894 fn ron_full_struct() {
895 let source = r#"(
896 name: "Ashborn Hound",
897 card_types: [Creature],
898 legendary: false,
899 power: Some(1),
900 toughness: None,
901 keywords: [],
902 flavor_text: "placeholder",
903 )"#;
904 let v = parse_ron(source).unwrap();
905 if let RonValue::Struct(s) = &v.value {
906 assert_eq!(s.fields.len(), 7);
907 assert_eq!(s.fields[0].0.value, "name");
908 assert_eq!(s.fields[0].1.value, RonValue::String("Ashborn Hound".to_string()));
909 } else {
910 panic!("expected Struct");
911 }
912 }
913
914 #[test]
920 fn map_empty() {
921 let mut p = parser("{}");
922 let v = p.parse_value().unwrap();
923 if let RonValue::Map(entries) = &v.value {
924 assert!(entries.is_empty());
925 } else {
926 panic!("expected Map");
927 }
928 }
929
930 #[test]
932 fn map_string_keys() {
933 let mut p = parser("{\"str\": 5, \"dex\": 3}");
934 let v = p.parse_value().unwrap();
935 if let RonValue::Map(entries) = &v.value {
936 assert_eq!(entries.len(), 2);
937 assert_eq!(entries[0].0.value, RonValue::String("str".to_string()));
938 assert_eq!(entries[0].1.value, RonValue::Integer(5));
939 } else {
940 panic!("expected Map");
941 }
942 }
943
944 #[test]
946 fn map_integer_keys() {
947 let mut p = parser("{1: \"one\", 2: \"two\"}");
948 let v = p.parse_value().unwrap();
949 if let RonValue::Map(entries) = &v.value {
950 assert_eq!(entries.len(), 2);
951 assert_eq!(entries[0].0.value, RonValue::Integer(1));
952 } else {
953 panic!("expected Map");
954 }
955 }
956
957 #[test]
959 fn map_trailing_comma() {
960 let mut p = parser("{\"a\": 1,}");
961 let v = p.parse_value().unwrap();
962 if let RonValue::Map(entries) = &v.value {
963 assert_eq!(entries.len(), 1);
964 } else {
965 panic!("expected Map");
966 }
967 }
968
969 #[test]
975 fn tuple_two_elements() {
976 let mut p = parser("(1.0, 2.5)");
977 let v = p.parse_value().unwrap();
978 if let RonValue::Tuple(elems) = &v.value {
979 assert_eq!(elems.len(), 2);
980 assert_eq!(elems[0].value, RonValue::Float(1.0));
981 assert_eq!(elems[1].value, RonValue::Float(2.5));
982 } else {
983 panic!("expected Tuple, got {:?}", v.value);
984 }
985 }
986
987 #[test]
989 fn tuple_mixed_types() {
990 let mut p = parser("(\"hello\", 42, true)");
991 let v = p.parse_value().unwrap();
992 if let RonValue::Tuple(elems) = &v.value {
993 assert_eq!(elems.len(), 3);
994 assert_eq!(elems[0].value, RonValue::String("hello".to_string()));
995 assert_eq!(elems[1].value, RonValue::Integer(42));
996 assert_eq!(elems[2].value, RonValue::Bool(true));
997 } else {
998 panic!("expected Tuple, got {:?}", v.value);
999 }
1000 }
1001
1002 #[test]
1004 fn struct_still_parses() {
1005 let mut p = parser("(name: \"foo\", age: 5)");
1006 let v = p.parse_value().unwrap();
1007 if let RonValue::Struct(s) = &v.value {
1008 assert_eq!(s.fields.len(), 2);
1009 } else {
1010 panic!("expected Struct, got {:?}", v.value);
1011 }
1012 }
1013
1014 #[test]
1016 fn empty_parens_is_struct() {
1017 let mut p = parser("()");
1018 let v = p.parse_value().unwrap();
1019 assert!(matches!(v.value, RonValue::Struct(_)));
1020 }
1021
1022 #[test]
1024 fn tuple_single_element_trailing_comma() {
1025 let mut p = parser("(42,)");
1026 let v = p.parse_value().unwrap();
1027 if let RonValue::Tuple(elems) = &v.value {
1028 assert_eq!(elems.len(), 1);
1029 } else {
1030 panic!("expected Tuple, got {:?}", v.value);
1031 }
1032 }
1033
1034 #[test]
1040 fn enum_variant_with_integer_data() {
1041 let mut p = parser("Damage(5)");
1042 let v = p.parse_value().unwrap();
1043 if let RonValue::EnumVariant(name, data) = &v.value {
1044 assert_eq!(name, "Damage");
1045 assert_eq!(data.value, RonValue::Integer(5));
1046 } else {
1047 panic!("expected EnumVariant, got {:?}", v.value);
1048 }
1049 }
1050
1051 #[test]
1053 fn enum_variant_with_string_data() {
1054 let mut p = parser("Message(\"hello\")");
1055 let v = p.parse_value().unwrap();
1056 if let RonValue::EnumVariant(name, data) = &v.value {
1057 assert_eq!(name, "Message");
1058 assert_eq!(data.value, RonValue::String("hello".to_string()));
1059 } else {
1060 panic!("expected EnumVariant, got {:?}", v.value);
1061 }
1062 }
1063
1064 #[test]
1066 fn bare_identifier_unchanged() {
1067 let mut p = parser("Creature,");
1068 let v = p.parse_value().unwrap();
1069 assert_eq!(v.value, RonValue::Identifier("Creature".to_string()));
1070 }
1071
1072 #[test]
1074 fn some_not_enum_variant() {
1075 let mut p = parser("Some(5)");
1076 let v = p.parse_value().unwrap();
1077 assert!(matches!(v.value, RonValue::Option(Some(_))));
1078 }
1079}