1use crate::parse::DocumentParser;
4use crate::prelude_internal::*;
5
6use super::{FromEure, ParseContext, ParseError, ParseErrorKind, ParserScope};
7
8#[must_use]
36pub struct RecordParser<'doc> {
37 map: &'doc NodeMap,
38 ctx: ParseContext<'doc>,
40}
41
42impl<'doc> RecordParser<'doc> {
43 pub(crate) fn new(ctx: &ParseContext<'doc>) -> Result<Self, ParseError> {
45 if let Some(fc) = ctx.flatten_ctx()
48 && fc.scope() == ParserScope::Extension
49 {
50 return Err(ParseError {
51 node_id: ctx.node_id(),
52 kind: ParseErrorKind::RecordInExtensionScope,
53 });
54 }
55
56 let node = ctx.node();
57 match &node.content {
58 NodeValue::Map(map) => Ok(Self {
59 map,
60 ctx: ctx.clone(),
61 }),
62 NodeValue::Hole(_) => Err(ParseError {
63 node_id: ctx.node_id(),
64 kind: ParseErrorKind::UnexpectedHole,
65 }),
66 _ => Err(ctx.unexpected_kind(crate::value::ValueKind::Map)),
67 }
68 }
69
70 pub(crate) fn from_doc_and_node(
72 doc: &'doc EureDocument,
73 node_id: NodeId,
74 ) -> Result<Self, ParseError> {
75 let ctx = ParseContext::new(doc, node_id);
76 Self::new(&ctx)
77 }
78
79 fn mark_accessed(&self, name: &str) {
81 self.ctx.accessed().add_field(name);
82 }
83
84 pub fn node_id(&self) -> NodeId {
86 self.ctx.node_id()
87 }
88
89 pub fn parse_field<T>(&self, name: &str) -> Result<T, T::Error>
93 where
94 T: FromEure<'doc>,
95 T::Error: From<ParseError>,
96 {
97 self.parse_field_with(name, T::parse)
98 }
99
100 pub fn parse_field_with<T>(&self, name: &str, mut parser: T) -> Result<T::Output, T::Error>
101 where
102 T: DocumentParser<'doc>,
103 T::Error: From<ParseError>,
104 {
105 self.mark_accessed(name);
106 let field_node_id = self
107 .map
108 .get(&ObjectKey::String(name.to_string()))
109 .ok_or_else(|| ParseError {
110 node_id: self.ctx.node_id(),
111 kind: ParseErrorKind::MissingField(name.to_string()),
112 })?;
113 let ctx = ParseContext::new(self.ctx.doc(), *field_node_id);
114 parser.parse(&ctx)
115 }
116
117 pub fn parse_field_optional<T>(&self, name: &str) -> Result<Option<T>, T::Error>
118 where
119 T: FromEure<'doc>,
120 T::Error: From<ParseError>,
121 {
122 self.parse_field_optional_with(name, T::parse)
123 }
124
125 pub fn parse_field_optional_with<T>(
129 &self,
130 name: &str,
131 mut parser: T,
132 ) -> Result<Option<T::Output>, T::Error>
133 where
134 T: DocumentParser<'doc>,
135 T::Error: From<ParseError>,
136 {
137 self.mark_accessed(name);
138 match self.map.get(&ObjectKey::String(name.to_string())) {
139 Some(field_node_id) => {
140 let ctx = ParseContext::new(self.ctx.doc(), *field_node_id);
141 Ok(Some(parser.parse(&ctx)?))
142 }
143 None => Ok(None),
144 }
145 }
146
147 pub fn field(&self, name: &str) -> Result<ParseContext<'doc>, ParseError> {
152 self.mark_accessed(name);
153 let field_node_id = self
154 .map
155 .get(&ObjectKey::String(name.to_string()))
156 .ok_or_else(|| ParseError {
157 node_id: self.ctx.node_id(),
158 kind: ParseErrorKind::MissingField(name.to_string()),
159 })?;
160 Ok(ParseContext::new(self.ctx.doc(), *field_node_id))
161 }
162
163 pub fn field_optional(&self, name: &str) -> Option<ParseContext<'doc>> {
168 self.mark_accessed(name);
169 self.map
170 .get(&ObjectKey::String(name.to_string()))
171 .map(|node_id| ParseContext::new(self.ctx.doc(), *node_id))
172 }
173
174 pub fn field_record(&self, name: &str) -> Result<RecordParser<'doc>, ParseError> {
178 self.mark_accessed(name);
179 let field_node_id = self
180 .map
181 .get(&ObjectKey::String(name.to_string()))
182 .ok_or_else(|| ParseError {
183 node_id: self.ctx.node_id(),
184 kind: ParseErrorKind::MissingField(name.to_string()),
185 })?;
186 let ctx = ParseContext::new(self.ctx.doc(), *field_node_id);
187 RecordParser::new(&ctx)
188 }
189
190 pub fn field_record_optional(
194 &self,
195 name: &str,
196 ) -> Result<Option<RecordParser<'doc>>, ParseError> {
197 self.mark_accessed(name);
198 match self.map.get(&ObjectKey::String(name.to_string())) {
199 Some(field_node_id) => {
200 let ctx = ParseContext::new(self.ctx.doc(), *field_node_id);
201 Ok(Some(RecordParser::new(&ctx)?))
202 }
203 None => Ok(None),
204 }
205 }
206
207 pub fn deny_unknown_fields(self) -> Result<(), ParseError> {
215 if let Some(fc) = self.ctx.flatten_ctx()
217 && fc.scope() == ParserScope::Record
218 {
219 return Ok(());
220 }
221
222 let accessed = self.ctx.accessed();
224 for (key, _) in self.map.iter() {
225 match key {
226 ObjectKey::String(name) => {
227 if !accessed.has_field(name.as_str()) {
228 return Err(ParseError {
229 node_id: self.ctx.node_id(),
230 kind: ParseErrorKind::UnknownField(name.clone()),
231 });
232 }
233 }
234 other => {
236 return Err(ParseError {
237 node_id: self.ctx.node_id(),
238 kind: ParseErrorKind::InvalidKeyType(other.clone()),
239 });
240 }
241 }
242 }
243 Ok(())
244 }
245
246 pub fn allow_unknown_fields(self) -> Result<(), ParseError> {
251 for (key, _) in self.map.iter() {
253 if !matches!(key, ObjectKey::String(_)) {
254 return Err(ParseError {
255 node_id: self.ctx.node_id(),
256 kind: ParseErrorKind::InvalidKeyType(key.clone()),
257 });
258 }
259 }
260 Ok(())
261 }
262
263 pub fn unknown_fields(
272 &self,
273 ) -> impl Iterator<
274 Item = Result<(&'doc str, ParseContext<'doc>), (&'doc ObjectKey, ParseContext<'doc>)>,
275 > + '_ {
276 let doc = self.ctx.doc();
277 let accessed = self.ctx.accessed().clone();
279 self.map
280 .iter()
281 .filter_map(move |(key, &node_id)| match key {
282 ObjectKey::String(name) => {
283 if !accessed.has_field(name.as_str()) {
284 Some(Ok((name.as_str(), ParseContext::new(doc, node_id))))
285 } else {
286 None }
288 }
289 other => Some(Err((other, ParseContext::new(doc, node_id)))),
290 })
291 }
292
293 pub fn unknown_entries(
302 &self,
303 ) -> impl Iterator<Item = (&'doc ObjectKey, ParseContext<'doc>)> + '_ {
304 let doc = self.ctx.doc();
305 let accessed = self.ctx.accessed().clone();
307 self.map.iter().filter_map(move |(key, &node_id)| {
308 match key {
309 ObjectKey::String(name) => {
310 if !accessed.has_field(name.as_str()) {
312 Some((key, ParseContext::new(doc, node_id)))
313 } else {
314 None
315 }
316 }
317 _ => Some((key, ParseContext::new(doc, node_id))),
319 }
320 })
321 }
322
323 pub fn flatten(&self) -> ParseContext<'doc> {
333 self.ctx.flatten()
334 }
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340 use crate::value::PrimitiveValue;
341
342 fn create_test_doc() -> EureDocument {
343 let mut doc = EureDocument::new();
344 let root_id = doc.get_root_id();
345
346 let name_id = doc
348 .add_map_child(ObjectKey::String("name".to_string()), root_id)
349 .unwrap()
350 .node_id;
351 doc.node_mut(name_id).content = NodeValue::Primitive(PrimitiveValue::Text(
352 crate::text::Text::plaintext("Alice".to_string()),
353 ));
354
355 let age_id = doc
356 .add_map_child(ObjectKey::String("age".to_string()), root_id)
357 .unwrap()
358 .node_id;
359 doc.node_mut(age_id).content = NodeValue::Primitive(PrimitiveValue::Integer(30.into()));
360
361 doc
362 }
363
364 #[test]
365 fn test_record_field() {
366 let doc = create_test_doc();
367 let rec = doc.parse_record(doc.get_root_id()).unwrap();
368
369 let name: String = rec.parse_field("name").unwrap();
370 assert_eq!(name, "Alice");
371 }
372
373 #[test]
374 fn test_record_field_missing() {
375 let doc = create_test_doc();
376 let rec = doc.parse_record(doc.get_root_id()).unwrap();
377
378 let result: Result<String, _> = rec.parse_field("nonexistent");
379 assert!(matches!(
380 result.unwrap_err().kind,
381 ParseErrorKind::MissingField(_)
382 ));
383 }
384
385 #[test]
386 fn test_record_field_optional() {
387 let doc = create_test_doc();
388 let rec = doc.parse_record(doc.get_root_id()).unwrap();
389
390 let name: Option<String> = rec.parse_field_optional("name").unwrap();
391 assert_eq!(name, Some("Alice".to_string()));
392
393 let missing: Option<String> = rec.parse_field_optional("nonexistent").unwrap();
394 assert_eq!(missing, None);
395 }
396
397 #[test]
398 fn test_record_deny_unknown_fields() {
399 let doc = create_test_doc();
400 let rec = doc.parse_record(doc.get_root_id()).unwrap();
401
402 let _name: String = rec.parse_field("name").unwrap();
403 let result = rec.deny_unknown_fields();
405 assert!(matches!(
406 result.unwrap_err().kind,
407 ParseErrorKind::UnknownField(_)
408 ));
409 }
410
411 #[test]
412 fn test_record_deny_unknown_fields_all_accessed() {
413 let doc = create_test_doc();
414 let rec = doc.parse_record(doc.get_root_id()).unwrap();
415
416 let _name: String = rec.parse_field("name").unwrap();
417 let _age: num_bigint::BigInt = rec.parse_field("age").unwrap();
418 rec.deny_unknown_fields().unwrap();
420 }
421
422 #[test]
423 fn test_record_allow_unknown_fields() {
424 let doc = create_test_doc();
425 let rec = doc.parse_record(doc.get_root_id()).unwrap();
426
427 let _name: String = rec.parse_field("name").unwrap();
428 rec.allow_unknown_fields().unwrap();
430 }
431
432 #[test]
433 fn test_record_unknown_fields_iterator() {
434 let doc = create_test_doc();
435 let rec = doc.parse_record(doc.get_root_id()).unwrap();
436
437 let _name: String = rec.parse_field("name").unwrap();
438 let unknown: Vec<_> = rec.unknown_fields().collect::<Result<Vec<_>, _>>().unwrap();
440 assert_eq!(unknown.len(), 1);
441 assert_eq!(unknown[0].0, "age");
442 }
443
444 #[test]
445 fn test_record_with_non_string_keys_deny_should_error() {
446 use crate::eure;
448
449 let doc = eure!({ 0 = "value" });
450 let rec = doc.parse_record(doc.get_root_id()).unwrap();
451
452 let result = rec.deny_unknown_fields();
453 assert!(
454 matches!(result.unwrap_err().kind, ParseErrorKind::InvalidKeyType(_)),
455 "deny_unknown_fields() should error on non-string keys"
456 );
457 }
458
459 #[test]
460 fn test_record_with_non_string_keys_unknown_fields_iterator() {
461 use crate::eure;
463
464 let doc = eure!({ 0 = "value" });
465 let rec = doc.parse_record(doc.get_root_id()).unwrap();
466
467 let result: Result<Vec<_>, _> = rec.unknown_fields().collect();
469 let (invalid_key, _ctx) = result.unwrap_err();
470 assert!(
471 matches!(invalid_key, ObjectKey::Number(_)),
472 "unknown_fields() should return the invalid key directly"
473 );
474 }
475
476 #[test]
477 fn test_unknown_fields_err_contains_correct_context() {
478 use crate::eure;
480
481 let doc = eure!({ 42 = "test" });
482 let rec = doc.parse_record(doc.get_root_id()).unwrap();
483
484 let result: Result<Vec<_>, _> = rec.unknown_fields().collect();
485 let (key, ctx) = result.unwrap_err();
486
487 assert_eq!(key, &ObjectKey::Number(42.into()));
489 let value: String = ctx.parse().unwrap();
491 assert_eq!(value, "test");
492 }
493
494 #[test]
495 fn test_unknown_fields_mixed_string_and_non_string_keys() {
496 use crate::eure;
498
499 let doc = eure!({
500 name = "Alice"
501 123 = "numeric"
502 });
503 let rec = doc.parse_record(doc.get_root_id()).unwrap();
504
505 let mut ok_fields = Vec::new();
507 let mut err_keys = Vec::new();
508 for result in rec.unknown_fields() {
509 match result {
510 Ok((name, _ctx)) => ok_fields.push(name.to_string()),
511 Err((key, _ctx)) => err_keys.push(key.clone()),
512 }
513 }
514
515 assert_eq!(ok_fields, vec!["name"]);
517 assert_eq!(err_keys, vec![ObjectKey::Number(123.into())]);
518 }
519
520 #[test]
521 fn test_unknown_fields_accessed_fields_filtered_non_string_always_returned() {
522 use crate::eure;
524
525 let doc = eure!({
526 name = "Alice"
527 age = 30
528 999 = "numeric"
529 });
530 let rec = doc.parse_record(doc.get_root_id()).unwrap();
531
532 let _name: String = rec.parse_field("name").unwrap();
534
535 let mut ok_fields = Vec::new();
537 let mut err_keys = Vec::new();
538 for result in rec.unknown_fields() {
539 match result {
540 Ok((name, _ctx)) => ok_fields.push(name.to_string()),
541 Err((key, _ctx)) => err_keys.push(key.clone()),
542 }
543 }
544
545 assert_eq!(ok_fields, vec!["age"]);
546 assert_eq!(err_keys, vec![ObjectKey::Number(999.into())]);
547 }
548
549 #[test]
550 fn test_unknown_fields_multiple_non_string_keys() {
551 use crate::eure;
553
554 let doc = eure!({
555 1 = "one"
556 2 = "two"
557 });
558 let rec = doc.parse_record(doc.get_root_id()).unwrap();
559
560 let mut err_keys: Vec<ObjectKey> = Vec::new();
562 for result in rec.unknown_fields() {
563 if let Err((key, _ctx)) = result {
564 err_keys.push(key.clone());
565 }
566 }
567
568 assert_eq!(err_keys.len(), 2);
570 assert!(err_keys.contains(&ObjectKey::Number(1.into())));
571 assert!(err_keys.contains(&ObjectKey::Number(2.into())));
572 }
573
574 #[test]
575 fn test_parse_ext() {
576 let mut doc = EureDocument::new();
577 let root_id = doc.get_root_id();
578
579 let ext_id = doc
581 .add_extension("optional".parse().unwrap(), root_id)
582 .unwrap()
583 .node_id;
584 doc.node_mut(ext_id).content = NodeValue::Primitive(PrimitiveValue::Bool(true));
585
586 let ctx = doc.parse_extension_context(root_id);
587 let optional: bool = ctx.parse_ext("optional").unwrap();
588 assert!(optional);
589 }
590
591 #[test]
592 fn test_parse_ext_optional_missing() {
593 let doc = EureDocument::new();
594 let root_id = doc.get_root_id();
595
596 let ctx = doc.parse_extension_context(root_id);
597 let optional: Option<bool> = ctx.parse_ext_optional("optional").unwrap();
598 assert_eq!(optional, None);
599 }
600
601 #[derive(Debug, PartialEq)]
604 struct ThreeLevelFlatten {
605 a: i32,
606 b: i32,
607 c: i32,
608 d: i32,
609 e: i32,
610 }
611
612 impl<'doc> FromEure<'doc> for ThreeLevelFlatten {
613 type Error = ParseError;
614
615 fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
616 let rec1 = ctx.parse_record()?;
618 let a = rec1.parse_field("a")?;
619 let ctx2 = rec1.flatten();
620
621 let rec2 = ctx2.parse_record()?;
623 let b = rec2.parse_field("b")?;
624 let c = rec2.parse_field("c")?;
625 let ctx3 = rec2.flatten();
626
627 let rec3 = ctx3.parse_record()?;
629 let d = rec3.parse_field("d")?;
630 let e = rec3.parse_field("e")?;
631 rec3.deny_unknown_fields()?;
632
633 rec2.deny_unknown_fields()?;
635
636 rec1.deny_unknown_fields()?;
638
639 Ok(Self { a, b, c, d, e })
640 }
641 }
642
643 #[test]
644 fn test_nested_flatten_preserves_consumed_fields() {
645 use crate::eure;
656
657 let doc = eure!({ a = 1, b = 2, c = 3, d = 4, e = 5 });
658 let result: ThreeLevelFlatten = doc.parse(doc.get_root_id()).unwrap();
659
660 assert_eq!(
661 result,
662 ThreeLevelFlatten {
663 a: 1,
664 b: 2,
665 c: 3,
666 d: 4,
667 e: 5
668 }
669 );
670 }
671
672 #[test]
673 fn test_nested_flatten_catches_unaccessed_field() {
674 use crate::eure;
685
686 let doc = eure!({ a = 1, b = 2, c = 3, d = 4, e = 5, f = 6 });
687 let result: Result<ThreeLevelFlatten, _> = doc.parse(doc.get_root_id());
688
689 assert_eq!(
690 result.unwrap_err().kind,
691 ParseErrorKind::UnknownField("f".to_string())
692 );
693 }
694
695 #[test]
696 fn test_flatten_union_reverts_accessed_fields_on_failure() {
697 use crate::eure;
698
699 let doc = eure!({
700 a = 1
701 b = 2
702 c = 3
703 d = 4
704 });
705
706 #[derive(Debug, PartialEq)]
708 enum TestOption {
709 A { a: i32, c: i32, e: i32 },
710 B { a: i32, b: i32 },
711 }
712
713 impl<'doc> FromEure<'doc> for TestOption {
714 type Error = ParseError;
715
716 fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
717 ctx.parse_union()?
718 .variant("A", |ctx: &ParseContext<'_>| {
719 let rec = ctx.parse_record()?;
720 let a = rec.parse_field("a")?;
721 let c = rec.parse_field("c")?;
722 let e = rec.parse_field("e")?; rec.deny_unknown_fields()?;
724 Ok(TestOption::A { a, c, e })
725 })
726 .variant("B", |ctx: &ParseContext<'_>| {
727 let rec = ctx.parse_record()?;
728 let a = rec.parse_field("a")?;
729 let b = rec.parse_field("b")?;
730 rec.deny_unknown_fields()?;
731 Ok(TestOption::B { a, b })
732 })
733 .parse()
734 }
735 }
736
737 let root_id = doc.get_root_id();
739 let root_ctx = ParseContext::new(&doc, root_id);
740 let record = root_ctx.parse_record().unwrap();
741
742 let option = record.flatten().parse::<TestOption>().unwrap();
744 assert_eq!(option, TestOption::B { a: 1, b: 2 });
745
746 let d: i32 = record.parse_field("d").unwrap();
748 assert_eq!(d, 4);
749
750 let result = record.deny_unknown_fields();
754
755 assert_eq!(
756 result.unwrap_err(),
757 ParseError {
758 node_id: root_id,
759 kind: ParseErrorKind::UnknownField("c".to_string()),
760 }
761 );
762 }
763
764 #[derive(Debug, PartialEq)]
770 struct AlternatingFlattenTest {
771 normal1: i32,
772 ext_normal2: i32,
773 ext_normal3: i32,
774 ext_content: String,
775 }
776
777 impl<'doc> FromEure<'doc> for AlternatingFlattenTest {
778 type Error = ParseError;
779
780 fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
781 let rec1 = ctx.parse_record()?;
783 let normal1 = rec1.parse_field("normal")?;
784 let ctx1 = rec1.flatten();
785
786 let ctx2 = ctx1.flatten_ext();
788 assert_eq!(ctx2.parser_scope(), Some(ParserScope::Extension));
789 let ext1_ctx = ctx2.ext("item")?;
790
791 let rec2 = ext1_ctx.parse_record()?;
793 let ext_normal2 = rec2.parse_field("normal")?;
794 let ctx3 = rec2.flatten();
795 assert_eq!(ctx3.parser_scope(), Some(ParserScope::Record));
796
797 let ctx4 = ctx3.flatten_ext();
799 assert_eq!(ctx4.parser_scope(), Some(ParserScope::Extension));
800 let ext2_ctx = ctx4.ext("item")?;
801
802 let rec3 = ext2_ctx.parse_record()?;
804 let ext_normal3 = rec3.parse_field("normal")?;
805 let ctx5 = rec3.flatten();
806
807 let ctx6 = ctx5.flatten_ext();
809 let ext3_ctx = ctx6.ext("item")?;
810
811 let rec4 = ext3_ctx.parse_record()?;
813 let ext_content = rec4.parse_field("content")?;
814
815 rec4.deny_unknown_fields()?;
817 ctx6.deny_unknown_extensions()?;
818 rec3.deny_unknown_fields()?;
819 ctx4.deny_unknown_extensions()?;
820 rec2.deny_unknown_fields()?;
821 ctx2.deny_unknown_extensions()?;
822 rec1.deny_unknown_fields()?;
823 ctx1.deny_unknown_extensions()?;
824
825 Ok(Self {
826 normal1,
827 ext_normal2,
828 ext_normal3,
829 ext_content,
830 })
831 }
832 }
833
834 #[test]
835 fn test_alternating_flatten_flatten_ext() {
836 use crate::eure;
837
838 let doc = eure!({
839 normal = 1
840 %item {
841 normal = 2
842 %item {
843 normal = 3
844 %item {
845 content = "Hello"
846 }
847 }
848 }
849 });
850
851 let result: AlternatingFlattenTest = doc.parse(doc.get_root_id()).unwrap();
852 assert_eq!(
853 result,
854 AlternatingFlattenTest {
855 normal1: 1,
856 ext_normal2: 2,
857 ext_normal3: 3,
858 ext_content: "Hello".to_string(),
859 }
860 );
861 }
862
863 #[test]
864 fn test_alternating_flatten_scope_changes() {
865 use crate::eure;
866
867 let doc = eure!({});
868 let root_id = doc.get_root_id();
869 let ctx = ParseContext::new(&doc, root_id);
870
871 let ctx1 = ctx.flatten();
873 assert_eq!(ctx1.parser_scope(), Some(ParserScope::Record));
874
875 let ctx2 = ctx1.flatten_ext();
877 assert_eq!(ctx2.parser_scope(), Some(ParserScope::Extension));
878
879 let ctx3 = ctx2.flatten();
881 assert_eq!(ctx3.parser_scope(), Some(ParserScope::Record));
882
883 let ctx4 = ctx3.flatten_ext();
885 assert_eq!(ctx4.parser_scope(), Some(ParserScope::Extension));
886 }
887}