ddex_parser/parser/
dom.rs

1// core/src/parser/dom.rs
2//! DOM-based parser for smaller DDEX files
3
4use crate::error::ParseError;
5use crate::parser::namespace_detector::{NamespaceContext, NamespaceDetector};
6use crate::parser::ParseOptions;
7use crate::transform::{flatten::Flattener, graph::GraphBuilder};
8use ddex_core::models::flat::ParsedERNMessage;
9use ddex_core::models::graph::ERNMessage;
10use ddex_core::models::versions::ERNVersion;
11use std::io::{BufRead, Seek, SeekFrom};
12use std::time::Instant;
13
14/// Parse using DOM for smaller files
15pub fn parse_dom<R: BufRead + Seek>(
16    mut reader: R,
17    version: ERNVersion,
18    options: ParseOptions,
19    security_config: &crate::parser::security::SecurityConfig,
20) -> Result<ParsedERNMessage, ParseError> {
21    let start = Instant::now();
22
23    // Check timeout
24    if !options.allow_blocking && options.timeout_ms > 0 {
25        // Would implement timeout checking
26    }
27
28    // First pass: detect namespaces with security enforcement
29    let mut namespace_detector = NamespaceDetector::new();
30    let namespace_result =
31        namespace_detector.detect_from_xml_with_security(&mut reader, security_config)?;
32    let namespace_context = NamespaceContext::from_detection_result(namespace_result);
33
34    // Reset reader for second pass
35    reader.seek(SeekFrom::Start(0))?;
36
37    // Build graph model from XML with namespace context
38    let graph_builder = GraphBuilder::new(version);
39    let graph = graph_builder.build_from_xml_with_context_and_security(
40        reader,
41        namespace_context,
42        security_config,
43    )?;
44
45    // Optionally resolve references
46    let graph = if options.resolve_references {
47        resolve_references(graph)?
48    } else {
49        graph
50    };
51
52    // Flatten to developer-friendly model
53    let flat = Flattener::flatten(graph.clone());
54
55    // Check elapsed time
56    let elapsed = start.elapsed();
57    if elapsed.as_millis() > options.timeout_ms as u128 {
58        return Err(ParseError::Timeout {
59            seconds: elapsed.as_secs(),
60        });
61    }
62
63    Ok(ParsedERNMessage {
64        graph,
65        flat,
66        extensions: None,
67    })
68}
69
70fn resolve_references(message: ERNMessage) -> Result<ERNMessage, ParseError> {
71    use crate::transform::resolve::ReferenceResolver;
72
73    let mut resolver = ReferenceResolver::new();
74    resolver.build_maps(&message);
75
76    // Check for unresolved references
77    let unresolved = resolver.validate_references(&message);
78    if !unresolved.is_empty() {
79        // Log warnings but don't fail
80        for uref in unresolved {
81            tracing::warn!(
82                "Unresolved reference: {} -> {} at {}",
83                uref.reference_type,
84                uref.reference_value,
85                uref.location
86            );
87        }
88    }
89
90    Ok(message)
91}