use std::io;
use io::BufWriter;
use io::Write;
use io::Read;
use sxd_document::Package;
use sxd_document::dom::Document;
use sxd_xpath::Value;
pub struct XmlElement(pub Package);
impl XmlElement {
pub fn str2xml(xml: &str) -> Result<Self, io::Error> {
let pkg: Package = sxd_document::parser::parse(xml).map_err(io::Error::other)?;
Ok(Self(pkg))
}
}
impl XmlElement {
pub fn as_document(&self) -> Document<'_> {
self.0.as_document()
}
}
pub fn select_value<'a>(doc: &'a Document<'a>, xpath: &str) -> Result<Value<'a>, io::Error> {
sxd_xpath::evaluate_xpath(doc, xpath).map_err(io::Error::other)
}
impl XmlElement {
pub fn xpath2bool(&self, xpath: &str) -> Result<bool, io::Error> {
let doc: Document = self.as_document();
let val: Value = select_value(&doc, xpath)?;
match val {
Value::Boolean(b) => Ok(b),
Value::Number(f) => Err(io::Error::other(format!("not a bool: {f}"))),
Value::String(s) => Err(io::Error::other(format!("not a bool: {s}"))),
Value::Nodeset(_) => Err(io::Error::other("node set got")),
}
}
pub fn xpath2number(&self, xpath: &str) -> Result<f64, io::Error> {
let doc: Document = self.as_document();
let val: Value = select_value(&doc, xpath)?;
match val {
Value::Boolean(b) => Err(io::Error::other(format!("not a number: {b}"))),
Value::Number(f) => Ok(f),
Value::String(s) => Err(io::Error::other(format!("not a number: {s}"))),
Value::Nodeset(_) => Err(io::Error::other("node set got")),
}
}
pub fn xpath2string(&self, xpath: &str) -> Result<String, io::Error> {
let doc: Document = self.as_document();
let val: Value = select_value(&doc, xpath)?;
match val {
Value::Boolean(b) => Err(io::Error::other(format!("not a string: {b}"))),
Value::Number(f) => Err(io::Error::other(format!("not a string: {f}"))),
Value::String(s) => Ok(s),
Value::Nodeset(_) => Err(io::Error::other("node set got")),
}
}
}
impl XmlElement {
pub fn xpath2print<W>(&self, xpath: &str, mut wtr: W) -> Result<(), io::Error>
where
W: Write,
{
let doc: Document = self.as_document();
let val: Value = select_value(&doc, xpath)?;
match val {
Value::Boolean(b) => writeln!(&mut wtr, "{b}"),
Value::Number(f) => writeln!(&mut wtr, "{f}"),
Value::String(s) => writeln!(&mut wtr, "{s}"),
Value::Nodeset(n) => writeln!(&mut wtr, "{n:?}"),
}?;
wtr.flush()
}
pub fn xpath2stdout(&self, xpath: &str) -> Result<(), io::Error> {
let o = io::stdout();
let mut ol = o.lock();
self.xpath2print(xpath, BufWriter::new(&mut ol))?;
ol.flush()
}
}
pub fn str2xml2xpath2stdout(xml: &str, xpath: &str) -> Result<(), io::Error> {
let xe: XmlElement = XmlElement::str2xml(xml)?;
xe.xpath2stdout(xpath)
}
pub const XML_SIZE_LIMIT_DEFAULT: u64 = 16777216;
pub fn stdin2xml2xpath2stdout(limit: u64, xpath: &str) -> Result<(), io::Error> {
let i = io::stdin().lock();
let mut limited = i.take(limit);
let mut buf: Vec<u8> = vec![];
limited.read_to_end(&mut buf)?;
let s: &str = std::str::from_utf8(&buf).map_err(io::Error::other)?;
str2xml2xpath2stdout(s, xpath)
}