1extern crate alloc;
4
5use alloc::format;
6use std::collections::HashSet;
7use std::sync::OnceLock;
8
9use crate::document::node::NodeMap;
10use crate::prelude_internal::*;
11
12use super::{ParseDocument, ParseError, ParseErrorKind};
13
14pub struct RecordParser<'doc> {
32 doc: &'doc EureDocument,
33 node_id: NodeId,
34 map: &'doc NodeMap,
35 accessed: HashSet<String>,
36}
37
38impl<'doc> RecordParser<'doc> {
39 pub(crate) fn new(doc: &'doc EureDocument, node_id: NodeId, map: &'doc NodeMap) -> Self {
41 Self {
42 doc,
43 node_id,
44 map,
45 accessed: HashSet::new(),
46 }
47 }
48
49 pub fn node_id(&self) -> NodeId {
51 self.node_id
52 }
53
54 pub fn field<T: ParseDocument<'doc>>(&mut self, name: &str) -> Result<T, ParseError> {
58 self.accessed.insert(name.to_string());
59 let field_node_id = self
60 .map
61 .get(&ObjectKey::String(name.to_string()))
62 .ok_or_else(|| ParseError {
63 node_id: self.node_id,
64 kind: ParseErrorKind::MissingField(name.to_string()),
65 })?;
66 T::parse(self.doc, field_node_id)
67 }
68
69 pub fn field_optional<T: ParseDocument<'doc>>(
73 &mut self,
74 name: &str,
75 ) -> Result<Option<T>, ParseError> {
76 self.accessed.insert(name.to_string());
77 match self.map.get(&ObjectKey::String(name.to_string())) {
78 Some(field_node_id) => Ok(Some(T::parse(self.doc, field_node_id)?)),
79 None => Ok(None),
80 }
81 }
82
83 pub fn field_node(&mut self, name: &str) -> Result<NodeId, ParseError> {
87 self.accessed.insert(name.to_string());
88 self.map
89 .get(&ObjectKey::String(name.to_string()))
90 .ok_or_else(|| ParseError {
91 node_id: self.node_id,
92 kind: ParseErrorKind::MissingField(name.to_string()),
93 })
94 }
95
96 pub fn field_node_optional(&mut self, name: &str) -> Option<NodeId> {
100 self.accessed.insert(name.to_string());
101 self.map.get(&ObjectKey::String(name.to_string()))
102 }
103
104 pub fn deny_unknown_fields(self) -> Result<(), ParseError> {
109 for (key, _) in self.map.iter() {
110 match key {
111 ObjectKey::String(name) => {
112 if !self.accessed.contains(name.as_str()) {
113 return Err(ParseError {
114 node_id: self.node_id,
115 kind: ParseErrorKind::UnknownField(name.clone()),
116 });
117 }
118 }
119 other => {
121 return Err(ParseError {
122 node_id: self.node_id,
123 kind: ParseErrorKind::InvalidKeyType(other.clone()),
124 });
125 }
126 }
127 }
128 Ok(())
129 }
130
131 pub fn allow_unknown_fields(self) -> Result<(), ParseError> {
136 for (key, _) in self.map.iter() {
138 if !matches!(key, ObjectKey::String(_)) {
139 return Err(ParseError {
140 node_id: self.node_id,
141 kind: ParseErrorKind::InvalidKeyType(key.clone()),
142 });
143 }
144 }
145 Ok(())
146 }
147
148 pub fn unknown_fields(&self) -> impl Iterator<Item = (&'doc str, NodeId)> + '_ {
152 self.map.iter().filter_map(|(key, &node_id)| {
153 if let ObjectKey::String(name) = key
154 && !self.accessed.contains(name.as_str())
155 {
156 return Some((name.as_str(), node_id));
157 }
158 None
159 })
160 }
161}
162
163pub struct ExtParser<'doc> {
176 doc: &'doc EureDocument,
177 node_id: NodeId,
178 extensions: &'doc Map<Identifier, NodeId>,
179 accessed: HashSet<Identifier>,
180}
181
182impl<'doc> ExtParser<'doc> {
183 pub(crate) fn new(
185 doc: &'doc EureDocument,
186 node_id: NodeId,
187 extensions: &'doc Map<Identifier, NodeId>,
188 ) -> Self {
189 Self {
190 doc,
191 node_id,
192 extensions,
193 accessed: HashSet::new(),
194 }
195 }
196
197 pub fn node_id(&self) -> NodeId {
199 self.node_id
200 }
201
202 pub fn ext<T: ParseDocument<'doc>>(&mut self, name: &str) -> Result<T, ParseError> {
206 let ident: Identifier = name.parse().map_err(|e| ParseError {
207 node_id: self.node_id,
208 kind: ParseErrorKind::InvalidIdentifier(e),
209 })?;
210 self.accessed.insert(ident.clone());
211 let ext_node_id = self.extensions.get(&ident).ok_or_else(|| ParseError {
212 node_id: self.node_id,
213 kind: ParseErrorKind::MissingExtension(name.to_string()),
214 })?;
215 T::parse(self.doc, *ext_node_id)
216 }
217
218 pub fn ext_optional<T: ParseDocument<'doc>>(
222 &mut self,
223 name: &str,
224 ) -> Result<Option<T>, ParseError> {
225 let ident: Identifier = name.parse().map_err(|e| ParseError {
226 node_id: self.node_id,
227 kind: ParseErrorKind::InvalidIdentifier(e),
228 })?;
229 self.accessed.insert(ident.clone());
230 match self.extensions.get(&ident) {
231 Some(ext_node_id) => Ok(Some(T::parse(self.doc, *ext_node_id)?)),
232 None => Ok(None),
233 }
234 }
235
236 pub fn ext_node(&mut self, name: &str) -> Result<NodeId, ParseError> {
240 let ident: Identifier = name.parse().map_err(|e| ParseError {
241 node_id: self.node_id,
242 kind: ParseErrorKind::InvalidIdentifier(e),
243 })?;
244 self.accessed.insert(ident.clone());
245 self.extensions
246 .get(&ident)
247 .copied()
248 .ok_or_else(|| ParseError {
249 node_id: self.node_id,
250 kind: ParseErrorKind::MissingExtension(name.to_string()),
251 })
252 }
253
254 pub fn ext_node_optional(&mut self, name: &str) -> Option<NodeId> {
258 let ident: Identifier = name.parse().ok()?;
259 self.accessed.insert(ident.clone());
260 self.extensions.get(&ident).copied()
261 }
262
263 pub fn deny_unknown_extensions(self) -> Result<(), ParseError> {
265 for (ident, _) in self.extensions.iter() {
266 if !self.accessed.contains(ident) {
267 return Err(ParseError {
268 node_id: self.node_id,
269 kind: ParseErrorKind::UnknownField(format!("$ext-type.{}", ident)),
270 });
271 }
272 }
273 Ok(())
274 }
275
276 pub fn allow_unknown_extensions(self) {
278 }
280
281 pub fn unknown_extensions(&self) -> impl Iterator<Item = (&'doc Identifier, NodeId)> + '_ {
285 self.extensions.iter().filter_map(|(ident, node_id)| {
286 if !self.accessed.contains(ident) {
287 Some((ident, *node_id))
288 } else {
289 None
290 }
291 })
292 }
293}
294
295impl EureDocument {
296 pub fn parse_record(&self, node_id: NodeId) -> Result<RecordParser<'_>, ParseError> {
301 static EMPTY_MAP: OnceLock<NodeMap> = OnceLock::new();
303
304 let node = self.node(node_id);
305 match &node.content {
306 NodeValue::Map(map) => Ok(RecordParser::new(self, node_id, map)),
307 NodeValue::Hole => {
309 let empty = EMPTY_MAP.get_or_init(NodeMap::default);
310 Ok(RecordParser::new(self, node_id, empty))
311 }
312 value => Err(ParseError {
313 node_id,
314 kind: value
315 .value_kind()
316 .map(|actual| ParseErrorKind::TypeMismatch {
317 expected: crate::value::ValueKind::Map,
318 actual,
319 })
320 .unwrap_or(ParseErrorKind::UnexpectedUninitialized),
321 }),
322 }
323 }
324
325 pub fn parse_extension(&self, node_id: NodeId) -> ExtParser<'_> {
327 let node = self.node(node_id);
328 ExtParser::new(self, node_id, &node.extensions)
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335 use crate::value::PrimitiveValue;
336
337 fn create_test_doc() -> EureDocument {
338 let mut doc = EureDocument::new();
339 let root_id = doc.get_root_id();
340
341 let name_id = doc
343 .add_map_child(ObjectKey::String("name".to_string()), root_id)
344 .unwrap()
345 .node_id;
346 doc.node_mut(name_id).content = NodeValue::Primitive(PrimitiveValue::Text(
347 crate::text::Text::plaintext("Alice".to_string()),
348 ));
349
350 let age_id = doc
351 .add_map_child(ObjectKey::String("age".to_string()), root_id)
352 .unwrap()
353 .node_id;
354 doc.node_mut(age_id).content = NodeValue::Primitive(PrimitiveValue::Integer(30.into()));
355
356 doc
357 }
358
359 #[test]
360 fn test_record_field() {
361 let doc = create_test_doc();
362 let mut rec = doc.parse_record(doc.get_root_id()).unwrap();
363
364 let name: String = rec.field("name").unwrap();
365 assert_eq!(name, "Alice");
366 }
367
368 #[test]
369 fn test_record_field_missing() {
370 let doc = create_test_doc();
371 let mut rec = doc.parse_record(doc.get_root_id()).unwrap();
372
373 let result: Result<String, _> = rec.field("nonexistent");
374 assert!(matches!(
375 result.unwrap_err().kind,
376 ParseErrorKind::MissingField(_)
377 ));
378 }
379
380 #[test]
381 fn test_record_field_optional() {
382 let doc = create_test_doc();
383 let mut rec = doc.parse_record(doc.get_root_id()).unwrap();
384
385 let name: Option<String> = rec.field_optional("name").unwrap();
386 assert_eq!(name, Some("Alice".to_string()));
387
388 let missing: Option<String> = rec.field_optional("nonexistent").unwrap();
389 assert_eq!(missing, None);
390 }
391
392 #[test]
393 fn test_record_deny_unknown_fields() {
394 let doc = create_test_doc();
395 let mut rec = doc.parse_record(doc.get_root_id()).unwrap();
396
397 let _name: String = rec.field("name").unwrap();
398 let result = rec.deny_unknown_fields();
400 assert!(matches!(
401 result.unwrap_err().kind,
402 ParseErrorKind::UnknownField(_)
403 ));
404 }
405
406 #[test]
407 fn test_record_deny_unknown_fields_all_accessed() {
408 let doc = create_test_doc();
409 let mut rec = doc.parse_record(doc.get_root_id()).unwrap();
410
411 let _name: String = rec.field("name").unwrap();
412 let _age: num_bigint::BigInt = rec.field("age").unwrap();
413 rec.deny_unknown_fields().unwrap();
415 }
416
417 #[test]
418 fn test_record_allow_unknown_fields() {
419 let doc = create_test_doc();
420 let mut rec = doc.parse_record(doc.get_root_id()).unwrap();
421
422 let _name: String = rec.field("name").unwrap();
423 rec.allow_unknown_fields().unwrap();
425 }
426
427 #[test]
428 fn test_record_unknown_fields_iterator() {
429 let doc = create_test_doc();
430 let mut rec = doc.parse_record(doc.get_root_id()).unwrap();
431
432 let _name: String = rec.field("name").unwrap();
433 let unknown: Vec<_> = rec.unknown_fields().collect();
435 assert_eq!(unknown.len(), 1);
436 assert_eq!(unknown[0].0, "age");
437 }
438
439 #[test]
440 fn test_record_with_non_string_keys_deny_should_error() {
441 let mut doc = EureDocument::new();
445 let root_id = doc.get_root_id();
446
447 use num_bigint::BigInt;
449 let value_id = doc
450 .add_map_child(ObjectKey::Number(BigInt::from(0)), root_id)
451 .unwrap()
452 .node_id;
453 doc.node_mut(value_id).content = NodeValue::Primitive(PrimitiveValue::Text(
454 crate::text::Text::plaintext("value".to_string()),
455 ));
456
457 let rec = doc.parse_record(doc.get_root_id()).unwrap();
458
459 let result = rec.deny_unknown_fields();
462 assert!(
463 result.is_err(),
464 "BUG: deny_unknown_fields() should error on non-string keys, but it succeeds"
465 );
466 }
467
468 #[test]
469 fn test_record_with_non_string_keys_unknown_fields_iterator() {
470 let mut doc = EureDocument::new();
473 let root_id = doc.get_root_id();
474
475 use num_bigint::BigInt;
477 let value_id = doc
478 .add_map_child(ObjectKey::Number(BigInt::from(0)), root_id)
479 .unwrap()
480 .node_id;
481 doc.node_mut(value_id).content = NodeValue::Primitive(PrimitiveValue::Text(
482 crate::text::Text::plaintext("value".to_string()),
483 ));
484
485 let rec = doc.parse_record(doc.get_root_id()).unwrap();
486
487 let unknown: Vec<_> = rec.unknown_fields().collect();
490 assert_eq!(
491 unknown.len(),
492 0,
493 "unknown_fields() should only return string keys, numeric keys are excluded"
494 );
495 }
496
497 #[test]
498 fn test_ext_parser() {
499 let mut doc = EureDocument::new();
500 let root_id = doc.get_root_id();
501
502 let ext_id = doc
504 .add_extension("optional".parse().unwrap(), root_id)
505 .unwrap()
506 .node_id;
507 doc.node_mut(ext_id).content = NodeValue::Primitive(PrimitiveValue::Bool(true));
508
509 let mut ext = doc.parse_extension(root_id);
510 let optional: bool = ext.ext("optional").unwrap();
511 assert!(optional);
512 }
513
514 #[test]
515 fn test_ext_parser_optional_missing() {
516 let doc = EureDocument::new();
517 let root_id = doc.get_root_id();
518
519 let mut ext = doc.parse_extension(root_id);
520 let optional: Option<bool> = ext.ext_optional("optional").unwrap();
521 assert_eq!(optional, None);
522 }
523}