1#![allow(unused_variables)]
2
3use alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec};
4use core::fmt;
5#[cfg(feature = "std")]
6use std::path::Path;
7
8#[cfg(feature = "svg")]
9pub mod svg;
10
11pub use azul_core::xml::*;
12use azul_core::{dom::Dom, impl_from, styled_dom::StyledDom, window::StringPairVec};
13use azul_css::{
14 parser::{CssApiWrapper, CssParseError},
15 AzString, Css, OptionAzString, U8Vec,
16};
17use xmlparser::Tokenizer;
18
19#[cfg(feature = "xml")]
20pub fn domxml_from_str(xml: &str, component_map: &mut XmlComponentMap) -> DomXml {
21 let mut error_css = CssApiWrapper::empty();
22
23 let parsed = match parse_xml_string(&xml) {
24 Ok(parsed) => parsed,
25 Err(e) => {
26 return DomXml {
27 parsed_dom: Dom::body()
28 .with_children(vec![Dom::text(format!("{}", e))].into())
29 .style(error_css.clone()),
30 };
31 }
32 };
33
34 let parsed_dom = match str_to_dom(parsed.as_ref(), component_map, None) {
35 Ok(o) => o,
36 Err(e) => {
37 return DomXml {
38 parsed_dom: Dom::body()
39 .with_children(vec![Dom::text(format!("{}", e))].into())
40 .style(error_css.clone()),
41 };
42 }
43 };
44
45 DomXml { parsed_dom }
46}
47
48#[cfg(all(feature = "std", feature = "xml"))]
54pub fn domxml_from_file<I: AsRef<Path>>(
55 file_path: I,
56 component_map: &mut XmlComponentMap,
57) -> DomXml {
58 use std::fs;
59
60 let mut error_css = CssApiWrapper::empty();
61
62 let xml = match fs::read_to_string(file_path.as_ref()) {
63 Ok(xml) => xml,
64 Err(e) => {
65 return DomXml {
66 parsed_dom: Dom::body()
67 .with_children(
68 vec![Dom::text(format!(
69 "Error reading: \"{}\": {}",
70 file_path.as_ref().to_string_lossy(),
71 e
72 ))]
73 .into(),
74 )
75 .style(error_css.clone()),
76 };
77 }
78 };
79
80 domxml_from_str(&xml, component_map)
81}
82
83#[cfg(feature = "xml")]
103pub fn parse_xml_string(xml: &str) -> Result<Vec<XmlNode>, XmlError> {
104 use xmlparser::{ElementEnd::*, Token::*, Tokenizer};
105
106 use self::XmlParseError::*;
107
108 let mut root_node = XmlNode::default();
109
110 let mut xml = xml.trim();
112 if xml.starts_with("<?") {
113 let pos = xml
114 .find("?>")
115 .ok_or(XmlError::MalformedHierarchy("<?xml".into(), "?>".into()))?;
116 xml = &xml[(pos + 2)..];
117 }
118
119 let mut xml = xml.trim();
121 if xml.starts_with("<!") {
122 let pos = xml
123 .find(">")
124 .ok_or(XmlError::MalformedHierarchy("<!doctype".into(), ">".into()))?;
125 xml = &xml[(pos + 1)..];
126 }
127
128 let tokenizer = Tokenizer::from_fragment(xml, 0..xml.len());
129
130 let mut current_hierarchy: Vec<usize> = Vec::new();
134
135 for token in tokenizer {
136 let token = token.map_err(|e| XmlError::ParserError(translate_xmlparser_error(e)))?;
137 match token {
138 ElementStart { local, .. } => {
139 if let Some(current_parent) = get_item(¤t_hierarchy, &mut root_node) {
140 let children_len = current_parent.children.len();
141 current_parent.children.push(XmlNode {
142 node_type: local.to_string().into(),
143 attributes: StringPairVec::new(),
144 children: Vec::new().into(),
145 text: None.into(),
146 });
147 current_hierarchy.push(children_len);
148 }
149 }
150 ElementEnd { end: Empty, .. } => {
151 current_hierarchy.pop();
152 }
153 ElementEnd {
154 end: Close(_, close_value),
155 ..
156 } => {
157 let i = get_item(¤t_hierarchy, &mut root_node);
158 if let Some(last) = i {
159 if last.node_type.as_str() != close_value.as_str() {
160 return Err(XmlError::MalformedHierarchy(
161 close_value.to_string().into(),
162 last.node_type.clone(),
163 ));
164 }
165 }
166 current_hierarchy.pop();
167 }
168 Attribute { local, value, .. } => {
169 if let Some(last) = get_item(¤t_hierarchy, &mut root_node) {
170 last.attributes.push(azul_core::window::AzStringPair {
172 key: local.to_string().into(),
173 value: value.as_str().to_string().into(),
174 });
175 }
176 }
177 Text { text } => {
178 let text = text.trim();
179 if !text.is_empty() {
180 if let Some(last) = get_item(¤t_hierarchy, &mut root_node) {
181 if let Some(s) = last.text.as_mut() {
182 let mut newstr = s.as_str().to_string();
183 newstr.push_str(text);
184 *s = newstr.into();
185 }
186 if last.text.is_none() {
187 last.text = Some(text.to_string().into()).into();
188 }
189 }
190 }
191 }
192 _ => {}
193 }
194 }
195
196 Ok(root_node.children.into())
197}
198
199#[cfg(feature = "xml")]
200pub fn parse_xml(s: &str) -> Result<Xml, XmlError> {
201 Ok(Xml {
202 root: parse_xml_string(s)?.into(),
203 })
204}
205
206#[cfg(not(feature = "xml"))]
207pub fn parse_xml(s: &str) -> Result<Xml, XmlError> {
208 Err(XmlError::NoParserAvailable)
209}
210
211#[cfg(feature = "xml")]
214pub fn translate_roxmltree_expandedname<'a, 'b>(
215 e: roxmltree::ExpandedName<'a, 'b>,
216) -> XmlQualifiedName {
217 let ns: Option<AzString> = e.namespace().map(|e| e.to_string().into());
218 XmlQualifiedName {
219 name: e.name().to_string().into(),
220 namespace: ns.into(),
221 }
222}
223
224#[cfg(feature = "xml")]
225fn translate_roxmltree_attribute<'a>(e: roxmltree::Attribute<'a>) -> XmlQualifiedName {
226 XmlQualifiedName {
227 name: e.name().to_string().into(),
228 namespace: e.namespace().map(|e| e.to_string().into()).into(),
229 }
230}
231
232#[cfg(feature = "xml")]
233fn translate_xmlparser_streamerror(e: xmlparser::StreamError) -> XmlStreamError {
234 match e {
235 xmlparser::StreamError::UnexpectedEndOfStream => XmlStreamError::UnexpectedEndOfStream,
236 xmlparser::StreamError::InvalidName => XmlStreamError::InvalidName,
237 xmlparser::StreamError::InvalidReference => XmlStreamError::InvalidReference,
238 xmlparser::StreamError::InvalidExternalID => XmlStreamError::InvalidExternalID,
239 xmlparser::StreamError::InvalidCommentData => XmlStreamError::InvalidCommentData,
240 xmlparser::StreamError::InvalidCommentEnd => XmlStreamError::InvalidCommentEnd,
241 xmlparser::StreamError::InvalidCharacterData => XmlStreamError::InvalidCharacterData,
242 xmlparser::StreamError::NonXmlChar(c, tp) => XmlStreamError::NonXmlChar(NonXmlCharError {
243 ch: c.into(),
244 pos: translate_roxml_textpos(tp),
245 }),
246 xmlparser::StreamError::InvalidChar(a, b, tp) => {
247 XmlStreamError::InvalidChar(InvalidCharError {
248 expected: a,
249 got: b,
250 pos: translate_roxml_textpos(tp),
251 })
252 }
253 xmlparser::StreamError::InvalidCharMultiple(a, b, tp) => {
254 XmlStreamError::InvalidCharMultiple(InvalidCharMultipleError {
255 expected: a,
256 got: b.to_vec().into(),
257 pos: translate_roxml_textpos(tp),
258 })
259 }
260 xmlparser::StreamError::InvalidQuote(a, tp) => {
261 XmlStreamError::InvalidQuote(InvalidQuoteError {
262 got: a.into(),
263 pos: translate_roxml_textpos(tp),
264 })
265 }
266 xmlparser::StreamError::InvalidSpace(a, tp) => {
267 XmlStreamError::InvalidSpace(InvalidSpaceError {
268 got: a.into(),
269 pos: translate_roxml_textpos(tp),
270 })
271 }
272 xmlparser::StreamError::InvalidString(a, tp) => {
273 XmlStreamError::InvalidString(InvalidStringError {
274 got: a.to_string().into(),
275 pos: translate_roxml_textpos(tp),
276 })
277 }
278 }
279}
280
281#[cfg(feature = "xml")]
282fn translate_xmlparser_error(e: xmlparser::Error) -> XmlParseError {
283 match e {
284 xmlparser::Error::InvalidDeclaration(se, tp) => {
285 XmlParseError::InvalidDeclaration(XmlTextError {
286 stream_error: translate_xmlparser_streamerror(se),
287 pos: translate_roxml_textpos(tp),
288 })
289 }
290 xmlparser::Error::InvalidComment(se, tp) => XmlParseError::InvalidComment(XmlTextError {
291 stream_error: translate_xmlparser_streamerror(se),
292 pos: translate_roxml_textpos(tp),
293 }),
294 xmlparser::Error::InvalidPI(se, tp) => XmlParseError::InvalidPI(XmlTextError {
295 stream_error: translate_xmlparser_streamerror(se),
296 pos: translate_roxml_textpos(tp),
297 }),
298 xmlparser::Error::InvalidDoctype(se, tp) => XmlParseError::InvalidDoctype(XmlTextError {
299 stream_error: translate_xmlparser_streamerror(se),
300 pos: translate_roxml_textpos(tp),
301 }),
302 xmlparser::Error::InvalidEntity(se, tp) => XmlParseError::InvalidEntity(XmlTextError {
303 stream_error: translate_xmlparser_streamerror(se),
304 pos: translate_roxml_textpos(tp),
305 }),
306 xmlparser::Error::InvalidElement(se, tp) => XmlParseError::InvalidElement(XmlTextError {
307 stream_error: translate_xmlparser_streamerror(se),
308 pos: translate_roxml_textpos(tp),
309 }),
310 xmlparser::Error::InvalidAttribute(se, tp) => {
311 XmlParseError::InvalidAttribute(XmlTextError {
312 stream_error: translate_xmlparser_streamerror(se),
313 pos: translate_roxml_textpos(tp),
314 })
315 }
316 xmlparser::Error::InvalidCdata(se, tp) => XmlParseError::InvalidCdata(XmlTextError {
317 stream_error: translate_xmlparser_streamerror(se),
318 pos: translate_roxml_textpos(tp),
319 }),
320 xmlparser::Error::InvalidCharData(se, tp) => XmlParseError::InvalidCharData(XmlTextError {
321 stream_error: translate_xmlparser_streamerror(se),
322 pos: translate_roxml_textpos(tp),
323 }),
324 xmlparser::Error::UnknownToken(tp) => {
325 XmlParseError::UnknownToken(translate_roxml_textpos(tp))
326 }
327 }
328}
329
330#[cfg(feature = "xml")]
331pub(crate) fn translate_roxmltree_error(e: roxmltree::Error) -> XmlError {
332 match e {
333 roxmltree::Error::InvalidXmlPrefixUri(s) => {
334 XmlError::InvalidXmlPrefixUri(translate_roxml_textpos(s))
335 }
336 roxmltree::Error::UnexpectedXmlUri(s) => {
337 XmlError::UnexpectedXmlUri(translate_roxml_textpos(s))
338 }
339 roxmltree::Error::UnexpectedXmlnsUri(s) => {
340 XmlError::UnexpectedXmlnsUri(translate_roxml_textpos(s))
341 }
342 roxmltree::Error::InvalidElementNamePrefix(s) => {
343 XmlError::InvalidElementNamePrefix(translate_roxml_textpos(s))
344 }
345 roxmltree::Error::DuplicatedNamespace(s, tp) => {
346 XmlError::DuplicatedNamespace(DuplicatedNamespaceError {
347 ns: s.into(),
348 pos: translate_roxml_textpos(tp),
349 })
350 }
351 roxmltree::Error::UnknownNamespace(s, tp) => {
352 XmlError::UnknownNamespace(UnknownNamespaceError {
353 ns: s.into(),
354 pos: translate_roxml_textpos(tp),
355 })
356 }
357 roxmltree::Error::UnexpectedCloseTag {
358 expected,
359 actual,
360 pos,
361 } => XmlError::UnexpectedCloseTag(UnexpectedCloseTagError {
362 expected: expected.into(),
363 actual: actual.into(),
364 pos: translate_roxml_textpos(pos),
365 }),
366 roxmltree::Error::UnexpectedEntityCloseTag(s) => {
367 XmlError::UnexpectedEntityCloseTag(translate_roxml_textpos(s))
368 }
369 roxmltree::Error::UnknownEntityReference(s, tp) => {
370 XmlError::UnknownEntityReference(UnknownEntityReferenceError {
371 entity: s.into(),
372 pos: translate_roxml_textpos(tp),
373 })
374 }
375 roxmltree::Error::MalformedEntityReference(s) => {
376 XmlError::MalformedEntityReference(translate_roxml_textpos(s))
377 }
378 roxmltree::Error::EntityReferenceLoop(s) => {
379 XmlError::EntityReferenceLoop(translate_roxml_textpos(s))
380 }
381 roxmltree::Error::InvalidAttributeValue(s) => {
382 XmlError::InvalidAttributeValue(translate_roxml_textpos(s))
383 }
384 roxmltree::Error::DuplicatedAttribute(s, tp) => {
385 XmlError::DuplicatedAttribute(DuplicatedAttributeError {
386 attribute: s.into(),
387 pos: translate_roxml_textpos(tp),
388 })
389 }
390 roxmltree::Error::NoRootNode => XmlError::NoRootNode,
391 roxmltree::Error::SizeLimit => XmlError::SizeLimit,
392 roxmltree::Error::DtdDetected => XmlError::DtdDetected,
393 roxmltree::Error::ParserError(s) => XmlError::ParserError(translate_xmlparser_error(s)),
394 }
395}
396
397#[cfg(feature = "xml")]
398#[inline(always)]
399const fn translate_roxml_textpos(o: roxmltree::TextPos) -> XmlTextPos {
400 XmlTextPos {
401 row: o.row,
402 col: o.col,
403 }
404}