1use std::io;
2
3use io::BufWriter;
4use io::Write;
5
6use io::Read;
7
8use sxd_document::Package;
9use sxd_document::dom::Document;
10
11use sxd_xpath::Value;
12
13pub struct XmlElement(pub Package);
14
15impl XmlElement {
16 pub fn str2xml(xml: &str) -> Result<Self, io::Error> {
17 let pkg: Package = sxd_document::parser::parse(xml).map_err(io::Error::other)?;
18 Ok(Self(pkg))
19 }
20}
21
22impl XmlElement {
23 pub fn as_document(&self) -> Document<'_> {
24 self.0.as_document()
25 }
26}
27
28pub fn select_value<'a>(doc: &'a Document<'a>, xpath: &str) -> Result<Value<'a>, io::Error> {
29 sxd_xpath::evaluate_xpath(doc, xpath).map_err(io::Error::other)
30}
31
32impl XmlElement {
33 pub fn xpath2bool(&self, xpath: &str) -> Result<bool, io::Error> {
34 let doc: Document = self.as_document();
35 let val: Value = select_value(&doc, xpath)?;
36 match val {
37 Value::Boolean(b) => Ok(b),
38 Value::Number(f) => Err(io::Error::other(format!("not a bool: {f}"))),
39 Value::String(s) => Err(io::Error::other(format!("not a bool: {s}"))),
40 Value::Nodeset(_) => Err(io::Error::other("node set got")),
41 }
42 }
43
44 pub fn xpath2number(&self, xpath: &str) -> Result<f64, io::Error> {
45 let doc: Document = self.as_document();
46 let val: Value = select_value(&doc, xpath)?;
47 match val {
48 Value::Boolean(b) => Err(io::Error::other(format!("not a number: {b}"))),
49 Value::Number(f) => Ok(f),
50 Value::String(s) => Err(io::Error::other(format!("not a number: {s}"))),
51 Value::Nodeset(_) => Err(io::Error::other("node set got")),
52 }
53 }
54
55 pub fn xpath2string(&self, xpath: &str) -> Result<String, io::Error> {
56 let doc: Document = self.as_document();
57 let val: Value = select_value(&doc, xpath)?;
58 match val {
59 Value::Boolean(b) => Err(io::Error::other(format!("not a string: {b}"))),
60 Value::Number(f) => Err(io::Error::other(format!("not a string: {f}"))),
61 Value::String(s) => Ok(s),
62 Value::Nodeset(_) => Err(io::Error::other("node set got")),
63 }
64 }
65}
66
67impl XmlElement {
68 pub fn xpath2print<W>(&self, xpath: &str, mut wtr: W) -> Result<(), io::Error>
69 where
70 W: Write,
71 {
72 let doc: Document = self.as_document();
73 let val: Value = select_value(&doc, xpath)?;
74 match val {
75 Value::Boolean(b) => writeln!(&mut wtr, "{b}"),
76 Value::Number(f) => writeln!(&mut wtr, "{f}"),
77 Value::String(s) => writeln!(&mut wtr, "{s}"),
78 Value::Nodeset(n) => writeln!(&mut wtr, "{n:?}"),
79 }?;
80 wtr.flush()
81 }
82
83 pub fn xpath2stdout(&self, xpath: &str) -> Result<(), io::Error> {
84 let o = io::stdout();
85 let mut ol = o.lock();
86 self.xpath2print(xpath, BufWriter::new(&mut ol))?;
87 ol.flush()
88 }
89}
90
91pub fn str2xml2xpath2stdout(xml: &str, xpath: &str) -> Result<(), io::Error> {
92 let xe: XmlElement = XmlElement::str2xml(xml)?;
93 xe.xpath2stdout(xpath)
94}
95
96pub const XML_SIZE_LIMIT_DEFAULT: u64 = 16777216;
97
98pub fn stdin2xml2xpath2stdout(limit: u64, xpath: &str) -> Result<(), io::Error> {
99 let i = io::stdin().lock();
100 let mut limited = i.take(limit);
101 let mut buf: Vec<u8> = vec![];
102 limited.read_to_end(&mut buf)?;
103 let s: &str = std::str::from_utf8(&buf).map_err(io::Error::other)?;
104 str2xml2xpath2stdout(s, xpath)
105}