1use crate::parse::DocumentParser;
4use crate::prelude_internal::*;
5
6use super::{ParseContext, ParseDocument, ParseError, ParseErrorKind, ParserScope, UnionTagMode};
7
8#[must_use]
36pub struct RecordParser<'doc> {
37 map: &'doc NodeMap,
38 union_tag_mode: UnionTagMode,
40 ctx: ParseContext<'doc>,
42}
43
44impl<'doc> RecordParser<'doc> {
45 pub(crate) fn new(ctx: &ParseContext<'doc>) -> Result<Self, ParseError> {
47 if let Some(fc) = ctx.flatten_ctx()
50 && fc.scope() == ParserScope::Extension
51 {
52 return Err(ParseError {
53 node_id: ctx.node_id(),
54 kind: ParseErrorKind::RecordInExtensionScope,
55 });
56 }
57
58 let node = ctx.node();
59 match &node.content {
60 NodeValue::Map(map) => Ok(Self {
61 map,
62 union_tag_mode: ctx.union_tag_mode(),
63 ctx: ctx.clone(),
64 }),
65 NodeValue::Hole(_) => Err(ParseError {
66 node_id: ctx.node_id(),
67 kind: ParseErrorKind::UnexpectedHole,
68 }),
69 value => Err(ParseError {
70 node_id: ctx.node_id(),
71 kind: value
72 .value_kind()
73 .map(|actual| ParseErrorKind::TypeMismatch {
74 expected: crate::value::ValueKind::Map,
75 actual,
76 })
77 .unwrap_or(ParseErrorKind::UnexpectedHole),
78 }),
79 }
80 }
81
82 pub(crate) fn from_doc_and_node(
84 doc: &'doc EureDocument,
85 node_id: NodeId,
86 ) -> Result<Self, ParseError> {
87 let ctx = ParseContext::new(doc, node_id);
88 Self::new(&ctx)
89 }
90
91 fn mark_accessed(&self, name: &str) {
93 self.ctx.accessed().add_field(name);
94 }
95
96 pub fn node_id(&self) -> NodeId {
98 self.ctx.node_id()
99 }
100
101 pub fn parse_field<T>(&self, name: &str) -> Result<T, T::Error>
105 where
106 T: ParseDocument<'doc>,
107 T::Error: From<ParseError>,
108 {
109 self.parse_field_with(name, T::parse)
110 }
111
112 pub fn parse_field_with<T>(&self, name: &str, mut parser: T) -> Result<T::Output, T::Error>
113 where
114 T: DocumentParser<'doc>,
115 T::Error: From<ParseError>,
116 {
117 self.mark_accessed(name);
118 let field_node_id = self
119 .map
120 .get(&ObjectKey::String(name.to_string()))
121 .ok_or_else(|| ParseError {
122 node_id: self.ctx.node_id(),
123 kind: ParseErrorKind::MissingField(name.to_string()),
124 })?;
125 let ctx =
126 ParseContext::with_union_tag_mode(self.ctx.doc(), *field_node_id, self.union_tag_mode);
127 parser.parse(&ctx)
128 }
129
130 pub fn parse_field_optional<T>(&self, name: &str) -> Result<Option<T>, T::Error>
131 where
132 T: ParseDocument<'doc>,
133 T::Error: From<ParseError>,
134 {
135 self.parse_field_optional_with(name, T::parse)
136 }
137
138 pub fn parse_field_optional_with<T>(
142 &self,
143 name: &str,
144 mut parser: T,
145 ) -> Result<Option<T::Output>, T::Error>
146 where
147 T: DocumentParser<'doc>,
148 T::Error: From<ParseError>,
149 {
150 self.mark_accessed(name);
151 match self.map.get(&ObjectKey::String(name.to_string())) {
152 Some(field_node_id) => {
153 let ctx = ParseContext::with_union_tag_mode(
154 self.ctx.doc(),
155 *field_node_id,
156 self.union_tag_mode,
157 );
158 Ok(Some(parser.parse(&ctx)?))
159 }
160 None => Ok(None),
161 }
162 }
163
164 pub fn field(&self, name: &str) -> Result<ParseContext<'doc>, ParseError> {
169 self.mark_accessed(name);
170 let field_node_id = self
171 .map
172 .get(&ObjectKey::String(name.to_string()))
173 .ok_or_else(|| ParseError {
174 node_id: self.ctx.node_id(),
175 kind: ParseErrorKind::MissingField(name.to_string()),
176 })?;
177 Ok(ParseContext::with_union_tag_mode(
178 self.ctx.doc(),
179 *field_node_id,
180 self.union_tag_mode,
181 ))
182 }
183
184 pub fn field_optional(&self, name: &str) -> Option<ParseContext<'doc>> {
189 self.mark_accessed(name);
190 self.map
191 .get(&ObjectKey::String(name.to_string()))
192 .map(|node_id| {
193 ParseContext::with_union_tag_mode(self.ctx.doc(), *node_id, self.union_tag_mode)
194 })
195 }
196
197 pub fn field_record(&self, name: &str) -> Result<RecordParser<'doc>, ParseError> {
201 self.mark_accessed(name);
202 let field_node_id = self
203 .map
204 .get(&ObjectKey::String(name.to_string()))
205 .ok_or_else(|| ParseError {
206 node_id: self.ctx.node_id(),
207 kind: ParseErrorKind::MissingField(name.to_string()),
208 })?;
209 let ctx =
210 ParseContext::with_union_tag_mode(self.ctx.doc(), *field_node_id, self.union_tag_mode);
211 RecordParser::new(&ctx)
212 }
213
214 pub fn field_record_optional(
218 &self,
219 name: &str,
220 ) -> Result<Option<RecordParser<'doc>>, ParseError> {
221 self.mark_accessed(name);
222 match self.map.get(&ObjectKey::String(name.to_string())) {
223 Some(field_node_id) => {
224 let ctx = ParseContext::with_union_tag_mode(
225 self.ctx.doc(),
226 *field_node_id,
227 self.union_tag_mode,
228 );
229 Ok(Some(RecordParser::new(&ctx)?))
230 }
231 None => Ok(None),
232 }
233 }
234
235 pub fn deny_unknown_fields(self) -> Result<(), ParseError> {
243 if let Some(fc) = self.ctx.flatten_ctx()
245 && fc.scope() == ParserScope::Record
246 {
247 return Ok(());
248 }
249
250 let accessed = self.ctx.accessed();
252 for (key, _) in self.map.iter() {
253 match key {
254 ObjectKey::String(name) => {
255 if !accessed.has_field(name.as_str()) {
256 return Err(ParseError {
257 node_id: self.ctx.node_id(),
258 kind: ParseErrorKind::UnknownField(name.clone()),
259 });
260 }
261 }
262 other => {
264 return Err(ParseError {
265 node_id: self.ctx.node_id(),
266 kind: ParseErrorKind::InvalidKeyType(other.clone()),
267 });
268 }
269 }
270 }
271 Ok(())
272 }
273
274 pub fn allow_unknown_fields(self) -> Result<(), ParseError> {
279 for (key, _) in self.map.iter() {
281 if !matches!(key, ObjectKey::String(_)) {
282 return Err(ParseError {
283 node_id: self.ctx.node_id(),
284 kind: ParseErrorKind::InvalidKeyType(key.clone()),
285 });
286 }
287 }
288 Ok(())
289 }
290
291 pub fn unknown_fields(
300 &self,
301 ) -> impl Iterator<
302 Item = Result<(&'doc str, ParseContext<'doc>), (&'doc ObjectKey, ParseContext<'doc>)>,
303 > + '_ {
304 let doc = self.ctx.doc();
305 let mode = self.union_tag_mode;
306 let accessed = self.ctx.accessed().clone();
308 self.map
309 .iter()
310 .filter_map(move |(key, &node_id)| match key {
311 ObjectKey::String(name) => {
312 if !accessed.has_field(name.as_str()) {
313 Some(Ok((
314 name.as_str(),
315 ParseContext::with_union_tag_mode(doc, node_id, mode),
316 )))
317 } else {
318 None }
320 }
321 other => Some(Err((
322 other,
323 ParseContext::with_union_tag_mode(doc, node_id, mode),
324 ))),
325 })
326 }
327
328 pub fn unknown_entries(
337 &self,
338 ) -> impl Iterator<Item = (&'doc ObjectKey, ParseContext<'doc>)> + '_ {
339 let doc = self.ctx.doc();
340 let mode = self.union_tag_mode;
341 let accessed = self.ctx.accessed().clone();
343 self.map.iter().filter_map(move |(key, &node_id)| {
344 match key {
345 ObjectKey::String(name) => {
346 if !accessed.has_field(name.as_str()) {
348 Some((key, ParseContext::with_union_tag_mode(doc, node_id, mode)))
349 } else {
350 None
351 }
352 }
353 _ => Some((key, ParseContext::with_union_tag_mode(doc, node_id, mode))),
355 }
356 })
357 }
358
359 pub fn flatten(&self) -> ParseContext<'doc> {
369 self.ctx.flatten()
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376 use crate::value::PrimitiveValue;
377
378 fn create_test_doc() -> EureDocument {
379 let mut doc = EureDocument::new();
380 let root_id = doc.get_root_id();
381
382 let name_id = doc
384 .add_map_child(ObjectKey::String("name".to_string()), root_id)
385 .unwrap()
386 .node_id;
387 doc.node_mut(name_id).content = NodeValue::Primitive(PrimitiveValue::Text(
388 crate::text::Text::plaintext("Alice".to_string()),
389 ));
390
391 let age_id = doc
392 .add_map_child(ObjectKey::String("age".to_string()), root_id)
393 .unwrap()
394 .node_id;
395 doc.node_mut(age_id).content = NodeValue::Primitive(PrimitiveValue::Integer(30.into()));
396
397 doc
398 }
399
400 #[test]
401 fn test_record_field() {
402 let doc = create_test_doc();
403 let rec = doc.parse_record(doc.get_root_id()).unwrap();
404
405 let name: String = rec.parse_field("name").unwrap();
406 assert_eq!(name, "Alice");
407 }
408
409 #[test]
410 fn test_record_field_missing() {
411 let doc = create_test_doc();
412 let rec = doc.parse_record(doc.get_root_id()).unwrap();
413
414 let result: Result<String, _> = rec.parse_field("nonexistent");
415 assert!(matches!(
416 result.unwrap_err().kind,
417 ParseErrorKind::MissingField(_)
418 ));
419 }
420
421 #[test]
422 fn test_record_field_optional() {
423 let doc = create_test_doc();
424 let rec = doc.parse_record(doc.get_root_id()).unwrap();
425
426 let name: Option<String> = rec.parse_field_optional("name").unwrap();
427 assert_eq!(name, Some("Alice".to_string()));
428
429 let missing: Option<String> = rec.parse_field_optional("nonexistent").unwrap();
430 assert_eq!(missing, None);
431 }
432
433 #[test]
434 fn test_record_deny_unknown_fields() {
435 let doc = create_test_doc();
436 let rec = doc.parse_record(doc.get_root_id()).unwrap();
437
438 let _name: String = rec.parse_field("name").unwrap();
439 let result = rec.deny_unknown_fields();
441 assert!(matches!(
442 result.unwrap_err().kind,
443 ParseErrorKind::UnknownField(_)
444 ));
445 }
446
447 #[test]
448 fn test_record_deny_unknown_fields_all_accessed() {
449 let doc = create_test_doc();
450 let rec = doc.parse_record(doc.get_root_id()).unwrap();
451
452 let _name: String = rec.parse_field("name").unwrap();
453 let _age: num_bigint::BigInt = rec.parse_field("age").unwrap();
454 rec.deny_unknown_fields().unwrap();
456 }
457
458 #[test]
459 fn test_record_allow_unknown_fields() {
460 let doc = create_test_doc();
461 let rec = doc.parse_record(doc.get_root_id()).unwrap();
462
463 let _name: String = rec.parse_field("name").unwrap();
464 rec.allow_unknown_fields().unwrap();
466 }
467
468 #[test]
469 fn test_record_unknown_fields_iterator() {
470 let doc = create_test_doc();
471 let rec = doc.parse_record(doc.get_root_id()).unwrap();
472
473 let _name: String = rec.parse_field("name").unwrap();
474 let unknown: Vec<_> = rec.unknown_fields().collect::<Result<Vec<_>, _>>().unwrap();
476 assert_eq!(unknown.len(), 1);
477 assert_eq!(unknown[0].0, "age");
478 }
479
480 #[test]
481 fn test_record_with_non_string_keys_deny_should_error() {
482 use crate::eure;
484
485 let doc = eure!({ 0 = "value" });
486 let rec = doc.parse_record(doc.get_root_id()).unwrap();
487
488 let result = rec.deny_unknown_fields();
489 assert!(
490 matches!(result.unwrap_err().kind, ParseErrorKind::InvalidKeyType(_)),
491 "deny_unknown_fields() should error on non-string keys"
492 );
493 }
494
495 #[test]
496 fn test_record_with_non_string_keys_unknown_fields_iterator() {
497 use crate::eure;
499
500 let doc = eure!({ 0 = "value" });
501 let rec = doc.parse_record(doc.get_root_id()).unwrap();
502
503 let result: Result<Vec<_>, _> = rec.unknown_fields().collect();
505 let (invalid_key, _ctx) = result.unwrap_err();
506 assert!(
507 matches!(invalid_key, ObjectKey::Number(_)),
508 "unknown_fields() should return the invalid key directly"
509 );
510 }
511
512 #[test]
513 fn test_unknown_fields_err_contains_correct_context() {
514 use crate::eure;
516
517 let doc = eure!({ 42 = "test" });
518 let rec = doc.parse_record(doc.get_root_id()).unwrap();
519
520 let result: Result<Vec<_>, _> = rec.unknown_fields().collect();
521 let (key, ctx) = result.unwrap_err();
522
523 assert_eq!(key, &ObjectKey::Number(42.into()));
525 let value: String = ctx.parse().unwrap();
527 assert_eq!(value, "test");
528 }
529
530 #[test]
531 fn test_unknown_fields_mixed_string_and_non_string_keys() {
532 use crate::eure;
534
535 let doc = eure!({
536 name = "Alice"
537 123 = "numeric"
538 });
539 let rec = doc.parse_record(doc.get_root_id()).unwrap();
540
541 let mut ok_fields = Vec::new();
543 let mut err_keys = Vec::new();
544 for result in rec.unknown_fields() {
545 match result {
546 Ok((name, _ctx)) => ok_fields.push(name.to_string()),
547 Err((key, _ctx)) => err_keys.push(key.clone()),
548 }
549 }
550
551 assert_eq!(ok_fields, vec!["name"]);
553 assert_eq!(err_keys, vec![ObjectKey::Number(123.into())]);
554 }
555
556 #[test]
557 fn test_unknown_fields_accessed_fields_filtered_non_string_always_returned() {
558 use crate::eure;
560
561 let doc = eure!({
562 name = "Alice"
563 age = 30
564 999 = "numeric"
565 });
566 let rec = doc.parse_record(doc.get_root_id()).unwrap();
567
568 let _name: String = rec.parse_field("name").unwrap();
570
571 let mut ok_fields = Vec::new();
573 let mut err_keys = Vec::new();
574 for result in rec.unknown_fields() {
575 match result {
576 Ok((name, _ctx)) => ok_fields.push(name.to_string()),
577 Err((key, _ctx)) => err_keys.push(key.clone()),
578 }
579 }
580
581 assert_eq!(ok_fields, vec!["age"]);
582 assert_eq!(err_keys, vec![ObjectKey::Number(999.into())]);
583 }
584
585 #[test]
586 fn test_unknown_fields_multiple_non_string_keys() {
587 use crate::eure;
589
590 let doc = eure!({
591 1 = "one"
592 2 = "two"
593 });
594 let rec = doc.parse_record(doc.get_root_id()).unwrap();
595
596 let mut err_keys: Vec<ObjectKey> = Vec::new();
598 for result in rec.unknown_fields() {
599 if let Err((key, _ctx)) = result {
600 err_keys.push(key.clone());
601 }
602 }
603
604 assert_eq!(err_keys.len(), 2);
606 assert!(err_keys.contains(&ObjectKey::Number(1.into())));
607 assert!(err_keys.contains(&ObjectKey::Number(2.into())));
608 }
609
610 #[test]
611 fn test_parse_ext() {
612 let mut doc = EureDocument::new();
613 let root_id = doc.get_root_id();
614
615 let ext_id = doc
617 .add_extension("optional".parse().unwrap(), root_id)
618 .unwrap()
619 .node_id;
620 doc.node_mut(ext_id).content = NodeValue::Primitive(PrimitiveValue::Bool(true));
621
622 let ctx = doc.parse_extension_context(root_id);
623 let optional: bool = ctx.parse_ext("optional").unwrap();
624 assert!(optional);
625 }
626
627 #[test]
628 fn test_parse_ext_optional_missing() {
629 let doc = EureDocument::new();
630 let root_id = doc.get_root_id();
631
632 let ctx = doc.parse_extension_context(root_id);
633 let optional: Option<bool> = ctx.parse_ext_optional("optional").unwrap();
634 assert_eq!(optional, None);
635 }
636
637 #[derive(Debug, PartialEq)]
640 struct ThreeLevelFlatten {
641 a: i32,
642 b: i32,
643 c: i32,
644 d: i32,
645 e: i32,
646 }
647
648 impl<'doc> ParseDocument<'doc> for ThreeLevelFlatten {
649 type Error = ParseError;
650
651 fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
652 let rec1 = ctx.parse_record()?;
654 let a = rec1.parse_field("a")?;
655 let ctx2 = rec1.flatten();
656
657 let rec2 = ctx2.parse_record()?;
659 let b = rec2.parse_field("b")?;
660 let c = rec2.parse_field("c")?;
661 let ctx3 = rec2.flatten();
662
663 let rec3 = ctx3.parse_record()?;
665 let d = rec3.parse_field("d")?;
666 let e = rec3.parse_field("e")?;
667 rec3.deny_unknown_fields()?;
668
669 rec2.deny_unknown_fields()?;
671
672 rec1.deny_unknown_fields()?;
674
675 Ok(Self { a, b, c, d, e })
676 }
677 }
678
679 #[test]
680 fn test_nested_flatten_preserves_consumed_fields() {
681 use crate::eure;
692
693 let doc = eure!({ a = 1, b = 2, c = 3, d = 4, e = 5 });
694 let result: ThreeLevelFlatten = doc.parse(doc.get_root_id()).unwrap();
695
696 assert_eq!(
697 result,
698 ThreeLevelFlatten {
699 a: 1,
700 b: 2,
701 c: 3,
702 d: 4,
703 e: 5
704 }
705 );
706 }
707
708 #[test]
709 fn test_nested_flatten_catches_unaccessed_field() {
710 use crate::eure;
721
722 let doc = eure!({ a = 1, b = 2, c = 3, d = 4, e = 5, f = 6 });
723 let result: Result<ThreeLevelFlatten, _> = doc.parse(doc.get_root_id());
724
725 assert_eq!(
726 result.unwrap_err().kind,
727 ParseErrorKind::UnknownField("f".to_string())
728 );
729 }
730
731 #[test]
732 fn test_flatten_union_reverts_accessed_fields_on_failure() {
733 use crate::eure;
734
735 let doc = eure!({
736 a = 1
737 b = 2
738 c = 3
739 d = 4
740 });
741
742 #[derive(Debug, PartialEq)]
744 enum TestOption {
745 A { a: i32, c: i32, e: i32 },
746 B { a: i32, b: i32 },
747 }
748
749 impl<'doc> ParseDocument<'doc> for TestOption {
750 type Error = ParseError;
751
752 fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
753 ctx.parse_union(VariantRepr::default())?
754 .variant("A", |ctx: &ParseContext<'_>| {
755 let rec = ctx.parse_record()?;
756 let a = rec.parse_field("a")?;
757 let c = rec.parse_field("c")?;
758 let e = rec.parse_field("e")?; rec.deny_unknown_fields()?;
760 Ok(TestOption::A { a, c, e })
761 })
762 .variant("B", |ctx: &ParseContext<'_>| {
763 let rec = ctx.parse_record()?;
764 let a = rec.parse_field("a")?;
765 let b = rec.parse_field("b")?;
766 rec.deny_unknown_fields()?;
767 Ok(TestOption::B { a, b })
768 })
769 .parse()
770 }
771 }
772
773 let root_id = doc.get_root_id();
775 let root_ctx = ParseContext::new(&doc, root_id);
776 let record = root_ctx.parse_record().unwrap();
777
778 let option = record.flatten().parse::<TestOption>().unwrap();
780 assert_eq!(option, TestOption::B { a: 1, b: 2 });
781
782 let d: i32 = record.parse_field("d").unwrap();
784 assert_eq!(d, 4);
785
786 let result = record.deny_unknown_fields();
790
791 assert_eq!(
792 result.unwrap_err(),
793 ParseError {
794 node_id: root_id,
795 kind: ParseErrorKind::UnknownField("c".to_string()),
796 }
797 );
798 }
799
800 #[derive(Debug, PartialEq)]
806 struct AlternatingFlattenTest {
807 normal1: i32,
808 ext_normal2: i32,
809 ext_normal3: i32,
810 ext_content: String,
811 }
812
813 impl<'doc> ParseDocument<'doc> for AlternatingFlattenTest {
814 type Error = ParseError;
815
816 fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
817 let rec1 = ctx.parse_record()?;
819 let normal1 = rec1.parse_field("normal")?;
820 let ctx1 = rec1.flatten();
821
822 let ctx2 = ctx1.flatten_ext();
824 assert_eq!(ctx2.parser_scope(), Some(ParserScope::Extension));
825 let ext1_ctx = ctx2.ext("item")?;
826
827 let rec2 = ext1_ctx.parse_record()?;
829 let ext_normal2 = rec2.parse_field("normal")?;
830 let ctx3 = rec2.flatten();
831 assert_eq!(ctx3.parser_scope(), Some(ParserScope::Record));
832
833 let ctx4 = ctx3.flatten_ext();
835 assert_eq!(ctx4.parser_scope(), Some(ParserScope::Extension));
836 let ext2_ctx = ctx4.ext("item")?;
837
838 let rec3 = ext2_ctx.parse_record()?;
840 let ext_normal3 = rec3.parse_field("normal")?;
841 let ctx5 = rec3.flatten();
842
843 let ctx6 = ctx5.flatten_ext();
845 let ext3_ctx = ctx6.ext("item")?;
846
847 let rec4 = ext3_ctx.parse_record()?;
849 let ext_content = rec4.parse_field("content")?;
850
851 rec4.deny_unknown_fields()?;
853 ctx6.deny_unknown_extensions()?;
854 rec3.deny_unknown_fields()?;
855 ctx4.deny_unknown_extensions()?;
856 rec2.deny_unknown_fields()?;
857 ctx2.deny_unknown_extensions()?;
858 rec1.deny_unknown_fields()?;
859 ctx1.deny_unknown_extensions()?;
860
861 Ok(Self {
862 normal1,
863 ext_normal2,
864 ext_normal3,
865 ext_content,
866 })
867 }
868 }
869
870 #[test]
871 fn test_alternating_flatten_flatten_ext() {
872 use crate::eure;
873
874 let doc = eure!({
875 normal = 1
876 %item {
877 normal = 2
878 %item {
879 normal = 3
880 %item {
881 content = "Hello"
882 }
883 }
884 }
885 });
886
887 let result: AlternatingFlattenTest = doc.parse(doc.get_root_id()).unwrap();
888 assert_eq!(
889 result,
890 AlternatingFlattenTest {
891 normal1: 1,
892 ext_normal2: 2,
893 ext_normal3: 3,
894 ext_content: "Hello".to_string(),
895 }
896 );
897 }
898
899 #[test]
900 fn test_alternating_flatten_scope_changes() {
901 use crate::eure;
902
903 let doc = eure!({});
904 let root_id = doc.get_root_id();
905 let ctx = ParseContext::new(&doc, root_id);
906
907 let ctx1 = ctx.flatten();
909 assert_eq!(ctx1.parser_scope(), Some(ParserScope::Record));
910
911 let ctx2 = ctx1.flatten_ext();
913 assert_eq!(ctx2.parser_scope(), Some(ParserScope::Extension));
914
915 let ctx3 = ctx2.flatten();
917 assert_eq!(ctx3.parser_scope(), Some(ParserScope::Record));
918
919 let ctx4 = ctx3.flatten_ext();
921 assert_eq!(ctx4.parser_scope(), Some(ParserScope::Extension));
922 }
923}