android_abx/lib.rs
1//! Parser and encoder for Android Binary XML (ABX).
2//!
3//! ABX is the binary XML format written by AOSP's `BinaryXmlSerializer` and read
4//! by `BinaryXmlPullParser`. Android uses it for platform files such as
5//! `/data/system/packages.xml`. It is not AXML, the format of compiled APK
6//! resources such as `AndroidManifest.xml`, which this crate cannot read.
7//!
8//! # Parsing
9//!
10//! [`AbxParser`] reads a document from a byte slice and [`AbxStreamParser`] from
11//! any [`Read`](std::io::Read). Both have the same methods and yield the same
12//! [`Event`]s.
13//!
14//! ```
15//! use android_abx::{AbxParser, Event};
16//!
17//! # let data = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/simple_pkg.abx"));
18//! let mut parser = AbxParser::new(data)?;
19//! while let Some(event) = parser.next_event()? {
20//! if let Event::StartTag { name, attributes } = event {
21//! println!("<{name}> has {} attributes", attributes.len());
22//! }
23//! }
24//! # Ok::<(), android_abx::AbxError>(())
25//! ```
26//!
27//! To convert a whole document to XML text, use [`abx_to_xml`]:
28//!
29//! ```
30//! # let data = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/simple_pkg.abx"));
31//! let xml = android_abx::abx_to_xml(data)?;
32//! assert_eq!(
33//! xml,
34//! r#"<?xml version="1.0" encoding="UTF-8"?><pkg name="com.example.chat" version="3" flags="1"></pkg>"#,
35//! );
36//! # Ok::<(), android_abx::AbxError>(())
37//! ```
38//!
39//! # Encoding
40//!
41//! [`AbxWriter`] and [`events_to_abx`] encode [`Event`]s back to ABX. With the
42//! `xml` feature, `xml_to_abx` encodes XML text.
43//!
44//! ```
45//! use android_abx::{AbxParser, events_to_abx};
46//!
47//! # let data = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/simple_pkg.abx"));
48//! let events = AbxParser::new(data)?.collect_events()?;
49//! assert_eq!(events_to_abx(&events)?, data);
50//! # Ok::<(), android_abx::AbxError>(())
51//! ```
52//!
53//! # Deserializing with serde
54//!
55//! With the `serde` feature, an element can be deserialized into any type
56//! that implements `serde::Deserialize`. Struct fields are filled from:
57//!
58//! - attributes, by name. Use `#[serde(rename = "...")]` for names that are not
59//! Rust identifiers;
60//! - child elements, by tag name. A `Vec<T>` field takes every matching child,
61//! any other field takes the first one. A child with only text can fill a
62//! scalar field such as `String` or `u32`;
63//! - the element's text, through a field renamed to `"$text"`. Only text events
64//! are collected: entity references such as `&` are dropped.
65//!
66//! When an attribute and a child element have the same name, the attribute is
67//! used. An `Option` field is `None` when the attribute or child is missing, or
68//! when the attribute has a null value. Enums with unit variants are matched by
69//! name against a string value.
70//!
71//! Use `from_slice`, `from_reader` or `from_file` when the root element is the
72//! record you want. For repeated elements under a root, such as `<pkg>` entries
73//! in `packages.xml`, use `AbxParser::deserialize_all` or
74//! `AbxStreamParser::deserialize_iter`.
75//!
76//! # Feature flags
77//!
78//! - `serde`: serde deserialization.
79//! - `xml`: `xml_to_abx`, to encode XML text.
80
81#![warn(missing_docs)]
82#![cfg_attr(docsrs, feature(doc_cfg))]
83
84mod error;
85pub use error::{AbxError, Result};
86
87mod wire;
88pub use wire::MAGIC;
89pub(crate) use wire::{
90 CMD_ATTRIBUTE, CMD_CDSECT, CMD_COMMENT, CMD_DOCDECL, CMD_END_DOCUMENT, CMD_END_TAG,
91 CMD_ENTITY_REF, CMD_IGNORABLE_WHITESPACE, CMD_PROCESSING_INSTRUCTION, CMD_START_DOCUMENT,
92 CMD_START_TAG, CMD_TEXT, INTERNED_NEW, TYPE_BOOLEAN_FALSE, TYPE_BOOLEAN_TRUE,
93 TYPE_BYTES_BASE64, TYPE_BYTES_HEX, TYPE_DOUBLE, TYPE_FLOAT, TYPE_INT, TYPE_INT_HEX, TYPE_LONG,
94 TYPE_LONG_HEX, TYPE_NULL, TYPE_STRING, TYPE_STRING_INTERNED,
95};
96
97mod event;
98pub(crate) use event::render_event;
99pub use event::{Attribute, AttributeValue, Event, InternedStr};
100
101mod decode;
102pub use decode::stream;
103pub use decode::stream::AbxStreamParser;
104pub use decode::{AbxParser, AbxParserOwned};
105
106mod encode;
107#[cfg(feature = "xml")]
108pub use encode::xml_to_abx;
109pub use encode::{AbxWriter, events_to_abx};
110
111#[cfg(feature = "serde")]
112mod de;
113#[cfg(feature = "serde")]
114pub use de::{from_element, from_file, from_reader, from_slice};
115
116/// Converts an ABX document to an XML string.
117///
118/// Shorthand for [`AbxParser::new`] followed by [`AbxParser::to_xml`].
119///
120/// # Errors
121///
122/// Returns an error if the input is truncated or malformed (unknown token,
123/// invalid interned-string index, invalid UTF-8).
124///
125/// # Examples
126///
127/// ```
128/// # let data = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/simple_pkg.abx"));
129/// let xml = android_abx::abx_to_xml(data)?;
130/// assert!(xml.starts_with(r#"<?xml version="1.0" encoding="UTF-8"?><pkg "#));
131/// # Ok::<(), android_abx::AbxError>(())
132/// ```
133pub fn abx_to_xml(data: &[u8]) -> Result<String> {
134 AbxParser::new(data)?.to_xml()
135}
136
137/// Parses an ABX document into a list of events.
138///
139/// Shorthand for [`AbxParser::new`] followed by [`AbxParser::collect_events`].
140///
141/// # Errors
142///
143/// Returns an error if the input is truncated or malformed (unknown token,
144/// invalid interned-string index, invalid UTF-8).
145///
146/// # Examples
147///
148/// ```
149/// use android_abx::Event;
150///
151/// # let data = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/simple_pkg.abx"));
152/// let events = android_abx::abx_events(data)?;
153/// assert_eq!(events.first(), Some(&Event::StartDocument));
154/// assert_eq!(events.last(), Some(&Event::EndDocument));
155/// # Ok::<(), android_abx::AbxError>(())
156/// ```
157pub fn abx_events(data: &[u8]) -> Result<Vec<Event>> {
158 AbxParser::new(data)?.collect_events()
159}
160
161/// Opens a file and returns an [`AbxStreamParser`] over it.
162///
163/// # Errors
164///
165/// Returns [`AbxError::Io`] if the file cannot be opened or read, and
166/// [`AbxError::InvalidMagic`] or [`AbxError::UnexpectedEof`] if it does not start
167/// with [`MAGIC`].
168///
169/// # Examples
170///
171/// ```no_run
172/// let xml = android_abx::open_file("/data/system/packages.xml")?.to_xml()?;
173/// # Ok::<(), android_abx::AbxError>(())
174/// ```
175pub fn open_file(
176 path: impl AsRef<std::path::Path>,
177) -> Result<AbxStreamParser<std::io::BufReader<std::fs::File>>> {
178 let f = std::fs::File::open(path)?;
179 AbxStreamParser::new(std::io::BufReader::new(f))
180}