android_abx/de/mod.rs
1//! `serde` support: deserialize a single ABX element's attributes, child
2//! elements, and direct text content into a Rust struct.
3//!
4//! ## Quick start
5//!
6//! For a document that *is* one record — a config file whose root element
7//! holds the data you want — use one of the one-shot entry points, same
8//! shape as `serde_json::from_slice`/`quick_xml::de::from_str`:
9//!
10//! ```rust,ignore
11//! let pkg: Pkg = android_abx::from_file("pkg.abx")?; // from a path
12//! let pkg: Pkg = android_abx::from_slice(&bytes)?; // from an in-memory buffer
13//! let pkg: Pkg = android_abx::from_reader(reader)?; // from any std::io::Read
14//! ```
15//!
16//! None of these check the root element's tag name against `T` —
17//! deserialization is structural, not name-based.
18//!
19//! For a document whose interesting content is *repeated* elements under an
20//! outer wrapper (AOSP's `packages.xml` shape: one `<packages>` root, many
21//! `<pkg>` children), use the parser directly and name the repeated element:
22//!
23//! ```rust,ignore
24//! let mut p = android_abx::open_file("packages.abx")?;
25//! let pkgs: Vec<Pkg> = p.deserialize_all("pkg")?;
26//! ```
27//!
28//! ([`crate::AbxStreamParser::deserialize_iter`] is the lazy equivalent, for
29//! streaming without collecting a `Vec` upfront.)
30//!
31//! A struct field maps to:
32//! - an **attribute** by its own name, or `#[serde(rename = "...")]` for an
33//! attribute name that isn't a valid Rust identifier;
34//! - a **child element**, the same way — a nested struct field consumes the
35//! first matching child, a `Vec<T>` field consumes all of them, and a
36//! scalar field (`String`, `i32`, ...) consumes a *leaf* child (one with
37//! no attributes or children of its own) as its text content;
38//! - the element's own **direct text content**, via a field renamed to
39//! `$text`, the same convention `quick-xml`'s serde support uses.
40//!
41//! # Differences from `quick-xml`
42//!
43//! No `@attr` prefix: an attribute always wins over a same-named child
44//! element instead of requiring `#[serde(rename = "@name")]` to
45//! disambiguate. A field literally named `$text` loses silently to that
46//! precedence rather than erroring.
47//!
48//! No `$value` enum-of-elements mapping (`xs:choice`-style heterogeneous
49//! children) — every child element name maps to one field, not a variant
50//! selector. And no `Serialize` side — this crate only parses ABX, so
51//! deserializing into a struct is one-way.
52//!
53//! ## Internal layout
54//!
55//! `traversal` walks the event stream and builds the recursive `ElementData`
56//! tree; `element` (`ElementDeserializer`) turns one `ElementData` into a
57//! `serde` map; `value` (`ValueDeserializer`) turns one attribute, the text,
58//! or a same-named child group into a single `serde` value. None of this is
59//! public API — only the functions re-exported below are.
60
61use std::io::Read;
62
63use serde::de::{self, DeserializeOwned};
64
65use crate::{AbxError, Attribute, Result};
66
67mod element;
68mod traversal;
69mod value;
70
71pub(crate) use traversal::{find_and_consume_element, find_and_consume_root_element};
72
73/// Struct field name convention for an element's direct text content.
74pub(crate) const TEXT_FIELD: &str = "$text";
75
76impl de::Error for AbxError {
77 fn custom<T: std::fmt::Display>(msg: T) -> Self {
78 AbxError::Deserialization(msg.to_string())
79 }
80}
81
82/// Deserialize a single element's attributes (and optional text content)
83/// into `T`, honoring `#[serde(rename = "...")]`, `Option<T>` for absent
84/// attributes, and numeric/bytes coercions. This convenience entry point has
85/// no child elements to offer — it's meant for callers who already have an
86/// `Event::StartTag`'s attributes in hand. Nested-child mapping is only
87/// available through [`crate::AbxParser::deserialize_next`] and
88/// [`crate::AbxStreamParser::deserialize_next`], which build the child tree
89/// by walking the event stream.
90pub fn from_element<T: DeserializeOwned>(
91 attributes: &[Attribute],
92 text: Option<&str>,
93) -> Result<T> {
94 T::deserialize(element::ElementDeserializer {
95 attributes,
96 text,
97 children: &[],
98 })
99}
100
101/// Deserialize an entire in-memory ABX document into `T`, using its root
102/// element. The one-shot entry point for "this whole document is one
103/// struct" — no parser to construct, no element name to spell out. Matches
104/// quick-xml's `from_str`/serde_json's `from_slice`: the root's tag name is
105/// not checked against `T` at all, so name your types however you like.
106///
107/// For a document whose interesting content is *repeated* elements nested
108/// under an outer wrapper (e.g. AOSP's `packages.xml`), use
109/// [`crate::AbxParser::deserialize_all`]/`deserialize_iter` instead — this
110/// function is for when the root itself is the record you want.
111pub fn from_slice<T: DeserializeOwned>(data: &[u8]) -> Result<T> {
112 let mut parser = crate::AbxParser::new(data)?;
113 find_and_consume_root_element(&mut parser)
114}
115
116/// Streaming equivalent of [`from_slice`]: deserialize the root element of
117/// an ABX document read from any [`std::io::Read`] source.
118pub fn from_reader<R: Read, T: DeserializeOwned>(reader: R) -> Result<T> {
119 let mut parser = crate::AbxStreamParser::new(reader)?;
120 find_and_consume_root_element(&mut parser)
121}
122
123/// Open a file and deserialize its root element into `T`.
124pub fn from_file<T: DeserializeOwned>(path: impl AsRef<std::path::Path>) -> Result<T> {
125 let mut parser = crate::open_file(path)?;
126 find_and_consume_root_element(&mut parser)
127}