wacore_binary/
attrs.rs

1use std::borrow::Cow;
2use std::str::FromStr;
3
4use crate::error::{BinaryError, Result};
5use crate::jid::Jid;
6use crate::node::{Attrs, Node, NodeRef, ValueRef};
7
8pub struct AttrParser<'a> {
9    pub attrs: &'a Attrs,
10    pub errors: Vec<BinaryError>,
11}
12
13pub struct AttrParserRef<'a> {
14    pub attrs: &'a [(Cow<'a, str>, ValueRef<'a>)],
15    pub errors: Vec<BinaryError>,
16}
17
18impl<'a> AttrParserRef<'a> {
19    pub fn new(node: &'a NodeRef<'a>) -> Self {
20        Self {
21            attrs: node.attrs.as_slice(),
22            errors: Vec::new(),
23        }
24    }
25
26    pub fn ok(&self) -> bool {
27        self.errors.is_empty()
28    }
29
30    pub fn finish(&self) -> Result<()> {
31        if self.ok() {
32            Ok(())
33        } else {
34            Err(BinaryError::AttrList(self.errors.clone()))
35        }
36    }
37
38    fn get_raw(&mut self, key: &str, require: bool) -> Option<&'a ValueRef<'a>> {
39        let val = self
40            .attrs
41            .iter()
42            .find(|(k, _)| k.as_ref() == key)
43            .map(|(_, v)| v);
44
45        if require && val.is_none() {
46            self.errors.push(BinaryError::AttrParse(format!(
47                "Required attribute '{key}' not found"
48            )));
49        }
50
51        val
52    }
53
54    /// Get string from the value.
55    /// For JID values, this returns None - use optional_jid instead.
56    pub fn optional_string(&mut self, key: &str) -> Option<&'a str> {
57        self.get_raw(key, false).and_then(|v| v.as_str())
58    }
59
60    pub fn string(&mut self, key: &str) -> String {
61        self.get_raw(key, true)
62            .map(|v| v.to_string_cow().into_owned())
63            .unwrap_or_default()
64    }
65
66    /// Get JID from the value.
67    /// If the value is a JidRef, returns it directly without parsing (zero allocation).
68    /// If the value is a string, parses it as a JID.
69    pub fn optional_jid(&mut self, key: &str) -> Option<Jid> {
70        self.get_raw(key, false).and_then(|v| match v.to_jid() {
71            Some(jid) => Some(jid),
72            None => {
73                // to_jid() only returns None if it's a String that failed to parse
74                if let ValueRef::String(s) = v {
75                    self.errors
76                        .push(BinaryError::AttrParse(format!("Invalid JID: {s}")));
77                }
78                None
79            }
80        })
81    }
82
83    pub fn jid(&mut self, key: &str) -> Jid {
84        self.get_raw(key, true);
85        self.optional_jid(key).unwrap_or_default()
86    }
87
88    pub fn non_ad_jid(&mut self, key: &str) -> Jid {
89        self.jid(key).to_non_ad()
90    }
91
92    fn get_string_value(&mut self, key: &str, require: bool) -> Option<Cow<'a, str>> {
93        self.get_raw(key, require).map(|v| v.to_string_cow())
94    }
95
96    fn get_bool(&mut self, key: &str, require: bool) -> Option<bool> {
97        self.get_string_value(key, require)
98            .and_then(|s| match s.parse::<bool>() {
99                Ok(val) => Some(val),
100                Err(e) => {
101                    self.errors.push(BinaryError::AttrParse(format!(
102                        "Failed to parse bool from '{s}' for key '{key}': {e}"
103                    )));
104                    None
105                }
106            })
107    }
108
109    pub fn optional_bool(&mut self, key: &str) -> bool {
110        self.get_bool(key, false).unwrap_or(false)
111    }
112
113    pub fn bool(&mut self, key: &str) -> bool {
114        self.get_bool(key, true).unwrap_or(false)
115    }
116
117    pub fn optional_u64(&mut self, key: &str) -> Option<u64> {
118        self.get_string_value(key, false)
119            .and_then(|s| match s.parse::<u64>() {
120                Ok(val) => Some(val),
121                Err(e) => {
122                    self.errors.push(BinaryError::AttrParse(format!(
123                        "Failed to parse u64 from '{s}' for key '{key}': {e}"
124                    )));
125                    None
126                }
127            })
128    }
129
130    pub fn unix_time(&mut self, key: &str) -> i64 {
131        self.get_raw(key, true);
132        self.optional_unix_time(key).unwrap_or_default()
133    }
134
135    pub fn optional_unix_time(&mut self, key: &str) -> Option<i64> {
136        self.get_i64(key, false)
137    }
138
139    pub fn unix_milli(&mut self, key: &str) -> i64 {
140        self.get_raw(key, true);
141        self.optional_unix_milli(key).unwrap_or_default()
142    }
143
144    pub fn optional_unix_milli(&mut self, key: &str) -> Option<i64> {
145        self.get_i64(key, false)
146    }
147
148    fn get_i64(&mut self, key: &str, require: bool) -> Option<i64> {
149        self.get_string_value(key, require)
150            .and_then(|s| match s.parse::<i64>() {
151                Ok(val) => Some(val),
152                Err(e) => {
153                    self.errors.push(BinaryError::AttrParse(format!(
154                        "Failed to parse i64 from '{s}' for key '{key}': {e}"
155                    )));
156                    None
157                }
158            })
159    }
160}
161
162impl<'a> AttrParser<'a> {
163    pub fn new(node: &'a Node) -> Self {
164        Self {
165            attrs: &node.attrs,
166            errors: Vec::new(),
167        }
168    }
169
170    pub fn ok(&self) -> bool {
171        self.errors.is_empty()
172    }
173
174    pub fn finish(&self) -> Result<()> {
175        if self.ok() {
176            Ok(())
177        } else {
178            Err(BinaryError::AttrList(self.errors.clone()))
179        }
180    }
181
182    fn get_raw(&mut self, key: &str, require: bool) -> Option<&'a String> {
183        let val = self.attrs.get(key);
184        if require && val.is_none() {
185            self.errors.push(BinaryError::AttrParse(format!(
186                "Required attribute '{key}' not found"
187            )));
188        }
189        val
190    }
191
192    // --- String ---
193    pub fn optional_string(&mut self, key: &str) -> Option<&'a str> {
194        self.get_raw(key, false).map(|s| s.as_str())
195    }
196
197    pub fn string(&mut self, key: &str) -> String {
198        self.get_raw(key, true).cloned().unwrap_or_default()
199    }
200
201    // --- JID ---
202    pub fn optional_jid(&mut self, key: &str) -> Option<Jid> {
203        self.get_raw(key, false)
204            .and_then(|s| match Jid::from_str(s) {
205                Ok(jid) => Some(jid),
206                Err(e) => {
207                    self.errors.push(BinaryError::from(e));
208                    None
209                }
210            })
211    }
212
213    pub fn jid(&mut self, key: &str) -> Jid {
214        self.get_raw(key, true); // Push "not found" error if needed.
215        self.optional_jid(key).unwrap_or_default()
216    }
217
218    pub fn non_ad_jid(&mut self, key: &str) -> Jid {
219        self.jid(key).to_non_ad()
220    }
221
222    // --- Boolean ---
223    fn get_bool(&mut self, key: &str, require: bool) -> Option<bool> {
224        self.get_raw(key, require)
225            .and_then(|s| match s.parse::<bool>() {
226                Ok(val) => Some(val),
227                Err(e) => {
228                    self.errors.push(BinaryError::AttrParse(format!(
229                        "Failed to parse bool from '{s}' for key '{key}': {e}"
230                    )));
231                    None
232                }
233            })
234    }
235
236    pub fn optional_bool(&mut self, key: &str) -> bool {
237        self.get_bool(key, false).unwrap_or(false)
238    }
239
240    pub fn bool(&mut self, key: &str) -> bool {
241        self.get_bool(key, true).unwrap_or(false)
242    }
243
244    // --- u64 ---
245    pub fn optional_u64(&mut self, key: &str) -> Option<u64> {
246        self.get_raw(key, false)
247            .and_then(|s| match s.parse::<u64>() {
248                Ok(val) => Some(val),
249                Err(e) => {
250                    self.errors.push(BinaryError::AttrParse(format!(
251                        "Failed to parse u64 from '{s}' for key '{key}': {e}"
252                    )));
253                    None
254                }
255            })
256    }
257
258    pub fn unix_time(&mut self, key: &str) -> i64 {
259        self.get_raw(key, true);
260        self.optional_unix_time(key).unwrap_or_default()
261    }
262
263    pub fn optional_unix_time(&mut self, key: &str) -> Option<i64> {
264        self.get_i64(key, false)
265    }
266
267    pub fn unix_milli(&mut self, key: &str) -> i64 {
268        self.get_raw(key, true);
269        self.optional_unix_milli(key).unwrap_or_default()
270    }
271
272    pub fn optional_unix_milli(&mut self, key: &str) -> Option<i64> {
273        self.get_i64(key, false)
274    }
275
276    fn get_i64(&mut self, key: &str, require: bool) -> Option<i64> {
277        self.get_raw(key, require)
278            .and_then(|s| match s.parse::<i64>() {
279                Ok(val) => Some(val),
280                Err(e) => {
281                    self.errors.push(BinaryError::AttrParse(format!(
282                        "Failed to parse i64 from '{s}' for key '{key}': {e}"
283                    )));
284                    None
285                }
286            })
287    }
288}