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(&self) -> impl Iterator<Item = (&'doc str, ParseContext<'doc>)> + '_ {
295 let doc = self.ctx.doc();
296 let mode = self.union_tag_mode;
297 let accessed = self.ctx.accessed().clone();
299 self.map.iter().filter_map(move |(key, &node_id)| {
300 if let ObjectKey::String(name) = key
301 && !accessed.has_field(name.as_str())
302 {
303 return Some((
304 name.as_str(),
305 ParseContext::with_union_tag_mode(doc, node_id, mode),
306 ));
307 }
308 None
309 })
310 }
311
312 pub fn flatten(&self) -> ParseContext<'doc> {
322 self.ctx.flatten()
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329 use crate::value::PrimitiveValue;
330
331 fn create_test_doc() -> EureDocument {
332 let mut doc = EureDocument::new();
333 let root_id = doc.get_root_id();
334
335 let name_id = doc
337 .add_map_child(ObjectKey::String("name".to_string()), root_id)
338 .unwrap()
339 .node_id;
340 doc.node_mut(name_id).content = NodeValue::Primitive(PrimitiveValue::Text(
341 crate::text::Text::plaintext("Alice".to_string()),
342 ));
343
344 let age_id = doc
345 .add_map_child(ObjectKey::String("age".to_string()), root_id)
346 .unwrap()
347 .node_id;
348 doc.node_mut(age_id).content = NodeValue::Primitive(PrimitiveValue::Integer(30.into()));
349
350 doc
351 }
352
353 #[test]
354 fn test_record_field() {
355 let doc = create_test_doc();
356 let rec = doc.parse_record(doc.get_root_id()).unwrap();
357
358 let name: String = rec.parse_field("name").unwrap();
359 assert_eq!(name, "Alice");
360 }
361
362 #[test]
363 fn test_record_field_missing() {
364 let doc = create_test_doc();
365 let rec = doc.parse_record(doc.get_root_id()).unwrap();
366
367 let result: Result<String, _> = rec.parse_field("nonexistent");
368 assert!(matches!(
369 result.unwrap_err().kind,
370 ParseErrorKind::MissingField(_)
371 ));
372 }
373
374 #[test]
375 fn test_record_field_optional() {
376 let doc = create_test_doc();
377 let rec = doc.parse_record(doc.get_root_id()).unwrap();
378
379 let name: Option<String> = rec.parse_field_optional("name").unwrap();
380 assert_eq!(name, Some("Alice".to_string()));
381
382 let missing: Option<String> = rec.parse_field_optional("nonexistent").unwrap();
383 assert_eq!(missing, None);
384 }
385
386 #[test]
387 fn test_record_deny_unknown_fields() {
388 let doc = create_test_doc();
389 let rec = doc.parse_record(doc.get_root_id()).unwrap();
390
391 let _name: String = rec.parse_field("name").unwrap();
392 let result = rec.deny_unknown_fields();
394 assert!(matches!(
395 result.unwrap_err().kind,
396 ParseErrorKind::UnknownField(_)
397 ));
398 }
399
400 #[test]
401 fn test_record_deny_unknown_fields_all_accessed() {
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 let _age: num_bigint::BigInt = rec.parse_field("age").unwrap();
407 rec.deny_unknown_fields().unwrap();
409 }
410
411 #[test]
412 fn test_record_allow_unknown_fields() {
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 rec.allow_unknown_fields().unwrap();
419 }
420
421 #[test]
422 fn test_record_unknown_fields_iterator() {
423 let doc = create_test_doc();
424 let rec = doc.parse_record(doc.get_root_id()).unwrap();
425
426 let _name: String = rec.parse_field("name").unwrap();
427 let unknown: Vec<_> = rec.unknown_fields().collect();
429 assert_eq!(unknown.len(), 1);
430 assert_eq!(unknown[0].0, "age");
431 }
432
433 #[test]
434 fn test_record_with_non_string_keys_deny_should_error() {
435 let mut doc = EureDocument::new();
439 let root_id = doc.get_root_id();
440
441 use num_bigint::BigInt;
443 let value_id = doc
444 .add_map_child(ObjectKey::Number(BigInt::from(0)), root_id)
445 .unwrap()
446 .node_id;
447 doc.node_mut(value_id).content = NodeValue::Primitive(PrimitiveValue::Text(
448 crate::text::Text::plaintext("value".to_string()),
449 ));
450
451 let rec = doc.parse_record(doc.get_root_id()).unwrap();
452
453 let result = rec.deny_unknown_fields();
456 assert!(
457 result.is_err(),
458 "BUG: deny_unknown_fields() should error on non-string keys, but it succeeds"
459 );
460 }
461
462 #[test]
463 fn test_record_with_non_string_keys_unknown_fields_iterator() {
464 let mut doc = EureDocument::new();
467 let root_id = doc.get_root_id();
468
469 use num_bigint::BigInt;
471 let value_id = doc
472 .add_map_child(ObjectKey::Number(BigInt::from(0)), root_id)
473 .unwrap()
474 .node_id;
475 doc.node_mut(value_id).content = NodeValue::Primitive(PrimitiveValue::Text(
476 crate::text::Text::plaintext("value".to_string()),
477 ));
478
479 let rec = doc.parse_record(doc.get_root_id()).unwrap();
480
481 let unknown: Vec<_> = rec.unknown_fields().collect();
484 assert_eq!(
485 unknown.len(),
486 0,
487 "unknown_fields() should only return string keys, numeric keys are excluded"
488 );
489 }
490
491 #[test]
492 fn test_parse_ext() {
493 let mut doc = EureDocument::new();
494 let root_id = doc.get_root_id();
495
496 let ext_id = doc
498 .add_extension("optional".parse().unwrap(), root_id)
499 .unwrap()
500 .node_id;
501 doc.node_mut(ext_id).content = NodeValue::Primitive(PrimitiveValue::Bool(true));
502
503 let ctx = doc.parse_extension_context(root_id);
504 let optional: bool = ctx.parse_ext("optional").unwrap();
505 assert!(optional);
506 }
507
508 #[test]
509 fn test_parse_ext_optional_missing() {
510 let doc = EureDocument::new();
511 let root_id = doc.get_root_id();
512
513 let ctx = doc.parse_extension_context(root_id);
514 let optional: Option<bool> = ctx.parse_ext_optional("optional").unwrap();
515 assert_eq!(optional, None);
516 }
517
518 #[derive(Debug, PartialEq)]
521 struct ThreeLevelFlatten {
522 a: i32,
523 b: i32,
524 c: i32,
525 d: i32,
526 e: i32,
527 }
528
529 impl<'doc> ParseDocument<'doc> for ThreeLevelFlatten {
530 type Error = ParseError;
531
532 fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
533 let rec1 = ctx.parse_record()?;
535 let a = rec1.parse_field("a")?;
536 let ctx2 = rec1.flatten();
537
538 let rec2 = ctx2.parse_record()?;
540 let b = rec2.parse_field("b")?;
541 let c = rec2.parse_field("c")?;
542 let ctx3 = rec2.flatten();
543
544 let rec3 = ctx3.parse_record()?;
546 let d = rec3.parse_field("d")?;
547 let e = rec3.parse_field("e")?;
548 rec3.deny_unknown_fields()?;
549
550 rec2.deny_unknown_fields()?;
552
553 rec1.deny_unknown_fields()?;
555
556 Ok(Self { a, b, c, d, e })
557 }
558 }
559
560 #[test]
561 fn test_nested_flatten_preserves_consumed_fields() {
562 use crate::eure;
573
574 let doc = eure!({ a = 1, b = 2, c = 3, d = 4, e = 5 });
575 let result: ThreeLevelFlatten = doc.parse(doc.get_root_id()).unwrap();
576
577 assert_eq!(
578 result,
579 ThreeLevelFlatten {
580 a: 1,
581 b: 2,
582 c: 3,
583 d: 4,
584 e: 5
585 }
586 );
587 }
588
589 #[test]
590 fn test_nested_flatten_catches_unaccessed_field() {
591 use crate::eure;
602
603 let doc = eure!({ a = 1, b = 2, c = 3, d = 4, e = 5, f = 6 });
604 let result: Result<ThreeLevelFlatten, _> = doc.parse(doc.get_root_id());
605
606 assert_eq!(
607 result.unwrap_err().kind,
608 ParseErrorKind::UnknownField("f".to_string())
609 );
610 }
611
612 #[test]
613 fn test_flatten_union_reverts_accessed_fields_on_failure() {
614 use crate::eure;
615
616 let doc = eure!({
617 a = 1
618 b = 2
619 c = 3
620 d = 4
621 });
622
623 #[derive(Debug, PartialEq)]
625 enum TestOption {
626 A { a: i32, c: i32, e: i32 },
627 B { a: i32, b: i32 },
628 }
629
630 impl<'doc> ParseDocument<'doc> for TestOption {
631 type Error = ParseError;
632
633 fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
634 ctx.parse_union(VariantRepr::default())?
635 .variant("A", |ctx: &ParseContext<'_>| {
636 let rec = ctx.parse_record()?;
637 let a = rec.parse_field("a")?;
638 let c = rec.parse_field("c")?;
639 let e = rec.parse_field("e")?; rec.deny_unknown_fields()?;
641 Ok(TestOption::A { a, c, e })
642 })
643 .variant("B", |ctx: &ParseContext<'_>| {
644 let rec = ctx.parse_record()?;
645 let a = rec.parse_field("a")?;
646 let b = rec.parse_field("b")?;
647 rec.deny_unknown_fields()?;
648 Ok(TestOption::B { a, b })
649 })
650 .parse()
651 }
652 }
653
654 let root_id = doc.get_root_id();
656 let root_ctx = ParseContext::new(&doc, root_id);
657 let record = root_ctx.parse_record().unwrap();
658
659 let option = record.flatten().parse::<TestOption>().unwrap();
661 assert_eq!(option, TestOption::B { a: 1, b: 2 });
662
663 let d: i32 = record.parse_field("d").unwrap();
665 assert_eq!(d, 4);
666
667 let result = record.deny_unknown_fields();
671
672 assert_eq!(
673 result.unwrap_err(),
674 ParseError {
675 node_id: root_id,
676 kind: ParseErrorKind::UnknownField("c".to_string()),
677 }
678 );
679 }
680}