1use crate::parse::DocumentParser;
4use crate::prelude_internal::*;
5
6use super::{FromEure, ParseContext, 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 _ => Err(ctx.unexpected_kind(crate::value::ValueKind::Map)),
70 }
71 }
72
73 pub(crate) fn from_doc_and_node(
75 doc: &'doc EureDocument,
76 node_id: NodeId,
77 ) -> Result<Self, ParseError> {
78 let ctx = ParseContext::new(doc, node_id);
79 Self::new(&ctx)
80 }
81
82 fn mark_accessed(&self, name: &str) {
84 self.ctx.accessed().add_field(name);
85 }
86
87 pub fn node_id(&self) -> NodeId {
89 self.ctx.node_id()
90 }
91
92 pub fn parse_field<T>(&self, name: &str) -> Result<T, T::Error>
96 where
97 T: FromEure<'doc>,
98 T::Error: From<ParseError>,
99 {
100 self.parse_field_with(name, T::parse)
101 }
102
103 pub fn parse_field_with<T>(&self, name: &str, mut parser: T) -> Result<T::Output, T::Error>
104 where
105 T: DocumentParser<'doc>,
106 T::Error: From<ParseError>,
107 {
108 self.mark_accessed(name);
109 let field_node_id = self
110 .map
111 .get(&ObjectKey::String(name.to_string()))
112 .ok_or_else(|| ParseError {
113 node_id: self.ctx.node_id(),
114 kind: ParseErrorKind::MissingField(name.to_string()),
115 })?;
116 let ctx =
117 ParseContext::with_union_tag_mode(self.ctx.doc(), *field_node_id, self.union_tag_mode);
118 parser.parse(&ctx)
119 }
120
121 pub fn parse_field_optional<T>(&self, name: &str) -> Result<Option<T>, T::Error>
122 where
123 T: FromEure<'doc>,
124 T::Error: From<ParseError>,
125 {
126 self.parse_field_optional_with(name, T::parse)
127 }
128
129 pub fn parse_field_optional_with<T>(
133 &self,
134 name: &str,
135 mut parser: T,
136 ) -> Result<Option<T::Output>, T::Error>
137 where
138 T: DocumentParser<'doc>,
139 T::Error: From<ParseError>,
140 {
141 self.mark_accessed(name);
142 match self.map.get(&ObjectKey::String(name.to_string())) {
143 Some(field_node_id) => {
144 let ctx = ParseContext::with_union_tag_mode(
145 self.ctx.doc(),
146 *field_node_id,
147 self.union_tag_mode,
148 );
149 Ok(Some(parser.parse(&ctx)?))
150 }
151 None => Ok(None),
152 }
153 }
154
155 pub fn field(&self, name: &str) -> Result<ParseContext<'doc>, ParseError> {
160 self.mark_accessed(name);
161 let field_node_id = self
162 .map
163 .get(&ObjectKey::String(name.to_string()))
164 .ok_or_else(|| ParseError {
165 node_id: self.ctx.node_id(),
166 kind: ParseErrorKind::MissingField(name.to_string()),
167 })?;
168 Ok(ParseContext::with_union_tag_mode(
169 self.ctx.doc(),
170 *field_node_id,
171 self.union_tag_mode,
172 ))
173 }
174
175 pub fn field_optional(&self, name: &str) -> Option<ParseContext<'doc>> {
180 self.mark_accessed(name);
181 self.map
182 .get(&ObjectKey::String(name.to_string()))
183 .map(|node_id| {
184 ParseContext::with_union_tag_mode(self.ctx.doc(), *node_id, self.union_tag_mode)
185 })
186 }
187
188 pub fn field_record(&self, name: &str) -> Result<RecordParser<'doc>, ParseError> {
192 self.mark_accessed(name);
193 let field_node_id = self
194 .map
195 .get(&ObjectKey::String(name.to_string()))
196 .ok_or_else(|| ParseError {
197 node_id: self.ctx.node_id(),
198 kind: ParseErrorKind::MissingField(name.to_string()),
199 })?;
200 let ctx =
201 ParseContext::with_union_tag_mode(self.ctx.doc(), *field_node_id, self.union_tag_mode);
202 RecordParser::new(&ctx)
203 }
204
205 pub fn field_record_optional(
209 &self,
210 name: &str,
211 ) -> Result<Option<RecordParser<'doc>>, ParseError> {
212 self.mark_accessed(name);
213 match self.map.get(&ObjectKey::String(name.to_string())) {
214 Some(field_node_id) => {
215 let ctx = ParseContext::with_union_tag_mode(
216 self.ctx.doc(),
217 *field_node_id,
218 self.union_tag_mode,
219 );
220 Ok(Some(RecordParser::new(&ctx)?))
221 }
222 None => Ok(None),
223 }
224 }
225
226 pub fn deny_unknown_fields(self) -> Result<(), ParseError> {
234 if let Some(fc) = self.ctx.flatten_ctx()
236 && fc.scope() == ParserScope::Record
237 {
238 return Ok(());
239 }
240
241 let accessed = self.ctx.accessed();
243 for (key, _) in self.map.iter() {
244 match key {
245 ObjectKey::String(name) => {
246 if !accessed.has_field(name.as_str()) {
247 return Err(ParseError {
248 node_id: self.ctx.node_id(),
249 kind: ParseErrorKind::UnknownField(name.clone()),
250 });
251 }
252 }
253 other => {
255 return Err(ParseError {
256 node_id: self.ctx.node_id(),
257 kind: ParseErrorKind::InvalidKeyType(other.clone()),
258 });
259 }
260 }
261 }
262 Ok(())
263 }
264
265 pub fn allow_unknown_fields(self) -> Result<(), ParseError> {
270 for (key, _) in self.map.iter() {
272 if !matches!(key, ObjectKey::String(_)) {
273 return Err(ParseError {
274 node_id: self.ctx.node_id(),
275 kind: ParseErrorKind::InvalidKeyType(key.clone()),
276 });
277 }
278 }
279 Ok(())
280 }
281
282 pub fn unknown_fields(
291 &self,
292 ) -> impl Iterator<
293 Item = Result<(&'doc str, ParseContext<'doc>), (&'doc ObjectKey, ParseContext<'doc>)>,
294 > + '_ {
295 let doc = self.ctx.doc();
296 let mode = self.union_tag_mode;
297 let accessed = self.ctx.accessed().clone();
299 self.map
300 .iter()
301 .filter_map(move |(key, &node_id)| match key {
302 ObjectKey::String(name) => {
303 if !accessed.has_field(name.as_str()) {
304 Some(Ok((
305 name.as_str(),
306 ParseContext::with_union_tag_mode(doc, node_id, mode),
307 )))
308 } else {
309 None }
311 }
312 other => Some(Err((
313 other,
314 ParseContext::with_union_tag_mode(doc, node_id, mode),
315 ))),
316 })
317 }
318
319 pub fn unknown_entries(
328 &self,
329 ) -> impl Iterator<Item = (&'doc ObjectKey, ParseContext<'doc>)> + '_ {
330 let doc = self.ctx.doc();
331 let mode = self.union_tag_mode;
332 let accessed = self.ctx.accessed().clone();
334 self.map.iter().filter_map(move |(key, &node_id)| {
335 match key {
336 ObjectKey::String(name) => {
337 if !accessed.has_field(name.as_str()) {
339 Some((key, ParseContext::with_union_tag_mode(doc, node_id, mode)))
340 } else {
341 None
342 }
343 }
344 _ => Some((key, ParseContext::with_union_tag_mode(doc, node_id, mode))),
346 }
347 })
348 }
349
350 pub fn flatten(&self) -> ParseContext<'doc> {
360 self.ctx.flatten()
361 }
362}
363
364#[cfg(test)]
365mod tests {
366 use super::*;
367 use crate::value::PrimitiveValue;
368
369 fn create_test_doc() -> EureDocument {
370 let mut doc = EureDocument::new();
371 let root_id = doc.get_root_id();
372
373 let name_id = doc
375 .add_map_child(ObjectKey::String("name".to_string()), root_id)
376 .unwrap()
377 .node_id;
378 doc.node_mut(name_id).content = NodeValue::Primitive(PrimitiveValue::Text(
379 crate::text::Text::plaintext("Alice".to_string()),
380 ));
381
382 let age_id = doc
383 .add_map_child(ObjectKey::String("age".to_string()), root_id)
384 .unwrap()
385 .node_id;
386 doc.node_mut(age_id).content = NodeValue::Primitive(PrimitiveValue::Integer(30.into()));
387
388 doc
389 }
390
391 #[test]
392 fn test_record_field() {
393 let doc = create_test_doc();
394 let rec = doc.parse_record(doc.get_root_id()).unwrap();
395
396 let name: String = rec.parse_field("name").unwrap();
397 assert_eq!(name, "Alice");
398 }
399
400 #[test]
401 fn test_record_field_missing() {
402 let doc = create_test_doc();
403 let rec = doc.parse_record(doc.get_root_id()).unwrap();
404
405 let result: Result<String, _> = rec.parse_field("nonexistent");
406 assert!(matches!(
407 result.unwrap_err().kind,
408 ParseErrorKind::MissingField(_)
409 ));
410 }
411
412 #[test]
413 fn test_record_field_optional() {
414 let doc = create_test_doc();
415 let rec = doc.parse_record(doc.get_root_id()).unwrap();
416
417 let name: Option<String> = rec.parse_field_optional("name").unwrap();
418 assert_eq!(name, Some("Alice".to_string()));
419
420 let missing: Option<String> = rec.parse_field_optional("nonexistent").unwrap();
421 assert_eq!(missing, None);
422 }
423
424 #[test]
425 fn test_record_deny_unknown_fields() {
426 let doc = create_test_doc();
427 let rec = doc.parse_record(doc.get_root_id()).unwrap();
428
429 let _name: String = rec.parse_field("name").unwrap();
430 let result = rec.deny_unknown_fields();
432 assert!(matches!(
433 result.unwrap_err().kind,
434 ParseErrorKind::UnknownField(_)
435 ));
436 }
437
438 #[test]
439 fn test_record_deny_unknown_fields_all_accessed() {
440 let doc = create_test_doc();
441 let rec = doc.parse_record(doc.get_root_id()).unwrap();
442
443 let _name: String = rec.parse_field("name").unwrap();
444 let _age: num_bigint::BigInt = rec.parse_field("age").unwrap();
445 rec.deny_unknown_fields().unwrap();
447 }
448
449 #[test]
450 fn test_record_allow_unknown_fields() {
451 let doc = create_test_doc();
452 let rec = doc.parse_record(doc.get_root_id()).unwrap();
453
454 let _name: String = rec.parse_field("name").unwrap();
455 rec.allow_unknown_fields().unwrap();
457 }
458
459 #[test]
460 fn test_record_unknown_fields_iterator() {
461 let doc = create_test_doc();
462 let rec = doc.parse_record(doc.get_root_id()).unwrap();
463
464 let _name: String = rec.parse_field("name").unwrap();
465 let unknown: Vec<_> = rec.unknown_fields().collect::<Result<Vec<_>, _>>().unwrap();
467 assert_eq!(unknown.len(), 1);
468 assert_eq!(unknown[0].0, "age");
469 }
470
471 #[test]
472 fn test_record_with_non_string_keys_deny_should_error() {
473 use crate::eure;
475
476 let doc = eure!({ 0 = "value" });
477 let rec = doc.parse_record(doc.get_root_id()).unwrap();
478
479 let result = rec.deny_unknown_fields();
480 assert!(
481 matches!(result.unwrap_err().kind, ParseErrorKind::InvalidKeyType(_)),
482 "deny_unknown_fields() should error on non-string keys"
483 );
484 }
485
486 #[test]
487 fn test_record_with_non_string_keys_unknown_fields_iterator() {
488 use crate::eure;
490
491 let doc = eure!({ 0 = "value" });
492 let rec = doc.parse_record(doc.get_root_id()).unwrap();
493
494 let result: Result<Vec<_>, _> = rec.unknown_fields().collect();
496 let (invalid_key, _ctx) = result.unwrap_err();
497 assert!(
498 matches!(invalid_key, ObjectKey::Number(_)),
499 "unknown_fields() should return the invalid key directly"
500 );
501 }
502
503 #[test]
504 fn test_unknown_fields_err_contains_correct_context() {
505 use crate::eure;
507
508 let doc = eure!({ 42 = "test" });
509 let rec = doc.parse_record(doc.get_root_id()).unwrap();
510
511 let result: Result<Vec<_>, _> = rec.unknown_fields().collect();
512 let (key, ctx) = result.unwrap_err();
513
514 assert_eq!(key, &ObjectKey::Number(42.into()));
516 let value: String = ctx.parse().unwrap();
518 assert_eq!(value, "test");
519 }
520
521 #[test]
522 fn test_unknown_fields_mixed_string_and_non_string_keys() {
523 use crate::eure;
525
526 let doc = eure!({
527 name = "Alice"
528 123 = "numeric"
529 });
530 let rec = doc.parse_record(doc.get_root_id()).unwrap();
531
532 let mut ok_fields = Vec::new();
534 let mut err_keys = Vec::new();
535 for result in rec.unknown_fields() {
536 match result {
537 Ok((name, _ctx)) => ok_fields.push(name.to_string()),
538 Err((key, _ctx)) => err_keys.push(key.clone()),
539 }
540 }
541
542 assert_eq!(ok_fields, vec!["name"]);
544 assert_eq!(err_keys, vec![ObjectKey::Number(123.into())]);
545 }
546
547 #[test]
548 fn test_unknown_fields_accessed_fields_filtered_non_string_always_returned() {
549 use crate::eure;
551
552 let doc = eure!({
553 name = "Alice"
554 age = 30
555 999 = "numeric"
556 });
557 let rec = doc.parse_record(doc.get_root_id()).unwrap();
558
559 let _name: String = rec.parse_field("name").unwrap();
561
562 let mut ok_fields = Vec::new();
564 let mut err_keys = Vec::new();
565 for result in rec.unknown_fields() {
566 match result {
567 Ok((name, _ctx)) => ok_fields.push(name.to_string()),
568 Err((key, _ctx)) => err_keys.push(key.clone()),
569 }
570 }
571
572 assert_eq!(ok_fields, vec!["age"]);
573 assert_eq!(err_keys, vec![ObjectKey::Number(999.into())]);
574 }
575
576 #[test]
577 fn test_unknown_fields_multiple_non_string_keys() {
578 use crate::eure;
580
581 let doc = eure!({
582 1 = "one"
583 2 = "two"
584 });
585 let rec = doc.parse_record(doc.get_root_id()).unwrap();
586
587 let mut err_keys: Vec<ObjectKey> = Vec::new();
589 for result in rec.unknown_fields() {
590 if let Err((key, _ctx)) = result {
591 err_keys.push(key.clone());
592 }
593 }
594
595 assert_eq!(err_keys.len(), 2);
597 assert!(err_keys.contains(&ObjectKey::Number(1.into())));
598 assert!(err_keys.contains(&ObjectKey::Number(2.into())));
599 }
600
601 #[test]
602 fn test_parse_ext() {
603 let mut doc = EureDocument::new();
604 let root_id = doc.get_root_id();
605
606 let ext_id = doc
608 .add_extension("optional".parse().unwrap(), root_id)
609 .unwrap()
610 .node_id;
611 doc.node_mut(ext_id).content = NodeValue::Primitive(PrimitiveValue::Bool(true));
612
613 let ctx = doc.parse_extension_context(root_id);
614 let optional: bool = ctx.parse_ext("optional").unwrap();
615 assert!(optional);
616 }
617
618 #[test]
619 fn test_parse_ext_optional_missing() {
620 let doc = EureDocument::new();
621 let root_id = doc.get_root_id();
622
623 let ctx = doc.parse_extension_context(root_id);
624 let optional: Option<bool> = ctx.parse_ext_optional("optional").unwrap();
625 assert_eq!(optional, None);
626 }
627
628 #[derive(Debug, PartialEq)]
631 struct ThreeLevelFlatten {
632 a: i32,
633 b: i32,
634 c: i32,
635 d: i32,
636 e: i32,
637 }
638
639 impl<'doc> FromEure<'doc> for ThreeLevelFlatten {
640 type Error = ParseError;
641
642 fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
643 let rec1 = ctx.parse_record()?;
645 let a = rec1.parse_field("a")?;
646 let ctx2 = rec1.flatten();
647
648 let rec2 = ctx2.parse_record()?;
650 let b = rec2.parse_field("b")?;
651 let c = rec2.parse_field("c")?;
652 let ctx3 = rec2.flatten();
653
654 let rec3 = ctx3.parse_record()?;
656 let d = rec3.parse_field("d")?;
657 let e = rec3.parse_field("e")?;
658 rec3.deny_unknown_fields()?;
659
660 rec2.deny_unknown_fields()?;
662
663 rec1.deny_unknown_fields()?;
665
666 Ok(Self { a, b, c, d, e })
667 }
668 }
669
670 #[test]
671 fn test_nested_flatten_preserves_consumed_fields() {
672 use crate::eure;
683
684 let doc = eure!({ a = 1, b = 2, c = 3, d = 4, e = 5 });
685 let result: ThreeLevelFlatten = doc.parse(doc.get_root_id()).unwrap();
686
687 assert_eq!(
688 result,
689 ThreeLevelFlatten {
690 a: 1,
691 b: 2,
692 c: 3,
693 d: 4,
694 e: 5
695 }
696 );
697 }
698
699 #[test]
700 fn test_nested_flatten_catches_unaccessed_field() {
701 use crate::eure;
712
713 let doc = eure!({ a = 1, b = 2, c = 3, d = 4, e = 5, f = 6 });
714 let result: Result<ThreeLevelFlatten, _> = doc.parse(doc.get_root_id());
715
716 assert_eq!(
717 result.unwrap_err().kind,
718 ParseErrorKind::UnknownField("f".to_string())
719 );
720 }
721
722 #[test]
723 fn test_flatten_union_reverts_accessed_fields_on_failure() {
724 use crate::eure;
725
726 let doc = eure!({
727 a = 1
728 b = 2
729 c = 3
730 d = 4
731 });
732
733 #[derive(Debug, PartialEq)]
735 enum TestOption {
736 A { a: i32, c: i32, e: i32 },
737 B { a: i32, b: i32 },
738 }
739
740 impl<'doc> FromEure<'doc> for TestOption {
741 type Error = ParseError;
742
743 fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
744 ctx.parse_union(VariantRepr::default())?
745 .variant("A", |ctx: &ParseContext<'_>| {
746 let rec = ctx.parse_record()?;
747 let a = rec.parse_field("a")?;
748 let c = rec.parse_field("c")?;
749 let e = rec.parse_field("e")?; rec.deny_unknown_fields()?;
751 Ok(TestOption::A { a, c, e })
752 })
753 .variant("B", |ctx: &ParseContext<'_>| {
754 let rec = ctx.parse_record()?;
755 let a = rec.parse_field("a")?;
756 let b = rec.parse_field("b")?;
757 rec.deny_unknown_fields()?;
758 Ok(TestOption::B { a, b })
759 })
760 .parse()
761 }
762 }
763
764 let root_id = doc.get_root_id();
766 let root_ctx = ParseContext::new(&doc, root_id);
767 let record = root_ctx.parse_record().unwrap();
768
769 let option = record.flatten().parse::<TestOption>().unwrap();
771 assert_eq!(option, TestOption::B { a: 1, b: 2 });
772
773 let d: i32 = record.parse_field("d").unwrap();
775 assert_eq!(d, 4);
776
777 let result = record.deny_unknown_fields();
781
782 assert_eq!(
783 result.unwrap_err(),
784 ParseError {
785 node_id: root_id,
786 kind: ParseErrorKind::UnknownField("c".to_string()),
787 }
788 );
789 }
790
791 #[derive(Debug, PartialEq)]
797 struct AlternatingFlattenTest {
798 normal1: i32,
799 ext_normal2: i32,
800 ext_normal3: i32,
801 ext_content: String,
802 }
803
804 impl<'doc> FromEure<'doc> for AlternatingFlattenTest {
805 type Error = ParseError;
806
807 fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
808 let rec1 = ctx.parse_record()?;
810 let normal1 = rec1.parse_field("normal")?;
811 let ctx1 = rec1.flatten();
812
813 let ctx2 = ctx1.flatten_ext();
815 assert_eq!(ctx2.parser_scope(), Some(ParserScope::Extension));
816 let ext1_ctx = ctx2.ext("item")?;
817
818 let rec2 = ext1_ctx.parse_record()?;
820 let ext_normal2 = rec2.parse_field("normal")?;
821 let ctx3 = rec2.flatten();
822 assert_eq!(ctx3.parser_scope(), Some(ParserScope::Record));
823
824 let ctx4 = ctx3.flatten_ext();
826 assert_eq!(ctx4.parser_scope(), Some(ParserScope::Extension));
827 let ext2_ctx = ctx4.ext("item")?;
828
829 let rec3 = ext2_ctx.parse_record()?;
831 let ext_normal3 = rec3.parse_field("normal")?;
832 let ctx5 = rec3.flatten();
833
834 let ctx6 = ctx5.flatten_ext();
836 let ext3_ctx = ctx6.ext("item")?;
837
838 let rec4 = ext3_ctx.parse_record()?;
840 let ext_content = rec4.parse_field("content")?;
841
842 rec4.deny_unknown_fields()?;
844 ctx6.deny_unknown_extensions()?;
845 rec3.deny_unknown_fields()?;
846 ctx4.deny_unknown_extensions()?;
847 rec2.deny_unknown_fields()?;
848 ctx2.deny_unknown_extensions()?;
849 rec1.deny_unknown_fields()?;
850 ctx1.deny_unknown_extensions()?;
851
852 Ok(Self {
853 normal1,
854 ext_normal2,
855 ext_normal3,
856 ext_content,
857 })
858 }
859 }
860
861 #[test]
862 fn test_alternating_flatten_flatten_ext() {
863 use crate::eure;
864
865 let doc = eure!({
866 normal = 1
867 %item {
868 normal = 2
869 %item {
870 normal = 3
871 %item {
872 content = "Hello"
873 }
874 }
875 }
876 });
877
878 let result: AlternatingFlattenTest = doc.parse(doc.get_root_id()).unwrap();
879 assert_eq!(
880 result,
881 AlternatingFlattenTest {
882 normal1: 1,
883 ext_normal2: 2,
884 ext_normal3: 3,
885 ext_content: "Hello".to_string(),
886 }
887 );
888 }
889
890 #[test]
891 fn test_alternating_flatten_scope_changes() {
892 use crate::eure;
893
894 let doc = eure!({});
895 let root_id = doc.get_root_id();
896 let ctx = ParseContext::new(&doc, root_id);
897
898 let ctx1 = ctx.flatten();
900 assert_eq!(ctx1.parser_scope(), Some(ParserScope::Record));
901
902 let ctx2 = ctx1.flatten_ext();
904 assert_eq!(ctx2.parser_scope(), Some(ParserScope::Extension));
905
906 let ctx3 = ctx2.flatten();
908 assert_eq!(ctx3.parser_scope(), Some(ParserScope::Record));
909
910 let ctx4 = ctx3.flatten_ext();
912 assert_eq!(ctx4.parser_scope(), Some(ParserScope::Extension));
913 }
914}