use std::borrow::Cow;
use std::str::FromStr;
use crate::error::{BinaryError, Result};
use crate::jid::Jid;
use crate::node::{Attrs, Node, NodeRef, NodeValue, ValueRef};
pub struct AttrParser<'a> {
pub attrs: &'a Attrs,
pub errors: Vec<BinaryError>,
}
pub struct AttrParserRef<'a> {
pub attrs: &'a [(Cow<'a, str>, ValueRef<'a>)],
pub errors: Vec<BinaryError>,
}
impl<'a> AttrParserRef<'a> {
pub fn new(node: &'a NodeRef<'a>) -> Self {
Self {
attrs: node.attrs.as_slice(),
errors: Vec::new(),
}
}
pub fn ok(&self) -> bool {
self.errors.is_empty()
}
pub fn finish(&self) -> Result<()> {
if self.ok() {
Ok(())
} else {
Err(BinaryError::AttrList(self.errors.clone()))
}
}
fn get_raw(&mut self, key: &str, require: bool) -> Option<&'a ValueRef<'a>> {
let val = self
.attrs
.iter()
.find(|(k, _)| k.as_ref() == key)
.map(|(_, v)| v);
if require && val.is_none() {
self.errors.push(BinaryError::AttrParse(format!(
"Required attribute '{key}' not found"
)));
}
val
}
pub fn optional_string(&mut self, key: &str) -> Option<Cow<'a, str>> {
self.get_raw(key, false).map(|v| v.to_string_cow())
}
pub fn required_string(&mut self, key: &str) -> Result<Cow<'a, str>> {
self.optional_string(key)
.ok_or_else(|| BinaryError::MissingAttr(key.to_string()))
}
pub fn optional_jid(&mut self, key: &str) -> Option<Jid> {
self.get_raw(key, false).and_then(|v| match v.to_jid() {
Some(jid) => Some(jid),
None => {
if let ValueRef::String(s) = v {
self.errors
.push(BinaryError::AttrParse(format!("Invalid JID: {s}")));
}
None
}
})
}
pub fn jid(&mut self, key: &str) -> Jid {
self.get_raw(key, true);
self.optional_jid(key).unwrap_or_default()
}
pub fn non_ad_jid(&mut self, key: &str) -> Jid {
self.jid(key).to_non_ad()
}
fn get_string_value(&mut self, key: &str, require: bool) -> Option<Cow<'a, str>> {
self.get_raw(key, require).map(|v| v.to_string_cow())
}
fn get_bool(&mut self, key: &str, require: bool) -> Option<bool> {
self.get_string_value(key, require)
.and_then(|s| match s.parse::<bool>() {
Ok(val) => Some(val),
Err(e) => {
self.errors.push(BinaryError::AttrParse(format!(
"Failed to parse bool from '{s}' for key '{key}': {e}"
)));
None
}
})
}
pub fn optional_bool(&mut self, key: &str) -> bool {
self.get_bool(key, false).unwrap_or(false)
}
pub fn bool(&mut self, key: &str) -> bool {
self.get_bool(key, true).unwrap_or(false)
}
pub fn optional_u64(&mut self, key: &str) -> Option<u64> {
self.get_string_value(key, false)
.and_then(|s| match s.parse::<u64>() {
Ok(val) => Some(val),
Err(e) => {
self.errors.push(BinaryError::AttrParse(format!(
"Failed to parse u64 from '{s}' for key '{key}': {e}"
)));
None
}
})
}
pub fn unix_time(&mut self, key: &str) -> i64 {
self.get_raw(key, true);
self.optional_unix_time(key).unwrap_or_default()
}
pub fn optional_unix_time(&mut self, key: &str) -> Option<i64> {
self.get_i64(key, false)
}
pub fn unix_milli(&mut self, key: &str) -> i64 {
self.get_raw(key, true);
self.optional_unix_milli(key).unwrap_or_default()
}
pub fn optional_unix_milli(&mut self, key: &str) -> Option<i64> {
self.get_i64(key, false)
}
fn get_i64(&mut self, key: &str, require: bool) -> Option<i64> {
self.get_string_value(key, require)
.and_then(|s| match s.parse::<i64>() {
Ok(val) => Some(val),
Err(e) => {
self.errors.push(BinaryError::AttrParse(format!(
"Failed to parse i64 from '{s}' for key '{key}': {e}"
)));
None
}
})
}
}
impl<'a> AttrParser<'a> {
pub fn new(node: &'a Node) -> Self {
Self {
attrs: &node.attrs,
errors: Vec::new(),
}
}
pub fn ok(&self) -> bool {
self.errors.is_empty()
}
pub fn finish(&self) -> Result<()> {
if self.ok() {
Ok(())
} else {
Err(BinaryError::AttrList(self.errors.clone()))
}
}
fn get_raw(&mut self, key: &str, require: bool) -> Option<&'a NodeValue> {
let val = self.attrs.get(key);
if require && val.is_none() {
self.errors.push(BinaryError::AttrParse(format!(
"Required attribute '{key}' not found"
)));
}
val
}
fn get_string_value(&mut self, key: &str, require: bool) -> Option<Cow<'a, str>> {
self.get_raw(key, require).map(|v| match v {
NodeValue::String(s) => Cow::Borrowed(s.as_str()),
NodeValue::Jid(j) => Cow::Owned(j.to_string()),
})
}
pub fn optional_string(&mut self, key: &str) -> Option<Cow<'a, str>> {
self.get_raw(key, false).map(|v| v.as_str())
}
pub fn required_string(&mut self, key: &str) -> Result<Cow<'a, str>> {
self.optional_string(key)
.ok_or_else(|| BinaryError::MissingAttr(key.to_string()))
}
pub fn optional_jid(&mut self, key: &str) -> Option<Jid> {
self.get_raw(key, false).and_then(|v| match v {
NodeValue::Jid(j) => Some(j.clone()),
NodeValue::String(s) => match Jid::from_str(s) {
Ok(jid) => Some(jid),
Err(e) => {
self.errors.push(BinaryError::from(e));
None
}
},
})
}
pub fn jid(&mut self, key: &str) -> Jid {
self.get_raw(key, true); self.optional_jid(key).unwrap_or_default()
}
pub fn non_ad_jid(&mut self, key: &str) -> Jid {
self.jid(key).to_non_ad()
}
fn get_bool(&mut self, key: &str, require: bool) -> Option<bool> {
self.get_string_value(key, require)
.and_then(|s| match s.parse::<bool>() {
Ok(val) => Some(val),
Err(e) => {
self.errors.push(BinaryError::AttrParse(format!(
"Failed to parse bool from '{s}' for key '{key}': {e}"
)));
None
}
})
}
pub fn optional_bool(&mut self, key: &str) -> bool {
self.get_bool(key, false).unwrap_or(false)
}
pub fn bool(&mut self, key: &str) -> bool {
self.get_bool(key, true).unwrap_or(false)
}
pub fn optional_u64(&mut self, key: &str) -> Option<u64> {
self.get_string_value(key, false)
.and_then(|s| match s.parse::<u64>() {
Ok(val) => Some(val),
Err(e) => {
self.errors.push(BinaryError::AttrParse(format!(
"Failed to parse u64 from '{s}' for key '{key}': {e}"
)));
None
}
})
}
pub fn unix_time(&mut self, key: &str) -> i64 {
self.get_raw(key, true);
self.optional_unix_time(key).unwrap_or_default()
}
pub fn optional_unix_time(&mut self, key: &str) -> Option<i64> {
self.get_i64(key, false)
}
pub fn unix_milli(&mut self, key: &str) -> i64 {
self.get_raw(key, true);
self.optional_unix_milli(key).unwrap_or_default()
}
pub fn optional_unix_milli(&mut self, key: &str) -> Option<i64> {
self.get_i64(key, false)
}
fn get_i64(&mut self, key: &str, require: bool) -> Option<i64> {
self.get_string_value(key, require)
.and_then(|s| match s.parse::<i64>() {
Ok(val) => Some(val),
Err(e) => {
self.errors.push(BinaryError::AttrParse(format!(
"Failed to parse i64 from '{s}' for key '{key}': {e}"
)));
None
}
})
}
}