rs-xml2selected 0.1.0

Prints the selected part of the xml using xpath.
Documentation
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)
}