lib3mf_core/parser/
xml_parser.rs1use crate::error::{Lib3mfError, Result};
2use lexical_core;
3use quick_xml::events::{BytesStart, Event};
4use quick_xml::reader::Reader;
5use std::borrow::Cow;
6use std::io::BufRead;
7
8pub struct XmlParser<R: BufRead> {
10 pub reader: Reader<R>,
12 pub buf: Vec<u8>,
14}
15
16impl<R: BufRead> XmlParser<R> {
17 pub fn new(reader: R) -> Self {
19 let mut reader = Reader::from_reader(reader);
20 reader.config_mut().trim_text(true);
21 reader.config_mut().expand_empty_elements = true;
22 Self {
23 reader,
24 buf: Vec::new(),
25 }
26 }
27
28 pub fn read_next_event(&mut self) -> Result<Event<'_>> {
30 self.buf.clear();
31 self.reader
32 .read_event_into(&mut self.buf)
33 .map_err(|e| Lib3mfError::Validation(e.to_string()))
34 }
35
36 pub fn read_text_content(&mut self) -> Result<String> {
38 let mut text = String::new();
39 let mut depth = 0;
40
41 loop {
42 match self.read_next_event()? {
43 Event::Text(e) => text.push_str(&String::from_utf8_lossy(e.as_ref())),
44 Event::CData(e) => text.push_str(&String::from_utf8_lossy(e.into_inner().as_ref())),
45 Event::Start(_) => depth += 1,
46 Event::End(_) => {
47 if depth > 0 {
48 depth -= 1;
49 } else {
50 return Ok(text);
51 }
52 }
53 Event::Eof => {
54 return Err(Lib3mfError::Validation(
55 "Unexpected EOF in text content".to_string(),
56 ));
57 }
58 _ => {}
59 }
60 }
61 }
62
63 pub fn read_to_end(&mut self, end: &[u8]) -> Result<()> {
65 self.reader
67 .read_to_end_into(quick_xml::name::QName(end), &mut self.buf)
68 .map_err(|e| Lib3mfError::Validation(e.to_string()))?;
69 Ok(())
70 }
71}
72
73pub fn get_attribute<'a>(e: &'a BytesStart, name: &[u8]) -> Option<Cow<'a, str>> {
77 e.try_get_attribute(name).ok().flatten().map(|a| {
78 a.unescape_value()
79 .unwrap_or_else(|_| String::from_utf8_lossy(&a.value).into_owned().into())
80 })
81}
82
83pub fn get_attribute_f32(e: &BytesStart, name: &[u8]) -> Result<f32> {
85 let attr = e.try_get_attribute(name).ok().flatten().ok_or_else(|| {
86 Lib3mfError::Validation(format!(
87 "Missing attribute: {}",
88 String::from_utf8_lossy(name)
89 ))
90 })?;
91 lexical_core::parse::<f32>(attr.value.as_ref()).map_err(|_| {
92 Lib3mfError::Validation(format!(
93 "Invalid float for attribute {}: {}",
94 String::from_utf8_lossy(name),
95 String::from_utf8_lossy(&attr.value)
96 ))
97 })
98}
99
100pub fn get_attribute_u32(e: &BytesStart, name: &[u8]) -> Result<u32> {
102 let attr = e.try_get_attribute(name).ok().flatten().ok_or_else(|| {
103 Lib3mfError::Validation(format!(
104 "Missing attribute: {}",
105 String::from_utf8_lossy(name)
106 ))
107 })?;
108 lexical_core::parse::<u32>(attr.value.as_ref()).map_err(|_| {
109 Lib3mfError::Validation(format!(
110 "Invalid integer for attribute {}: {}",
111 String::from_utf8_lossy(name),
112 String::from_utf8_lossy(&attr.value)
113 ))
114 })
115}
116
117pub fn get_attribute_u32_opt(e: &BytesStart, name: &[u8]) -> Result<Option<u32>> {
119 match e.try_get_attribute(name).ok().flatten() {
120 Some(attr) => lexical_core::parse::<u32>(attr.value.as_ref())
121 .map(Some)
122 .map_err(|_| {
123 Lib3mfError::Validation(format!(
124 "Invalid integer: {}",
125 String::from_utf8_lossy(&attr.value)
126 ))
127 }),
128 None => Ok(None),
129 }
130}
131
132pub fn get_attribute_uuid(e: &BytesStart) -> Result<Option<uuid::Uuid>> {
134 let val = get_attribute(e, b"uuid").or_else(|| get_attribute(e, b"p:uuid"));
136
137 match val {
138 Some(s) => uuid::Uuid::parse_str(&s)
139 .map(Some)
140 .map_err(|_| Lib3mfError::Validation(format!("Invalid UUID: {}", s))),
141 None => Ok(None),
142 }
143}