Skip to main content

android_abx/encode/
xml.rs

1use quick_xml::XmlVersion;
2use quick_xml::events::{BytesStart, Event as XmlEvent};
3use quick_xml::reader::Reader;
4
5use super::AbxWriter;
6use crate::{AbxError, Attribute, AttributeValue, Event, InternedStr, Result};
7
8fn utf8(bytes: &[u8]) -> Result<&str> {
9    std::str::from_utf8(bytes).map_err(|_| AbxError::InvalidUtf8)
10}
11
12fn xml_err(e: impl std::fmt::Display) -> AbxError {
13    AbxError::Xml(e.to_string())
14}
15
16fn start_tag_attributes(start: &BytesStart) -> Result<Vec<Attribute>> {
17    let mut attributes = Vec::new();
18    for attr in start.attributes() {
19        let attr = attr.map_err(xml_err)?;
20        let name = utf8(attr.key.as_ref())?;
21        let value = attr
22            .normalized_value(XmlVersion::Implicit1_0)
23            .map_err(xml_err)?;
24        attributes.push(Attribute {
25            name: InternedStr::from(name),
26            value: AttributeValue::String(value.into_owned()),
27        });
28    }
29    Ok(attributes)
30}
31
32/// Encodes XML text as ABX bytes.
33///
34/// - Every attribute value is written as [`AttributeValue::String`]: no type is
35///   inferred, so `version="3"` stays a string.
36/// - The `<?xml ...?>` declaration is dropped, and
37///   [`StartDocument`](Event::StartDocument)/[`EndDocument`](Event::EndDocument)
38///   are added.
39/// - Entity references in text become [`Event::EntityReference`]; in attribute
40///   values they are decoded.
41/// - Namespaces are not processed: `android:name` is a plain name.
42///
43/// # Errors
44///
45/// Returns [`AbxError::Xml`] if the XML is malformed, or
46/// [`AbxError::ValueTooLong`] if a string is longer than 65,535 bytes.
47///
48/// # Examples
49///
50/// ```
51/// use android_abx::{AbxParser, AttributeValue, xml_to_abx};
52///
53/// let data = xml_to_abx(r#"<pkg name="com.example" version="3"/>"#)?;
54/// let version = AbxParser::new(&data)?.find_attribute("pkg", "version")?;
55/// assert_eq!(version, Some(AttributeValue::String("3".into())));
56/// # Ok::<(), android_abx::AbxError>(())
57/// ```
58pub fn xml_to_abx(xml: &str) -> Result<Vec<u8>> {
59    let mut reader = Reader::from_str(xml);
60    reader.config_mut().trim_text(false);
61
62    let mut w = AbxWriter::new(Vec::new())?;
63    w.write_event(&Event::StartDocument)?;
64
65    loop {
66        match reader.read_event().map_err(xml_err)? {
67            XmlEvent::Start(start) => {
68                let name = utf8(start.name().as_ref())?.to_string();
69                let attributes = start_tag_attributes(&start)?;
70                w.write_event(&Event::StartTag {
71                    name: name.into(),
72                    attributes,
73                })?;
74            }
75            XmlEvent::Empty(start) => {
76                let name: InternedStr = utf8(start.name().as_ref())?.into();
77                let attributes = start_tag_attributes(&start)?;
78                w.write_event(&Event::StartTag {
79                    name: name.clone(),
80                    attributes,
81                })?;
82                w.write_event(&Event::EndTag { name })?;
83            }
84            XmlEvent::End(end) => {
85                let name = utf8(end.name().as_ref())?.to_string();
86                w.write_event(&Event::EndTag { name: name.into() })?;
87            }
88            XmlEvent::Text(text) => {
89                let s = text.decode().map_err(xml_err)?;
90                w.write_event(&Event::Text(s.into_owned()))?;
91            }
92            XmlEvent::GeneralRef(bytes_ref) => {
93                let s = bytes_ref.decode().map_err(xml_err)?;
94                w.write_event(&Event::EntityReference(s.into_owned()))?;
95            }
96            XmlEvent::CData(cdata) => {
97                let s = utf8(cdata.as_ref())?.to_string();
98                w.write_event(&Event::CdataSection(s))?;
99            }
100            XmlEvent::Comment(comment) => {
101                let s = utf8(comment.as_ref())?.to_string();
102                w.write_event(&Event::Comment(s))?;
103            }
104            XmlEvent::PI(pi) => {
105                let s = utf8(pi.as_ref())?.to_string();
106                w.write_event(&Event::ProcessingInstruction(s))?;
107            }
108            XmlEvent::DocType(doctype) => {
109                let s = utf8(doctype.as_ref())?.to_string();
110                w.write_event(&Event::DocDecl(s))?;
111            }
112            XmlEvent::Decl(_) => {}
113            XmlEvent::Eof => break,
114        }
115    }
116
117    w.write_event(&Event::EndDocument)?;
118    Ok(w.into_inner())
119}