android_xml_converter/
lib.rs1use std::io;
2use thiserror::Error;
3
4#[derive(Error, Debug)]
5pub enum ConversionError {
6 #[error("IO error: {0}")]
7 Io(#[from] io::Error),
8
9 #[error(
10 "Invalid ABX file format - magic header mismatch. Expected: {expected:02X?}, got: {actual:02X?}"
11 )]
12 InvalidMagicHeader { expected: [u8; 4], actual: [u8; 4] },
13
14 #[error("Failed to read {0} from stream")]
15 ReadError(String),
16
17 #[error("Invalid interned string index: {0}")]
18 InvalidInternedStringIndex(u16),
19
20 #[error("Unknown attribute type: {0}")]
21 UnknownAttributeType(u8),
22
23 #[error("Parse error: {0}")]
24 ParseError(String),
25
26 #[error("XML parsing failed: {0}")]
27 XmlParsing(String),
28
29 #[error("String too long: {0} bytes (max: {1})")]
30 StringTooLong(usize, usize),
31
32 #[error("Binary data too long: {0} bytes (max: {1})")]
33 BinaryDataTooLong(usize, usize),
34
35 #[error("Invalid hex string")]
36 InvalidHex,
37
38 #[error("Invalid base64 string")]
39 InvalidBase64,
40
41 #[error("UTF-8 conversion error: {0}")]
42 Utf8Error(#[from] std::str::Utf8Error),
43}
44
45impl From<quick_xml::Error> for ConversionError {
47 fn from(err: quick_xml::Error) -> Self {
48 ConversionError::XmlParsing(err.to_string())
49 }
50}
51
52impl From<quick_xml::events::attributes::AttrError> for ConversionError {
53 fn from(err: quick_xml::events::attributes::AttrError) -> Self {
54 ConversionError::XmlParsing(err.to_string())
55 }
56}
57
58pub type Result<T> = std::result::Result<T, ConversionError>;
59
60pub const PROTOCOL_MAGIC_VERSION_0: [u8; 4] = [0x41, 0x42, 0x58, 0x00];
66
67pub const START_DOCUMENT: u8 = 0;
69pub const END_DOCUMENT: u8 = 1;
70pub const START_TAG: u8 = 2;
71pub const END_TAG: u8 = 3;
72pub const TEXT: u8 = 4;
73pub const CDSECT: u8 = 5;
74pub const ENTITY_REF: u8 = 6;
75pub const IGNORABLE_WHITESPACE: u8 = 7;
76pub const PROCESSING_INSTRUCTION: u8 = 8;
77pub const COMMENT: u8 = 9;
78pub const DOCDECL: u8 = 10;
79pub const ATTRIBUTE: u8 = 15;
80
81pub const TYPE_NULL: u8 = 1 << 4;
83pub const TYPE_STRING: u8 = 2 << 4;
84pub const TYPE_STRING_INTERNED: u8 = 3 << 4;
85pub const TYPE_BYTES_HEX: u8 = 4 << 4;
86pub const TYPE_BYTES_BASE64: u8 = 5 << 4;
87pub const TYPE_INT: u8 = 6 << 4;
88pub const TYPE_INT_HEX: u8 = 7 << 4;
89pub const TYPE_LONG: u8 = 8 << 4;
90pub const TYPE_LONG_HEX: u8 = 9 << 4;
91pub const TYPE_FLOAT: u8 = 10 << 4;
92pub const TYPE_DOUBLE: u8 = 11 << 4;
93pub const TYPE_BOOLEAN_TRUE: u8 = 12 << 4;
94pub const TYPE_BOOLEAN_FALSE: u8 = 13 << 4;
95
96pub const MAX_UNSIGNED_SHORT: u16 = 65535;
102
103pub const INTERNED_STRING_NEW_MARKER: u16 = 0xFFFF;
105
106pub const INITIAL_STRING_POOL_CAPACITY: usize = 64;
108
109pub const INITIAL_EVENT_BUFFER_CAPACITY: usize = 8192;
111
112#[inline]
113pub fn encode_xml_entities(text: &str) -> std::borrow::Cow<'_, str> {
114 if !text
116 .bytes()
117 .any(|b| matches!(b, b'&' | b'<' | b'>' | b'"' | b'\''))
118 {
119 return std::borrow::Cow::Borrowed(text);
120 }
121
122 let mut result = String::with_capacity(text.len() + 16);
123 for ch in text.chars() {
124 match ch {
125 '&' => result.push_str("&"),
126 '<' => result.push_str("<"),
127 '>' => result.push_str(">"),
128 '"' => result.push_str("""),
129 '\'' => result.push_str("'"),
130 _ => result.push(ch),
131 }
132 }
133 std::borrow::Cow::Owned(result)
134}
135
136#[inline]
138pub fn show_warning(feature: &str, details: Option<&str>) {
139 eprintln!("WARNING: {} is not supported and might be lost.", feature);
140 if let Some(details) = details {
141 eprintln!(" {}", details);
142 }
143}
144
145pub mod type_detection {
150 #[inline]
152 pub fn is_boolean(s: &str) -> bool {
153 matches!(s, "true" | "false")
154 }
155
156 #[inline]
158 pub fn is_whitespace_only(s: &str) -> bool {
159 s.bytes().all(|b| matches!(b, b' ' | b'\t' | b'\n' | b'\r'))
160 }
161}