ddex_parser/transform/
graph.rs

1// core/src/transform/graph.rs
2// Remove unused imports and variables
3use crate::error::ParseError;
4use crate::parser::namespace_detector::NamespaceContext;
5use crate::parser::xml_validator::XmlValidator;
6use ddex_core::models::graph::{
7    ERNMessage, MessageHeader, MessageRecipient, MessageSender, MessageType, Release,
8};
9use ddex_core::models::versions::ERNVersion;
10use quick_xml::events::Event;
11use quick_xml::Reader;
12use std::io::BufRead;
13
14pub struct GraphBuilder {
15    version: ERNVersion,
16}
17
18impl GraphBuilder {
19    pub fn new(version: ERNVersion) -> Self {
20        Self { version }
21    }
22
23    pub fn build_from_xml<R: BufRead + std::io::Seek>(
24        &self,
25        reader: R,
26    ) -> Result<ERNMessage, ParseError> {
27        self.build_from_xml_with_security_config(
28            reader,
29            &crate::parser::security::SecurityConfig::default(),
30        )
31    }
32
33    pub fn build_from_xml_with_security_config<R: BufRead + std::io::Seek>(
34        &self,
35        mut reader: R,
36        _security_config: &crate::parser::security::SecurityConfig,
37    ) -> Result<ERNMessage, ParseError> {
38        let mut xml_reader = Reader::from_reader(&mut reader);
39
40        // Enable strict XML validation
41        xml_reader.config_mut().trim_text(true);
42        xml_reader.config_mut().check_end_names = true;
43        xml_reader.config_mut().expand_empty_elements = false;
44
45        // Start with a minimal header - we'll parse it inline during the main loop
46        let message_header = self.create_minimal_header()?;
47        let mut validator = XmlValidator::strict();
48        let mut releases = Vec::new();
49        let resources = Vec::new(); // Remove mut
50        let parties = Vec::new(); // Remove mut
51        let deals = Vec::new(); // Remove mut
52
53        // Parse with XML validation and depth tracking
54        let mut buf = Vec::new();
55        let mut in_release_list = false;
56
57        loop {
58            match xml_reader.read_event_into(&mut buf) {
59                Ok(ref event) => {
60                    // Validate XML structure
61                    validator.validate_event(event, &xml_reader)?;
62
63                    // Check depth limit
64                    if validator.get_depth() > 100 {
65                        return Err(ParseError::DepthLimitExceeded {
66                            depth: validator.get_depth(),
67                            max: 100,
68                        });
69                    }
70
71                    match event {
72                        Event::Start(ref e) => {
73                            match e.name().as_ref() {
74                                b"ReleaseList" => in_release_list = true,
75                                b"Release" if in_release_list => {
76                                    // Create a minimal release and manually validate the end event
77                                    releases.push(
78                                        self.parse_minimal_release(
79                                            &mut xml_reader,
80                                            &mut validator,
81                                        )?,
82                                    );
83                                }
84                                _ => {}
85                            }
86                        }
87                        Event::End(ref e) => {
88                            if e.name().as_ref() == b"ReleaseList" {
89                                in_release_list = false;
90                            }
91                        }
92                        Event::Eof => break,
93                        _ => {}
94                    }
95                }
96                Err(e) => {
97                    return Err(ParseError::XmlError {
98                        message: format!("XML parsing error: {}", e),
99                        location: crate::error::ErrorLocation {
100                            line: 0,
101                            column: 0,
102                            byte_offset: Some(xml_reader.buffer_position() as usize),
103                            path: "parser".to_string(),
104                        },
105                    });
106                }
107            }
108            buf.clear();
109        }
110
111        Ok(ERNMessage {
112            message_header,
113            parties,
114            resources,
115            releases,
116            deals,
117            version: self.version,
118            profile: None,
119            message_audit_trail: None,
120            extensions: None,
121            legacy_extensions: None,
122            comments: None,
123            attributes: None,
124        })
125    }
126
127    /// Build graph model from XML with namespace context
128    pub fn build_from_xml_with_context<R: BufRead + std::io::Seek>(
129        &self,
130        reader: R,
131        _context: NamespaceContext,
132    ) -> Result<ERNMessage, ParseError> {
133        self.build_from_xml_with_context_and_security(
134            reader,
135            _context,
136            &crate::parser::security::SecurityConfig::default(),
137        )
138    }
139
140    pub fn build_from_xml_with_context_and_security<R: BufRead + std::io::Seek>(
141        &self,
142        reader: R,
143        _context: NamespaceContext,
144        security_config: &crate::parser::security::SecurityConfig,
145    ) -> Result<ERNMessage, ParseError> {
146        // For now, delegate to the security-aware method
147        // In the future, this would use the namespace context for proper element resolution
148        self.build_from_xml_with_security_config(reader, security_config)
149    }
150
151    fn create_minimal_header(&self) -> Result<MessageHeader, ParseError> {
152        use chrono::Utc;
153
154        // Return a minimal valid header without parsing
155        Ok(MessageHeader {
156            message_id: format!("MSG_{:?}", self.version),
157            message_type: MessageType::NewReleaseMessage,
158            message_created_date_time: Utc::now(),
159            message_sender: MessageSender {
160                party_id: Vec::new(),
161                party_name: Vec::new(),
162                trading_name: None,
163                extensions: None,
164                attributes: None,
165                comments: None,
166            },
167            message_recipient: MessageRecipient {
168                party_id: Vec::new(),
169                party_name: Vec::new(),
170                trading_name: None,
171                extensions: None,
172                attributes: None,
173                comments: None,
174            },
175            message_control_type: None,
176            message_thread_id: Some("THREAD_001".to_string()),
177            extensions: None,
178            attributes: None,
179            comments: None,
180        })
181    }
182
183    fn parse_minimal_release<R: BufRead>(
184        &self,
185        reader: &mut Reader<R>,
186        validator: &mut crate::parser::xml_validator::XmlValidator,
187    ) -> Result<Release, ParseError> {
188        use ddex_core::models::common::LocalizedString;
189
190        let release = Release {
191            // Remove mut
192            release_reference: format!("R_{:?}", self.version),
193            release_id: Vec::new(),
194            release_title: vec![LocalizedString::new(format!(
195                "Test Release {:?}",
196                self.version
197            ))],
198            release_subtitle: None,
199            release_type: None,
200            genre: Vec::new(),
201            release_resource_reference_list: Vec::new(),
202            display_artist: Vec::new(),
203            party_list: Vec::new(),
204            release_date: Vec::new(),
205            territory_code: Vec::new(),
206            excluded_territory_code: Vec::new(),
207            extensions: None,
208            attributes: None,
209            comments: None,
210        };
211
212        // Skip to the end of the Release element, calling validator for each event
213        let mut buf = Vec::new();
214        let mut depth = 1;
215        while depth > 0 {
216            match reader.read_event_into(&mut buf) {
217                Ok(ref event) => {
218                    // Validate each event so the validator stack stays consistent
219                    validator.validate_event(event, reader)?;
220
221                    match event {
222                        Event::Start(_) => depth += 1,
223                        Event::End(_) => depth -= 1,
224                        Event::Eof => break,
225                        _ => {}
226                    }
227                }
228                Err(e) => {
229                    return Err(ParseError::XmlError {
230                        message: format!("XML parsing error in release: {}", e),
231                        location: crate::error::ErrorLocation {
232                            line: 0,
233                            column: 0,
234                            byte_offset: Some(reader.buffer_position() as usize),
235                            path: "parse_minimal_release".to_string(),
236                        },
237                    });
238                }
239            }
240            buf.clear();
241        }
242
243        Ok(release)
244    }
245}