1use crate::span::{Position, Span, Spanned};
6use crate::error::{SchemaParseError, SchemaErrorKind};
7use crate::ron::RonValue;
8use crate::ron::parser::Parser as RonParser;
9use super::{SchemaType, FieldDef, StructDef, EnumDef, HashSet, Schema, HashMap};
10
11#[derive(Debug)]
12struct Parser<'a> {
13 source: &'a str,
14 bytes: &'a [u8],
15 offset: usize,
16 line: usize,
17 column: usize,
18}
19
20impl<'a> Parser<'a> {
21 fn new(source: &'a str) -> Self {
22 Self { source, bytes: source.as_bytes(), offset: 0, line: 1, column: 1 }
23 }
24
25 fn position(&self) -> Position {
26 Position { offset: self.offset, line: self.line, column: self.column }
27 }
28
29 fn peek(&self) -> Option<u8> {
30 self.bytes.get(self.offset).copied()
31 }
32
33 fn advance(&mut self) {
34 if let Some(byte) = self.peek() {
35 if byte == b'\n'{
36 self.column = 1;
37 self.line += 1;
38 } else {
39 self.column += 1;
40 }
41 self.offset += 1;
42 }
43 }
44
45 fn skip_whitespace(&mut self) {
46 loop {
47 match self.peek() {
48 Some(b' ' | b'\t' | b'\n' | b'\r') => self.advance(),
49 Some(b'/') if self.bytes.get(self.offset + 1) == Some(&b'/') => {
50 while self.peek().is_some_and(|b| b != b'\n') {
51 self.advance();
52 }
53 }
54 _ => break,
55 }
56 }
57 }
58
59 fn expect_char(&mut self, expected: u8) -> Result<(), SchemaParseError> {
60 let start = self.position();
61 match self.peek() {
62 Some(b) if b == expected => {
63 self.advance();
64 Ok(())
65 },
66 Some(b) => {
67 self.advance();
68 let end = self.position();
69 Err(SchemaParseError {
70 span: Span {
71 start,
72 end
73 },
74 kind: SchemaErrorKind::UnexpectedToken {
75 expected: format!("'{}'", expected as char),
76 found: format!("'{}'", b as char)
77 }
78 })
79 },
80 None => {
81 Err(SchemaParseError {
82 span: Span {
83 start,
84 end: start
85 },
86 kind: SchemaErrorKind::UnexpectedToken {
87 expected: format!("'{}'", expected as char),
88 found: "end of input".to_string()
89 }
90 })
91 }
92 }
93 }
94
95 fn parse_identifier(&mut self) -> Result<Spanned<String>, SchemaParseError> {
96 let start = self.position();
97
98 match self.peek() {
100 Some(b) if b.is_ascii_alphabetic() || b == b'_' => {},
101 Some(b) => {
102 self.advance();
103 let end = self.position();
104 return Err(SchemaParseError {
105 span: Span { start, end },
106 kind: SchemaErrorKind::UnexpectedToken {
107 expected: "identifier".to_string(),
108 found: format!("'{}'", b as char),
109 },
110 });
111 },
112 None => {
113 return Err(SchemaParseError {
114 span: Span { start, end: start },
115 kind: SchemaErrorKind::UnexpectedToken {
116 expected: "identifier".to_string(),
117 found: "end of input".to_string(),
118 },
119 });
120 },
121 }
122
123 while self.peek().is_some_and(|b| b.is_ascii_alphanumeric() || b == b'_') {
125 self.advance();
126 }
127
128 let end = self.position();
130 Ok(Spanned {
131 value: self.source[start.offset..end.offset].to_string(),
132 span: Span { start, end },
133 })
134 }
135
136 #[allow(clippy::too_many_lines)]
137 fn parse_type(&mut self) -> Result<Spanned<SchemaType>, SchemaParseError> {
138 self.skip_whitespace();
139 let start = self.position();
140
141 match self.peek() {
142 Some(b'[') => {
143 self.advance();
145 self.skip_whitespace();
146 let inner = self.parse_type()?;
147 self.skip_whitespace();
148 self.expect_char(b']')?;
149 let end = self.position();
150 Ok(Spanned {
151 value: SchemaType::List(Box::new(inner.value)),
152 span: Span { start, end },
153 })
154 }
155 Some(b'{') => {
156 self.advance();
158 self.skip_whitespace();
159 let key_type = self.parse_type()?;
160 match &key_type.value {
162 SchemaType::String | SchemaType::Integer | SchemaType::EnumRef(_) => {}
163 _ => {
164 return Err(SchemaParseError {
165 span: key_type.span,
166 kind: SchemaErrorKind::InvalidMapKeyType {
167 found: format!("{:?}", key_type.value),
168 },
169 });
170 }
171 }
172 self.skip_whitespace();
173 self.expect_char(b':')?;
174 self.skip_whitespace();
175 let value_type = self.parse_type()?;
176 self.skip_whitespace();
177 self.expect_char(b'}')?;
178 let end = self.position();
179 Ok(Spanned {
180 value: SchemaType::Map(Box::new(key_type.value), Box::new(value_type.value)),
181 span: Span { start, end },
182 })
183 }
184 Some(b'(') => {
185 let saved = (self.offset, self.line, self.column);
190 self.advance(); self.skip_whitespace();
192
193 let is_struct = if self.peek() == Some(b')') {
194 true } else {
196 let probe_pos = (self.offset, self.line, self.column);
198 let is_field = if let Ok(_id) = self.parse_identifier() {
199 self.skip_whitespace();
200
201 self.peek() == Some(b':')
202 } else {
203 false
204 };
205 self.offset = probe_pos.0;
207 self.line = probe_pos.1;
208 self.column = probe_pos.2;
209 is_field
210 };
211
212 self.offset = saved.0;
214 self.line = saved.1;
215 self.column = saved.2;
216
217 if is_struct {
218 let struct_def = self.parse_struct()?;
219 let end = self.position();
220 Ok(Spanned {
221 value: SchemaType::Struct(struct_def),
222 span: Span { start, end },
223 })
224 } else {
225 let types = self.parse_tuple_type()?;
226 let end = self.position();
227 Ok(Spanned {
228 value: SchemaType::Tuple(types),
229 span: Span { start, end },
230 })
231 }
232 }
233 Some(b) if b.is_ascii_alphabetic() => {
234 let id = self.parse_identifier()?;
236 match id.value.as_str() {
237 "String" => Ok(Spanned { value: SchemaType::String, span: id.span }),
238 "Integer" => Ok(Spanned { value: SchemaType::Integer, span: id.span }),
239 "Float" => Ok(Spanned { value: SchemaType::Float, span: id.span }),
240 "Bool" => Ok(Spanned { value: SchemaType::Bool, span: id.span }),
241 "Option" => {
242 self.skip_whitespace();
244 self.expect_char(b'(')?;
245 self.skip_whitespace();
246 let inner = self.parse_type()?;
247 self.skip_whitespace();
248 self.expect_char(b')')?;
249 let end = self.position();
250 Ok(Spanned {
251 value: SchemaType::Option(Box::new(inner.value)),
252 span: Span { start, end },
253 })
254 }
255 _ => Ok(Spanned { value: SchemaType::EnumRef(id.value), span: id.span }),
256 }
257 }
258 Some(b) => {
259 self.advance();
261 let end = self.position();
262 Err(SchemaParseError {
263 span: Span { start, end },
264 kind: SchemaErrorKind::UnexpectedToken {
265 expected: "type".to_string(),
266 found: format!("'{}'", b as char),
267 },
268 })
269 }
270 None => {
271 Err(SchemaParseError {
272 span: Span { start, end: start },
273 kind: SchemaErrorKind::UnexpectedToken {
274 expected: "type".to_string(),
275 found: "end of input".to_string(),
276 },
277 })
278 }
279 }
280 }
281
282 fn parse_field(&mut self) -> Result<FieldDef, SchemaParseError> {
283 self.skip_whitespace();
284 let name = self.parse_identifier()?;
285 self.skip_whitespace();
286 self.expect_char(b':')?;
287 self.skip_whitespace();
288 let type_ = self.parse_type()?;
289 self.skip_whitespace();
290
291 let default = if self.peek() == Some(b'=') {
293 self.advance(); self.skip_whitespace();
295 let mut ron_parser = RonParser::new_at(
296 self.source,
297 self.offset,
298 self.position(),
299 );
300 let value = ron_parser.parse_single_value().map_err(|e| {
301 SchemaParseError {
302 span: e.span,
303 kind: SchemaErrorKind::UnexpectedToken {
304 expected: "default value".to_string(),
305 found: format!("{:?}", e.kind),
306 },
307 }
308 })?;
309 let bytes_consumed = ron_parser.current_offset() - self.offset;
311 for _ in 0..bytes_consumed {
312 self.advance();
313 }
314 Some(value)
315 } else {
316 None
317 };
318
319 Ok(FieldDef{
320 name,
321 type_,
322 default,
323 })
324 }
325
326 fn parse_struct(&mut self) -> Result<StructDef, SchemaParseError> {
327 self.skip_whitespace();
328 self.expect_char(b'(')?;
329 let mut fields: Vec<FieldDef> = Vec::new();
330 loop {
331 self.skip_whitespace();
332 if let Some(byte) = self.peek() {
333 if byte == b')' {
334 break ;
335 }
336 let field = self.parse_field()?;
337 fields.push(field);
338 self.skip_whitespace();
339 if self.peek() == Some(b',') {
340 self.advance();
341 }
342 } else {
343 return Err(SchemaParseError {
344 span: Span { start: self.position(), end: self.position() },
345 kind: SchemaErrorKind::UnexpectedToken { expected: ")".to_string(), found: "end of file".to_string() }
346 });
347 }
348 }
349 self.expect_char(b')')?;
350 Ok(StructDef { fields })
351 }
352
353 fn parse_tuple_type(&mut self) -> Result<Vec<SchemaType>, SchemaParseError> {
355 self.skip_whitespace();
356 self.expect_char(b'(')?;
357 let mut types = Vec::new();
358 loop {
359 self.skip_whitespace();
360 if self.peek() == Some(b')') {
361 break;
362 }
363 let t = self.parse_type()?;
364 types.push(t.value);
365 self.skip_whitespace();
366 if self.peek() == Some(b',') {
367 self.advance();
368 }
369 }
370 self.expect_char(b')')?;
371 Ok(types)
372 }
373
374 fn parse_enum_def(&mut self) -> Result<EnumDef, SchemaParseError> {
375 self.skip_whitespace();
376 let keyword = self.parse_identifier()?;
377 if keyword.value != "enum" {
378 return Err(SchemaParseError {
379 span: keyword.span,
380 kind: SchemaErrorKind::UnexpectedToken {
381 expected: "\"enum\"".to_string(),
382 found: keyword.value,
383 },
384 });
385 }
386 self.skip_whitespace();
387 let name = self.parse_identifier()?;
388 self.skip_whitespace();
389 self.expect_char(b'{')?;
390 let mut variants = HashMap::new();
391 loop {
392 self.skip_whitespace();
393 if let Some(byte) = self.peek() {
394 if byte == b'}' {
395 break;
396 }
397 let variant = self.parse_identifier()?;
398 self.skip_whitespace();
400 let data_type = if self.peek() == Some(b'(') {
401 self.advance(); self.skip_whitespace();
403 let t = self.parse_type()?;
404 self.skip_whitespace();
405 self.expect_char(b')')?;
406 Some(t.value)
407 } else {
408 None
409 };
410 variants.insert(variant.value, data_type);
411 self.skip_whitespace();
412 if self.peek() == Some(b',') {
413 self.advance();
414 }
415 } else {
416 return Err(SchemaParseError {
417 span: Span { start: self.position(), end: self.position() },
418 kind: SchemaErrorKind::UnexpectedToken { expected: "}".to_string(), found: "end of file".to_string() }
419 });
420 }
421 }
422
423 self.expect_char(b'}')?;
424 Ok(EnumDef { name: name.value, variants })
425 }
426
427 fn parse_alias_def(&mut self) -> Result<(String, Spanned<SchemaType>), SchemaParseError> {
429 self.skip_whitespace();
430 self.parse_identifier()?; self.skip_whitespace();
432 let name = self.parse_identifier()?;
433 self.skip_whitespace();
434 self.expect_char(b'=')?;
435 self.skip_whitespace();
436 let type_ = self.parse_type()?;
437 Ok((name.value, type_))
438 }
439}
440
441pub fn parse_schema(source: &str) -> Result<Schema, SchemaParseError> {
448 let mut parser = Parser::new(source);
449 parser.skip_whitespace();
450
451 let mut root = if parser.peek() == Some(b'(') {
452 parser.parse_struct()?
453 } else {
454 StructDef { fields: Vec::new() }
455 };
456
457 let mut enums: HashMap<String, EnumDef> = HashMap::new();
458 let mut aliases: HashMap<String, Spanned<SchemaType>> = HashMap::new();
459
460 loop {
461 parser.skip_whitespace();
462 if parser.peek().is_none() {
463 break;
464 }
465
466 let start = parser.position();
468 let keyword = parser.parse_identifier()?;
469
470 match keyword.value.as_str() {
471 "enum" => {
472 parser.offset = start.offset;
474 parser.line = start.line;
475 parser.column = start.column;
476
477 let enum_def = parser.parse_enum_def()?;
478 if let Some(old) = enums.insert(enum_def.name.clone(), enum_def) {
479 return Err(SchemaParseError {
480 span: Span { start: parser.position(), end: parser.position() },
481 kind: SchemaErrorKind::DuplicateEnum { name: old.name },
482 });
483 }
484 }
485 "type" => {
486 parser.offset = start.offset;
488 parser.line = start.line;
489 parser.column = start.column;
490
491 let (name, type_) = parser.parse_alias_def()?;
492 if aliases.contains_key(&name) {
493 return Err(SchemaParseError {
494 span: type_.span,
495 kind: SchemaErrorKind::DuplicateAlias { name },
496 });
497 }
498 aliases.insert(name, type_);
499 }
500 other => {
501 return Err(SchemaParseError {
502 span: keyword.span,
503 kind: SchemaErrorKind::UnexpectedToken {
504 expected: "\"enum\" or \"type\"".to_string(),
505 found: other.to_string(),
506 },
507 });
508 }
509 }
510 }
511
512 let alias_names: HashSet<String> = aliases.keys().cloned().collect();
515 reclassify_refs_in_struct_by_name(&mut root, &alias_names);
516 for spanned_type in aliases.values_mut() {
517 reclassify_refs_in_type_by_name(&mut spanned_type.value, &alias_names);
518 }
519
520 verify_refs(&root, &enums, &aliases)?;
522
523 verify_no_recursive_aliases(&aliases)?;
525
526 verify_defaults(&root, &enums, &aliases)?;
528
529 Ok(Schema { root, enums, aliases })
530}
531
532fn reclassify_refs_in_struct_by_name(
535 struct_def: &mut StructDef,
536 alias_names: &HashSet<String>,
537) {
538 for field in &mut struct_def.fields {
539 reclassify_refs_in_type_by_name(&mut field.type_.value, alias_names);
540 }
541}
542
543fn reclassify_refs_in_type_by_name(
544 schema_type: &mut SchemaType,
545 alias_names: &HashSet<String>,
546) {
547 match schema_type {
548 SchemaType::EnumRef(name) if alias_names.contains(name.as_str()) => {
549 *schema_type = SchemaType::AliasRef(name.clone());
550 }
551 SchemaType::Option(inner) | SchemaType::List(inner) => {
552 reclassify_refs_in_type_by_name(inner, alias_names);
553 }
554 SchemaType::Map(key, value) => {
555 reclassify_refs_in_type_by_name(key, alias_names);
556 reclassify_refs_in_type_by_name(value, alias_names);
557 }
558 SchemaType::Tuple(types) => {
559 for t in types {
560 reclassify_refs_in_type_by_name(t, alias_names);
561 }
562 }
563 SchemaType::Struct(struct_def) => {
564 reclassify_refs_in_struct_by_name(struct_def, alias_names);
565 }
566 _ => {}
567 }
568}
569
570fn verify_refs(
573 struct_def: &StructDef,
574 enums: &HashMap<String, EnumDef>,
575 aliases: &HashMap<String, Spanned<SchemaType>>,
576) -> Result<(), SchemaParseError> {
577 for field in &struct_def.fields {
578 check_type_refs(&field.type_.value, field.type_.span, enums, aliases)?;
579 }
580 Ok(())
581}
582
583fn check_type_refs(
584 schema_type: &SchemaType,
585 span: Span,
586 enums: &HashMap<String, EnumDef>,
587 aliases: &HashMap<String, Spanned<SchemaType>>,
588) -> Result<(), SchemaParseError> {
589 match schema_type {
590 SchemaType::EnumRef(name) => {
591 if !enums.contains_key(name) {
592 return Err(SchemaParseError {
593 span,
594 kind: SchemaErrorKind::UnresolvedType { name: name.clone() },
595 });
596 }
597 }
598 SchemaType::AliasRef(name) => {
599 if !aliases.contains_key(name) {
600 return Err(SchemaParseError {
601 span,
602 kind: SchemaErrorKind::UnresolvedType { name: name.clone() },
603 });
604 }
605 }
606 SchemaType::Option(inner) | SchemaType::List(inner) => {
607 check_type_refs(inner, span, enums, aliases)?;
608 }
609 SchemaType::Map(key, value) => {
610 check_type_refs(key, span, enums, aliases)?;
611 check_type_refs(value, span, enums, aliases)?;
612 }
613 SchemaType::Tuple(types) => {
614 for t in types {
615 check_type_refs(t, span, enums, aliases)?;
616 }
617 }
618 SchemaType::Struct(struct_def) => {
619 verify_refs(struct_def, enums, aliases)?;
620 }
621 _ => {}
622 }
623 Ok(())
624}
625
626fn verify_no_recursive_aliases(
628 aliases: &HashMap<String, Spanned<SchemaType>>,
629) -> Result<(), SchemaParseError> {
630 for (name, spanned_type) in aliases {
631 let mut visited = HashSet::new();
632 visited.insert(name.as_str());
633 if let Some(cycle_name) = find_alias_cycle(&spanned_type.value, aliases, &mut visited) {
634 return Err(SchemaParseError {
635 span: spanned_type.span,
636 kind: SchemaErrorKind::RecursiveAlias { name: cycle_name },
637 });
638 }
639 }
640 Ok(())
641}
642
643fn find_alias_cycle<'a>(
644 schema_type: &'a SchemaType,
645 aliases: &'a HashMap<String, Spanned<SchemaType>>,
646 visited: &mut HashSet<&'a str>,
647) -> Option<String> {
648 match schema_type {
649 SchemaType::AliasRef(name) => {
650 if visited.contains(name.as_str()) {
651 return Some(name.clone());
652 }
653 visited.insert(name.as_str());
654 if let Some(target) = aliases.get(name) {
655 return find_alias_cycle(&target.value, aliases, visited);
656 }
657 None
658 }
659 SchemaType::Option(inner) | SchemaType::List(inner) => {
660 find_alias_cycle(inner, aliases, visited)
661 }
662 SchemaType::Map(key, value) => {
663 if let Some(cycle) = find_alias_cycle(key, aliases, visited) {
664 return Some(cycle);
665 }
666 find_alias_cycle(value, aliases, visited)
667 }
668 SchemaType::Tuple(types) => {
669 for t in types {
670 if let Some(cycle) = find_alias_cycle(t, aliases, visited) {
671 return Some(cycle);
672 }
673 }
674 None
675 }
676 SchemaType::Struct(struct_def) => {
677 for field in &struct_def.fields {
678 if let Some(cycle) = find_alias_cycle(&field.type_.value, aliases, visited) {
679 return Some(cycle);
680 }
681 }
682 None
683 }
684 _ => None,
685 }
686}
687
688fn resolve_type<'a>(
690 schema_type: &'a SchemaType,
691 aliases: &'a HashMap<String, Spanned<SchemaType>>,
692) -> &'a SchemaType {
693 match schema_type {
694 SchemaType::AliasRef(name) => {
695 if let Some(target) = aliases.get(name) {
696 resolve_type(&target.value, aliases)
697 } else {
698 schema_type
699 }
700 }
701 _ => schema_type,
702 }
703}
704
705#[allow(clippy::match_same_arms)]
708fn default_matches_type(
709 value: &RonValue,
710 schema_type: &SchemaType,
711 enums: &HashMap<String, EnumDef>,
712 aliases: &HashMap<String, Spanned<SchemaType>>,
713) -> bool {
714 let resolved = resolve_type(schema_type, aliases);
715 match (resolved, value) {
716 (SchemaType::String, RonValue::String(_)) => true,
717 (SchemaType::Integer, RonValue::Integer(_)) => true,
718 (SchemaType::Float, RonValue::Float(_)) => true,
719 (SchemaType::Bool, RonValue::Bool(_)) => true,
720 (SchemaType::Option(_), RonValue::Option(None)) => true,
721 (SchemaType::Option(inner), RonValue::Option(Some(inner_val))) => {
722 default_matches_type(&inner_val.value, inner, enums, aliases)
723 }
724 (SchemaType::List(elem_type), RonValue::List(elements)) => {
725 elements.iter().all(|e| default_matches_type(&e.value, elem_type, enums, aliases))
726 }
727 (SchemaType::EnumRef(enum_name), RonValue::Identifier(variant)) => {
728 enums.get(enum_name).is_some_and(|e| e.variants.contains_key(variant))
729 }
730 (SchemaType::EnumRef(enum_name), RonValue::EnumVariant(variant, data)) => {
731 enums.get(enum_name).is_some_and(|e| {
732 matches!(e.variants.get(variant), Some(Some(data_type)) if default_matches_type(&data.value, data_type, enums, aliases))
733 })
734 }
735 (SchemaType::Tuple(types), RonValue::Tuple(values)) => {
736 types.len() == values.len()
737 && types.iter().zip(values.iter()).all(|(t, v)| default_matches_type(&v.value, t, enums, aliases))
738 }
739 (SchemaType::Map(_, _), RonValue::Map(_)) => true, _ => false,
741 }
742}
743
744fn describe_type(schema_type: &SchemaType) -> String {
746 match schema_type {
747 SchemaType::String => "String".to_string(),
748 SchemaType::Integer => "Integer".to_string(),
749 SchemaType::Float => "Float".to_string(),
750 SchemaType::Bool => "Bool".to_string(),
751 SchemaType::Option(inner) => format!("Option({})", describe_type(inner)),
752 SchemaType::List(inner) => format!("[{}]", describe_type(inner)),
753 SchemaType::EnumRef(name) | SchemaType::AliasRef(name) => name.clone(),
754 SchemaType::Map(k, v) => format!("{{{}: {}}}", describe_type(k), describe_type(v)),
755 SchemaType::Tuple(types) => {
756 let inner: Vec<String> = types.iter().map(describe_type).collect();
757 format!("({})", inner.join(", "))
758 }
759 SchemaType::Struct(_) => "Struct".to_string(),
760 }
761}
762
763fn describe_value(value: &RonValue) -> String {
765 match value {
766 RonValue::String(s) => format!("String(\"{s}\")"),
767 RonValue::Integer(n) => format!("Integer({n})"),
768 RonValue::Float(f) => format!("Float({f})"),
769 RonValue::Bool(b) => format!("Bool({b})"),
770 RonValue::Option(None) => "None".to_string(),
771 RonValue::Option(Some(_)) => "Some(...)".to_string(),
772 RonValue::Identifier(s) => format!("Identifier({s})"),
773 RonValue::EnumVariant(name, _) => format!("{name}(...)"),
774 RonValue::List(_) => "List".to_string(),
775 RonValue::Map(_) => "Map".to_string(),
776 RonValue::Tuple(_) => "Tuple".to_string(),
777 RonValue::Struct(_) => "Struct".to_string(),
778 }
779}
780
781fn verify_defaults(
783 struct_def: &StructDef,
784 enums: &HashMap<String, EnumDef>,
785 aliases: &HashMap<String, Spanned<SchemaType>>,
786) -> Result<(), SchemaParseError> {
787 for field in &struct_def.fields {
788 if let Some(default) = &field.default {
789 if !default_matches_type(&default.value, &field.type_.value, enums, aliases) {
790 return Err(SchemaParseError {
791 span: default.span,
792 kind: SchemaErrorKind::InvalidDefault {
793 field_name: field.name.value.clone(),
794 expected: describe_type(&field.type_.value),
795 found: describe_value(&default.value),
796 },
797 });
798 }
799 }
800 }
801 for field in &struct_def.fields {
803 if let SchemaType::Struct(inner) = &field.type_.value {
804 verify_defaults(inner, enums, aliases)?;
805 }
806 }
807 Ok(())
808}
809
810#[cfg(test)]
811mod tests {
812 use super::*;
813 use crate::ron::RonValue;
814
815 fn parser(source: &str) -> Parser<'_> {
820 Parser::new(source)
821 }
822
823 #[test]
829 fn peek_returns_current_byte() {
830 let p = parser("abc");
831 assert_eq!(p.peek(), Some(b'a'));
832 }
833
834 #[test]
836 fn peek_returns_none_at_end() {
837 let p = parser("");
838 assert_eq!(p.peek(), None);
839 }
840
841 #[test]
847 fn advance_increments_offset_and_column() {
848 let mut p = parser("ab");
849 p.advance();
850 assert_eq!(p.offset, 1);
851 assert_eq!(p.column, 2);
852 assert_eq!(p.peek(), Some(b'b'));
853 }
854
855 #[test]
857 fn advance_past_newline_increments_line() {
858 let mut p = parser("a\nb");
859 p.advance(); p.advance(); assert_eq!(p.line, 2);
862 assert_eq!(p.column, 1);
863 }
864
865 #[test]
867 fn advance_at_end_is_noop() {
868 let mut p = parser("");
869 p.advance();
870 assert_eq!(p.offset, 0);
871 }
872
873 #[test]
879 fn position_initial_state() {
880 let p = parser("abc");
881 let pos = p.position();
882 assert_eq!(pos.offset, 0);
883 assert_eq!(pos.line, 1);
884 assert_eq!(pos.column, 1);
885 }
886
887 #[test]
889 fn position_after_advance() {
890 let mut p = parser("ab\nc");
891 p.advance(); p.advance(); p.advance(); let pos = p.position();
895 assert_eq!(pos.offset, 3);
896 assert_eq!(pos.line, 2);
897 assert_eq!(pos.column, 1);
898 }
899
900 #[test]
906 fn skip_whitespace_skips_spaces_tabs_newlines() {
907 let mut p = parser(" \t\nabc");
908 p.skip_whitespace();
909 assert_eq!(p.peek(), Some(b'a'));
910 }
911
912 #[test]
914 fn skip_whitespace_skips_line_comment() {
915 let mut p = parser("// comment\nabc");
916 p.skip_whitespace();
917 assert_eq!(p.peek(), Some(b'a'));
918 }
919
920 #[test]
922 fn skip_whitespace_skips_comment_then_whitespace() {
923 let mut p = parser("// comment\n abc");
924 p.skip_whitespace();
925 assert_eq!(p.peek(), Some(b'a'));
926 }
927
928 #[test]
930 fn skip_whitespace_noop_on_nonwhitespace() {
931 let mut p = parser("abc");
932 p.skip_whitespace();
933 assert_eq!(p.offset, 0);
934 }
935
936 #[test]
942 fn expect_char_consumes_matching_byte() {
943 let mut p = parser("(abc");
944 assert!(p.expect_char(b'(').is_ok());
945 assert_eq!(p.peek(), Some(b'a'));
946 }
947
948 #[test]
950 fn expect_char_error_on_mismatch() {
951 let mut p = parser("abc");
952 let err = p.expect_char(b'(').unwrap_err();
953 assert!(matches!(err.kind, SchemaErrorKind::UnexpectedToken { .. }));
954 }
955
956 #[test]
958 fn expect_char_error_at_end_of_input() {
959 let mut p = parser("");
960 let err = p.expect_char(b'(').unwrap_err();
961 match err.kind {
962 SchemaErrorKind::UnexpectedToken { found, .. } => {
963 assert_eq!(found, "end of input");
964 }
965 other => panic!("expected UnexpectedToken, got {:?}", other),
966 }
967 }
968
969 #[test]
975 fn parse_identifier_reads_alpha() {
976 let mut p = parser("name:");
977 let id = p.parse_identifier().unwrap();
978 assert_eq!(id.value, "name");
979 }
980
981 #[test]
983 fn parse_identifier_reads_snake_case() {
984 let mut p = parser("field_name:");
985 let id = p.parse_identifier().unwrap();
986 assert_eq!(id.value, "field_name");
987 }
988
989 #[test]
991 fn parse_identifier_reads_alphanumeric() {
992 let mut p = parser("cost2:");
993 let id = p.parse_identifier().unwrap();
994 assert_eq!(id.value, "cost2");
995 }
996
997 #[test]
999 fn parse_identifier_reads_pascal_case() {
1000 let mut p = parser("CardType ");
1001 let id = p.parse_identifier().unwrap();
1002 assert_eq!(id.value, "CardType");
1003 }
1004
1005 #[test]
1007 fn parse_identifier_stops_at_delimiter() {
1008 let mut p = parser("name: String");
1009 let id = p.parse_identifier().unwrap();
1010 assert_eq!(id.value, "name");
1011 assert_eq!(p.peek(), Some(b':'));
1012 }
1013
1014 #[test]
1016 fn parse_identifier_span_is_correct() {
1017 let mut p = parser("name:");
1018 let id = p.parse_identifier().unwrap();
1019 assert_eq!(id.span.start.offset, 0);
1020 assert_eq!(id.span.end.offset, 4);
1021 }
1022
1023 #[test]
1025 fn parse_identifier_error_on_digit_start() {
1026 let mut p = parser("42abc");
1027 assert!(p.parse_identifier().is_err());
1028 }
1029
1030 #[test]
1032 fn parse_identifier_error_at_end_of_input() {
1033 let mut p = parser("");
1034 assert!(p.parse_identifier().is_err());
1035 }
1036
1037 #[test]
1043 fn parse_type_string() {
1044 let mut p = parser("String");
1045 let t = p.parse_type().unwrap();
1046 assert_eq!(t.value, SchemaType::String);
1047 }
1048
1049 #[test]
1051 fn parse_type_integer() {
1052 let mut p = parser("Integer");
1053 let t = p.parse_type().unwrap();
1054 assert_eq!(t.value, SchemaType::Integer);
1055 }
1056
1057 #[test]
1059 fn parse_type_float() {
1060 let mut p = parser("Float");
1061 let t = p.parse_type().unwrap();
1062 assert_eq!(t.value, SchemaType::Float);
1063 }
1064
1065 #[test]
1067 fn parse_type_bool() {
1068 let mut p = parser("Bool");
1069 let t = p.parse_type().unwrap();
1070 assert_eq!(t.value, SchemaType::Bool);
1071 }
1072
1073 #[test]
1075 fn parse_type_list() {
1076 let mut p = parser("[String]");
1077 let t = p.parse_type().unwrap();
1078 assert_eq!(t.value, SchemaType::List(Box::new(SchemaType::String)));
1079 }
1080
1081 #[test]
1083 fn parse_type_option() {
1084 let mut p = parser("Option(Integer)");
1085 let t = p.parse_type().unwrap();
1086 assert_eq!(t.value, SchemaType::Option(Box::new(SchemaType::Integer)));
1087 }
1088
1089 #[test]
1091 fn parse_type_enum_ref() {
1092 let mut p = parser("Faction");
1093 let t = p.parse_type().unwrap();
1094 assert_eq!(t.value, SchemaType::EnumRef("Faction".to_string()));
1095 }
1096
1097 #[test]
1099 fn parse_type_nested_list_of_option() {
1100 let mut p = parser("[Option(String)]");
1101 let t = p.parse_type().unwrap();
1102 assert_eq!(
1103 t.value,
1104 SchemaType::List(Box::new(SchemaType::Option(Box::new(SchemaType::String))))
1105 );
1106 }
1107
1108 #[test]
1110 fn parse_type_inline_struct() {
1111 let mut p = parser("(\n x: Integer,\n)");
1112 let t = p.parse_type().unwrap();
1113 if let SchemaType::Struct(s) = &t.value {
1114 assert_eq!(s.fields.len(), 1);
1115 assert_eq!(s.fields[0].name.value, "x");
1116 } else {
1117 panic!("expected SchemaType::Struct");
1118 }
1119 }
1120
1121 #[test]
1123 fn parse_type_error_on_unexpected_token() {
1124 let mut p = parser("42");
1125 let err = p.parse_type().unwrap_err();
1126 match err.kind {
1127 SchemaErrorKind::UnexpectedToken { expected, .. } => {
1128 assert_eq!(expected, "type");
1129 }
1130 other => panic!("expected UnexpectedToken, got {:?}", other),
1131 }
1132 }
1133
1134 #[test]
1140 fn parse_field_name_and_type() {
1141 let mut p = parser("name: String,");
1142 let f = p.parse_field().unwrap();
1143 assert_eq!(f.name.value, "name");
1144 assert_eq!(f.type_.value, SchemaType::String);
1145 }
1146
1147 #[test]
1149 fn parse_field_error_missing_colon() {
1150 let mut p = parser("name String");
1151 let err = p.parse_field().unwrap_err();
1152 assert!(matches!(err.kind, SchemaErrorKind::UnexpectedToken { .. }));
1153 }
1154
1155 #[test]
1157 fn parse_field_no_default() {
1158 let mut p = parser("name: String,");
1159 let f = p.parse_field().unwrap();
1160 assert!(f.default.is_none());
1161 }
1162
1163 #[test]
1165 fn parse_field_default_string() {
1166 let mut p = parser("name: String = \"unnamed\",");
1167 let f = p.parse_field().unwrap();
1168 assert!(f.default.is_some());
1169 assert_eq!(f.default.unwrap().value, RonValue::String("unnamed".to_string()));
1170 }
1171
1172 #[test]
1174 fn parse_field_default_integer() {
1175 let mut p = parser("count: Integer = 0,");
1176 let f = p.parse_field().unwrap();
1177 assert_eq!(f.default.unwrap().value, RonValue::Integer(0));
1178 }
1179
1180 #[test]
1182 fn parse_field_default_float() {
1183 let mut p = parser("weight: Float = 1.0,");
1184 let f = p.parse_field().unwrap();
1185 assert_eq!(f.default.unwrap().value, RonValue::Float(1.0));
1186 }
1187
1188 #[test]
1190 fn parse_field_default_bool() {
1191 let mut p = parser("active: Bool = false,");
1192 let f = p.parse_field().unwrap();
1193 assert_eq!(f.default.unwrap().value, RonValue::Bool(false));
1194 }
1195
1196 #[test]
1198 fn parse_field_default_none() {
1199 let mut p = parser("label: Option(String) = None,");
1200 let f = p.parse_field().unwrap();
1201 assert_eq!(f.default.unwrap().value, RonValue::Option(None));
1202 }
1203
1204 #[test]
1206 fn parse_field_default_some() {
1207 let mut p = parser("label: Option(String) = Some(\"default\"),");
1208 let f = p.parse_field().unwrap();
1209 if let RonValue::Option(Some(inner)) = &f.default.unwrap().value {
1210 assert_eq!(inner.value, RonValue::String("default".to_string()));
1211 } else {
1212 panic!("expected Option(Some(...))");
1213 }
1214 }
1215
1216 #[test]
1218 fn parse_field_default_empty_list() {
1219 let mut p = parser("tags: [String] = [],");
1220 let f = p.parse_field().unwrap();
1221 assert_eq!(f.default.unwrap().value, RonValue::List(vec![]));
1222 }
1223
1224 #[test]
1226 fn parse_field_default_identifier() {
1227 let mut p = parser("status: Status = Active,");
1228 let f = p.parse_field().unwrap();
1229 assert_eq!(f.default.unwrap().value, RonValue::Identifier("Active".to_string()));
1230 }
1231
1232 #[test]
1234 fn parse_field_default_has_span() {
1235 let mut p = parser("name: String = \"hi\",");
1236 let f = p.parse_field().unwrap();
1237 let default = f.default.unwrap();
1238 assert!(default.span.start.column > 1);
1239 }
1240
1241 #[test]
1247 fn default_type_check_string_accepts_string() {
1248 let result = parse_schema("(\n name: String = \"hi\",\n)");
1249 assert!(result.is_ok());
1250 }
1251
1252 #[test]
1254 fn default_type_check_string_rejects_integer() {
1255 let err = parse_schema("(\n name: String = 42,\n)").unwrap_err();
1256 assert!(matches!(err.kind, SchemaErrorKind::InvalidDefault { field_name, .. } if field_name == "name"));
1257 }
1258
1259 #[test]
1261 fn default_type_check_integer_accepts_integer() {
1262 assert!(parse_schema("(\n count: Integer = 0,\n)").is_ok());
1263 }
1264
1265 #[test]
1267 fn default_type_check_integer_rejects_string() {
1268 let err = parse_schema("(\n count: Integer = \"zero\",\n)").unwrap_err();
1269 assert!(matches!(err.kind, SchemaErrorKind::InvalidDefault { .. }));
1270 }
1271
1272 #[test]
1274 fn default_type_check_float_accepts_float() {
1275 assert!(parse_schema("(\n weight: Float = 1.0,\n)").is_ok());
1276 }
1277
1278 #[test]
1280 fn default_type_check_float_rejects_integer() {
1281 let err = parse_schema("(\n weight: Float = 1,\n)").unwrap_err();
1282 assert!(matches!(err.kind, SchemaErrorKind::InvalidDefault { .. }));
1283 }
1284
1285 #[test]
1287 fn default_type_check_bool_accepts_bool() {
1288 assert!(parse_schema("(\n active: Bool = false,\n)").is_ok());
1289 }
1290
1291 #[test]
1293 fn default_type_check_bool_rejects_string() {
1294 let err = parse_schema("(\n active: Bool = \"false\",\n)").unwrap_err();
1295 assert!(matches!(err.kind, SchemaErrorKind::InvalidDefault { .. }));
1296 }
1297
1298 #[test]
1300 fn default_type_check_option_accepts_none() {
1301 assert!(parse_schema("(\n label: Option(String) = None,\n)").is_ok());
1302 }
1303
1304 #[test]
1306 fn default_type_check_option_accepts_some_correct() {
1307 assert!(parse_schema("(\n label: Option(String) = Some(\"hi\"),\n)").is_ok());
1308 }
1309
1310 #[test]
1312 fn default_type_check_option_rejects_some_wrong_type() {
1313 let err = parse_schema("(\n label: Option(String) = Some(42),\n)").unwrap_err();
1314 assert!(matches!(err.kind, SchemaErrorKind::InvalidDefault { .. }));
1315 }
1316
1317 #[test]
1319 fn default_type_check_list_accepts_empty() {
1320 assert!(parse_schema("(\n tags: [String] = [],\n)").is_ok());
1321 }
1322
1323 #[test]
1325 fn default_type_check_list_accepts_correct_elements() {
1326 assert!(parse_schema("(\n tags: [String] = [\"a\", \"b\"],\n)").is_ok());
1327 }
1328
1329 #[test]
1331 fn default_type_check_list_rejects_wrong_elements() {
1332 let err = parse_schema("(\n tags: [String] = [1, 2],\n)").unwrap_err();
1333 assert!(matches!(err.kind, SchemaErrorKind::InvalidDefault { .. }));
1334 }
1335
1336 #[test]
1338 fn default_type_check_enum_accepts_valid_variant() {
1339 assert!(parse_schema("(\n status: Status = Active,\n)\nenum Status { Active, Inactive }").is_ok());
1340 }
1341
1342 #[test]
1344 fn default_type_check_enum_rejects_invalid_variant() {
1345 let err = parse_schema("(\n status: Status = Unknown,\n)\nenum Status { Active, Inactive }").unwrap_err();
1346 assert!(matches!(err.kind, SchemaErrorKind::InvalidDefault { .. }));
1347 }
1348
1349 #[test]
1351 fn default_type_check_error_includes_expected() {
1352 let err = parse_schema("(\n name: String = 42,\n)").unwrap_err();
1353 if let SchemaErrorKind::InvalidDefault { expected, .. } = &err.kind {
1354 assert_eq!(expected, "String");
1355 } else {
1356 panic!("expected InvalidDefault");
1357 }
1358 }
1359
1360 #[test]
1362 fn default_type_check_error_includes_found() {
1363 let err = parse_schema("(\n name: String = 42,\n)").unwrap_err();
1364 if let SchemaErrorKind::InvalidDefault { found, .. } = &err.kind {
1365 assert!(found.contains("Integer"));
1366 } else {
1367 panic!("expected InvalidDefault");
1368 }
1369 }
1370
1371 #[test]
1373 fn default_type_check_error_has_span() {
1374 let err = parse_schema("(\n name: String = 42,\n)").unwrap_err();
1375 assert!(err.span.start.line > 0);
1376 }
1377
1378 #[test]
1380 fn default_type_check_alias_resolved() {
1381 assert!(parse_schema("(\n name: Name = \"hi\",\n)\ntype Name = String").is_ok());
1382 }
1383
1384 #[test]
1386 fn default_type_check_alias_rejects_wrong_type() {
1387 let err = parse_schema("(\n name: Name = 42,\n)\ntype Name = String").unwrap_err();
1388 assert!(matches!(err.kind, SchemaErrorKind::InvalidDefault { .. }));
1389 }
1390
1391 #[test]
1397 fn parse_struct_empty() {
1398 let mut p = parser("()");
1399 let s = p.parse_struct().unwrap();
1400 assert!(s.fields.is_empty());
1401 }
1402
1403 #[test]
1405 fn parse_struct_single_field() {
1406 let mut p = parser("(\n name: String,\n)");
1407 let s = p.parse_struct().unwrap();
1408 assert_eq!(s.fields.len(), 1);
1409 assert_eq!(s.fields[0].name.value, "name");
1410 }
1411
1412 #[test]
1414 fn parse_struct_multiple_fields() {
1415 let mut p = parser("(\n a: String,\n b: Integer,\n)");
1416 let s = p.parse_struct().unwrap();
1417 assert_eq!(s.fields.len(), 2);
1418 }
1419
1420 #[test]
1422 fn parse_struct_no_trailing_comma() {
1423 let mut p = parser("(\n name: String\n)");
1424 let s = p.parse_struct().unwrap();
1425 assert_eq!(s.fields.len(), 1);
1426 }
1427
1428 #[test]
1430 fn parse_struct_error_on_unclosed() {
1431 let mut p = parser("(\n name: String,\n");
1432 assert!(p.parse_struct().is_err());
1433 }
1434
1435 #[test]
1441 fn parse_enum_def_simple() {
1442 let mut p = parser("enum Dir { North, South }");
1443 let e = p.parse_enum_def().unwrap();
1444 assert_eq!(e.name, "Dir");
1445 assert_eq!(e.variants.len(), 2);
1446 assert!(e.variants.contains_key("North"));
1447 assert!(e.variants.contains_key("South"));
1448 }
1449
1450 #[test]
1452 fn parse_enum_def_trailing_comma() {
1453 let mut p = parser("enum Dir { North, South, }");
1454 let e = p.parse_enum_def().unwrap();
1455 assert_eq!(e.variants.len(), 2);
1456 }
1457
1458 #[test]
1460 fn parse_enum_def_single_variant() {
1461 let mut p = parser("enum Single { Only }");
1462 let e = p.parse_enum_def().unwrap();
1463 assert_eq!(e.variants.len(), 1);
1464 }
1465
1466 #[test]
1468 fn parse_enum_def_error_wrong_keyword() {
1469 let mut p = parser("struct Dir { North }");
1470 let err = p.parse_enum_def().unwrap_err();
1471 assert!(matches!(err.kind, SchemaErrorKind::UnexpectedToken { .. }));
1472 }
1473
1474 #[test]
1476 fn parse_enum_def_error_on_unclosed() {
1477 let mut p = parser("enum Dir { North, South");
1478 assert!(p.parse_enum_def().is_err());
1479 }
1480
1481 #[test]
1487 fn schema_empty_input() {
1488 let schema = parse_schema("").unwrap();
1489 assert!(schema.root.fields.is_empty());
1490 }
1491
1492 #[test]
1494 fn schema_empty_input_no_enums() {
1495 let schema = parse_schema("").unwrap();
1496 assert!(schema.enums.is_empty());
1497 }
1498
1499 #[test]
1501 fn schema_enum_ref_resolves() {
1502 let source = "(\n faction: Faction,\n)\nenum Faction { Sentinels, Reavers }";
1503 let schema = parse_schema(source).unwrap();
1504 assert_eq!(schema.root.fields[0].type_.value, SchemaType::EnumRef("Faction".to_string()));
1505 }
1506
1507 #[test]
1509 fn schema_multiple_enums_stored() {
1510 let source = "enum A { X }\nenum B { Y }";
1511 let schema = parse_schema(source).unwrap();
1512 assert_eq!(schema.enums.len(), 2);
1513 }
1514
1515 #[test]
1517 fn schema_comments_before_root() {
1518 let source = "// comment\n(\n name: String,\n)";
1519 let schema = parse_schema(source).unwrap();
1520 assert_eq!(schema.root.fields.len(), 1);
1521 }
1522
1523 #[test]
1525 fn schema_inline_comment_after_field() {
1526 let source = "(\n name: String, // a name\n)";
1527 let schema = parse_schema(source).unwrap();
1528 assert_eq!(schema.root.fields[0].name.value, "name");
1529 }
1530
1531 #[test]
1533 fn schema_unresolved_type_ref() {
1534 let err = parse_schema("(\n f: Faction,\n)").unwrap_err();
1535 assert_eq!(err.kind, SchemaErrorKind::UnresolvedType { name: "Faction".to_string() });
1536 }
1537
1538 #[test]
1540 fn schema_unresolved_type_ref_in_option() {
1541 let err = parse_schema("(\n t: Option(Timing),\n)").unwrap_err();
1542 assert_eq!(err.kind, SchemaErrorKind::UnresolvedType { name: "Timing".to_string() });
1543 }
1544
1545 #[test]
1547 fn schema_unresolved_type_ref_in_list() {
1548 let err = parse_schema("(\n t: [CardType],\n)").unwrap_err();
1549 assert_eq!(err.kind, SchemaErrorKind::UnresolvedType { name: "CardType".to_string() });
1550 }
1551
1552 #[test]
1554 fn schema_duplicate_enum_name() {
1555 let err = parse_schema("enum A { X }\nenum A { Y }").unwrap_err();
1556 assert_eq!(err.kind, SchemaErrorKind::DuplicateEnum { name: "A".to_string() });
1557 }
1558
1559 #[test]
1565 fn alias_stored_in_schema() {
1566 let source = "(\n cost: Cost,\n)\ntype Cost = (generic: Integer,)";
1567 let schema = parse_schema(source).unwrap();
1568 assert!(schema.aliases.contains_key("Cost"));
1569 }
1570
1571 #[test]
1573 fn alias_ref_reclassified() {
1574 let source = "(\n cost: Cost,\n)\ntype Cost = (generic: Integer,)";
1575 let schema = parse_schema(source).unwrap();
1576 assert_eq!(schema.root.fields[0].type_.value, SchemaType::AliasRef("Cost".to_string()));
1577 }
1578
1579 #[test]
1581 fn alias_to_primitive() {
1582 let source = "(\n name: Name,\n)\ntype Name = String";
1583 let schema = parse_schema(source).unwrap();
1584 assert_eq!(schema.aliases["Name"].value, SchemaType::String);
1585 }
1586
1587 #[test]
1589 fn alias_to_list() {
1590 let source = "(\n tags: Tags,\n)\ntype Tags = [String]";
1591 let schema = parse_schema(source).unwrap();
1592 assert_eq!(schema.aliases["Tags"].value, SchemaType::List(Box::new(SchemaType::String)));
1593 }
1594
1595 #[test]
1597 fn alias_to_option() {
1598 let source = "(\n power: Power,\n)\ntype Power = Option(Integer)";
1599 let schema = parse_schema(source).unwrap();
1600 assert_eq!(schema.aliases["Power"].value, SchemaType::Option(Box::new(SchemaType::Integer)));
1601 }
1602
1603 #[test]
1605 fn alias_ref_inside_list_reclassified() {
1606 let source = "(\n costs: [Cost],\n)\ntype Cost = (generic: Integer,)";
1607 let schema = parse_schema(source).unwrap();
1608 assert_eq!(
1609 schema.root.fields[0].type_.value,
1610 SchemaType::List(Box::new(SchemaType::AliasRef("Cost".to_string())))
1611 );
1612 }
1613
1614 #[test]
1616 fn alias_ref_inside_option_reclassified() {
1617 let source = "(\n cost: Option(Cost),\n)\ntype Cost = (generic: Integer,)";
1618 let schema = parse_schema(source).unwrap();
1619 assert_eq!(
1620 schema.root.fields[0].type_.value,
1621 SchemaType::Option(Box::new(SchemaType::AliasRef("Cost".to_string())))
1622 );
1623 }
1624
1625 #[test]
1627 fn alias_and_enum_coexist() {
1628 let source = "(\n cost: Cost,\n kind: Kind,\n)\ntype Cost = (generic: Integer,)\nenum Kind { A, B }";
1629 let schema = parse_schema(source).unwrap();
1630 assert!(schema.aliases.contains_key("Cost"));
1631 assert!(schema.enums.contains_key("Kind"));
1632 }
1633
1634 #[test]
1640 fn alias_duplicate_name() {
1641 let source = "type A = String\ntype A = Integer";
1642 let err = parse_schema(source).unwrap_err();
1643 assert_eq!(err.kind, SchemaErrorKind::DuplicateAlias { name: "A".to_string() });
1644 }
1645
1646 #[test]
1648 fn alias_recursive_direct() {
1649 let source = "(\n x: Foo,\n)\ntype Foo = Option(Foo)";
1650 let err = parse_schema(source).unwrap_err();
1651 assert_eq!(err.kind, SchemaErrorKind::RecursiveAlias { name: "Foo".to_string() });
1652 }
1653
1654 #[test]
1656 fn alias_recursive_indirect() {
1657 let source = "(\n x: Foo,\n)\ntype Foo = Option(Bar)\ntype Bar = [Foo]";
1658 let err = parse_schema(source).unwrap_err();
1659 assert!(matches!(err.kind, SchemaErrorKind::RecursiveAlias { .. }));
1660 }
1661
1662 #[test]
1668 fn parse_type_map_string_to_integer() {
1669 let mut p = parser("{String: Integer}");
1670 let t = p.parse_type().unwrap();
1671 assert_eq!(
1672 t.value,
1673 SchemaType::Map(Box::new(SchemaType::String), Box::new(SchemaType::Integer))
1674 );
1675 }
1676
1677 #[test]
1679 fn parse_type_map_integer_keys() {
1680 let mut p = parser("{Integer: String}");
1681 let t = p.parse_type().unwrap();
1682 assert_eq!(
1683 t.value,
1684 SchemaType::Map(Box::new(SchemaType::Integer), Box::new(SchemaType::String))
1685 );
1686 }
1687
1688 #[test]
1690 fn schema_map_field() {
1691 let source = "(\n attrs: {String: Integer},\n)";
1692 let schema = parse_schema(source).unwrap();
1693 assert_eq!(
1694 schema.root.fields[0].type_.value,
1695 SchemaType::Map(Box::new(SchemaType::String), Box::new(SchemaType::Integer))
1696 );
1697 }
1698
1699 #[test]
1701 fn schema_map_enum_key() {
1702 let source = "(\n scores: {Stat: Integer},\n)\nenum Stat { Str, Dex, Con }";
1703 let schema = parse_schema(source).unwrap();
1704 assert_eq!(
1705 schema.root.fields[0].type_.value,
1706 SchemaType::Map(Box::new(SchemaType::EnumRef("Stat".to_string())), Box::new(SchemaType::Integer))
1707 );
1708 }
1709
1710 #[test]
1712 fn schema_map_float_key_rejected() {
1713 let source = "(\n bad: {Float: String},\n)";
1714 let err = parse_schema(source).unwrap_err();
1715 assert!(matches!(err.kind, SchemaErrorKind::InvalidMapKeyType { .. }));
1716 }
1717
1718 #[test]
1720 fn schema_map_bool_key_rejected() {
1721 let source = "(\n bad: {Bool: String},\n)";
1722 let err = parse_schema(source).unwrap_err();
1723 assert!(matches!(err.kind, SchemaErrorKind::InvalidMapKeyType { .. }));
1724 }
1725
1726 #[test]
1732 fn parse_type_tuple() {
1733 let mut p = parser("(Float, Float)");
1734 let t = p.parse_type().unwrap();
1735 assert_eq!(t.value, SchemaType::Tuple(vec![SchemaType::Float, SchemaType::Float]));
1736 }
1737
1738 #[test]
1740 fn parse_type_tuple_mixed() {
1741 let mut p = parser("(String, Integer, Bool)");
1742 let t = p.parse_type().unwrap();
1743 assert_eq!(
1744 t.value,
1745 SchemaType::Tuple(vec![SchemaType::String, SchemaType::Integer, SchemaType::Bool])
1746 );
1747 }
1748
1749 #[test]
1751 fn schema_tuple_field() {
1752 let source = "(\n pos: (Float, Float),\n)";
1753 let schema = parse_schema(source).unwrap();
1754 assert_eq!(
1755 schema.root.fields[0].type_.value,
1756 SchemaType::Tuple(vec![SchemaType::Float, SchemaType::Float])
1757 );
1758 }
1759
1760 #[test]
1762 fn schema_struct_still_works() {
1763 let source = "(\n cost: (generic: Integer,),\n)";
1764 let schema = parse_schema(source).unwrap();
1765 if let SchemaType::Struct(s) = &schema.root.fields[0].type_.value {
1766 assert_eq!(s.fields[0].name.value, "generic");
1767 } else {
1768 panic!("expected Struct");
1769 }
1770 }
1771
1772 #[test]
1774 fn schema_empty_parens_is_struct() {
1775 let source = "(\n empty: (),\n)";
1776 let schema = parse_schema(source).unwrap();
1777 assert!(matches!(schema.root.fields[0].type_.value, SchemaType::Struct(_)));
1778 }
1779
1780 #[test]
1786 fn parse_enum_data_variant() {
1787 let source = "enum Effect { Damage(Integer), Heal(Integer), Draw }";
1788 let schema = parse_schema(source).unwrap();
1789 let effect = schema.enums.get("Effect").unwrap();
1790 assert_eq!(effect.variants.get("Damage"), Some(&Some(SchemaType::Integer)));
1791 assert_eq!(effect.variants.get("Heal"), Some(&Some(SchemaType::Integer)));
1792 assert_eq!(effect.variants.get("Draw"), Some(&None));
1793 }
1794
1795 #[test]
1797 fn parse_enum_struct_data_variant() {
1798 let source = "enum Action { Move((Integer, Integer)), Wait }";
1799 let schema = parse_schema(source).unwrap();
1800 let action = schema.enums.get("Action").unwrap();
1801 assert!(matches!(action.variants.get("Move"), Some(Some(SchemaType::Tuple(_)))));
1802 assert_eq!(action.variants.get("Wait"), Some(&None));
1803 }
1804}