ddex_parser/parser/
detector.rs

1use crate::error::ParseError;
2use ddex_core::models::versions::ERNVersion;
3use quick_xml::{events::Event, Reader};
4use std::io::BufRead;
5// core/src/parser/detector.rs
6
7pub struct VersionDetector;
8
9impl VersionDetector {
10    pub fn detect<R: std::io::Read>(reader: R) -> crate::error::Result<ERNVersion> {
11        let mut buf_reader = std::io::BufReader::new(reader);
12        Self::detect_from_bufread(&mut buf_reader)
13    }
14
15    pub fn detect_from_bufread<R: BufRead>(reader: R) -> crate::error::Result<ERNVersion> {
16        let mut xml_reader = Reader::from_reader(reader);
17        xml_reader.config_mut().trim_text(true);
18
19        let mut buf = Vec::new();
20        let mut found_root = false;
21        let mut namespace_uris = Vec::new();
22
23        // Parse XML and collect namespace URIs from the root element
24        loop {
25            match xml_reader.read_event_into(&mut buf) {
26                Ok(Event::Start(ref e)) | Ok(Event::Empty(ref e)) => {
27                    found_root = true;
28
29                    // Extract namespace URIs from attributes
30                    for attr in e.attributes() {
31                        match attr {
32                            Ok(attr) => {
33                                let key = std::str::from_utf8(attr.key.as_ref()).unwrap_or("");
34                                let value = std::str::from_utf8(&attr.value).unwrap_or("");
35
36                                // Look for xmlns declarations
37                                if key == "xmlns" || key.starts_with("xmlns:") {
38                                    namespace_uris.push(value.to_string());
39                                }
40                            }
41                            Err(e) => {
42                                return Err(ParseError::XmlError(format!("Invalid XML attribute: {}", e)));
43                            }
44                        }
45                    }
46                    break; // Only need the root element
47                }
48                Ok(Event::Eof) => {
49                    break;
50                }
51                Ok(_) => {} // Skip other events
52                Err(e) => {
53                    return Err(ParseError::XmlError(format!("XML parsing error: {}", e)));
54                }
55            }
56            buf.clear();
57        }
58
59        // If no root element found, it's invalid XML
60        if !found_root {
61            return Err(ParseError::XmlError("No root element found - invalid XML".to_string()));
62        }
63
64        // Check for DDEX ERN version in namespace URIs
65        for uri in &namespace_uris {
66            if uri.contains("http://ddex.net/xml/ern/382") {
67                return Ok(ERNVersion::V3_8_2);
68            } else if uri.contains("http://ddex.net/xml/ern/42") {
69                return Ok(ERNVersion::V4_2);
70            } else if uri.contains("http://ddex.net/xml/ern/43") {
71                return Ok(ERNVersion::V4_3);
72            }
73        }
74
75        // If no DDEX ERN namespace found, it's not a valid DDEX document
76        Err(ParseError::XmlError("No DDEX ERN namespace found - not a valid DDEX document".to_string()))
77    }
78}