1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
use std::fmt::Display;
use std::io::BufRead;
use std::path::Path;
use std::str::FromStr;

use crate::utils::ResultLogExt;
use minidom::{Children, Element};
use quick_xml::Reader;

use failure::{format_err, Error};

pub fn attr_map<'a, T>(from: &'a Element, name: &str, elemname: &'static str) -> Result<T, Error>
where
    T: From<&'a str>,
{
    from.attr(name)
        .map(T::from)
        .ok_or_else(|| format_err!("{} not found in {} element", name, elemname))
}

pub fn attr_parse_hex<'a>(
    from: &'a Element,
    name: &str,
    elemname: &'static str,
) -> Result<u64, Error> {
    from.attr(name)
        .ok_or_else(|| format_err!("{} not found in {} element", name, elemname))
        .and_then(|st| {
            if st.starts_with("0x") {
                u64::from_str_radix(&st[2..], 16).map_err(|e| format_err!("{}", e))
            } else if st.starts_with('0') {
                u64::from_str_radix(&st[1..], 8).map_err(|e| format_err!("{}", e))
            } else {
                u64::from_str_radix(st, 10).map_err(|e| format_err!("{}", e))
            }
        })
}

pub fn attr_parse<'a, T, E>(
    from: &'a Element,
    name: &str,
    elemname: &'static str,
) -> Result<T, Error>
where
    T: FromStr<Err = E>,
    E: Display,
{
    from.attr(name)
        .ok_or_else(|| format_err!("{} not found in {} element", name, elemname))
        .and_then(|st| st.parse::<T>().map_err(|e| format_err!("{}", e)))
}

pub fn child_text<'a>(
    from: &'a Element,
    name: &str,
    elemname: &'static str,
) -> Result<String, Error> {
    match get_child_no_ns(from, name) {
        Some(child) => Ok(child.text()),
        None => Err(format_err!(
            "child element \"{}\" not found in \"{}\" element",
            name,
            elemname
        )),
    }
}

pub fn get_child_no_ns<'a>(from: &'a Element, name: &str) -> Option<&'a Element> {
    for child in from.children() {
        if child.name() == name {
            return Some(child);
        }
    }
    None
}

pub fn assert_root_name(from: &Element, name: &str) -> Result<(), Error> {
    if from.name() != name {
        Err(format_err!(
            "tried to parse element \"{}\" from element \"{}\"",
            name,
            from.name()
        ))
    } else {
        Ok(())
    }
}

pub trait FromElem: Sized {
    fn from_elem(e: &Element) -> Result<Self, Error>;

    fn from_reader<T: BufRead>(r: &mut Reader<T>) -> Result<Self, Error> {
        let mut root = Element::from_reader(r)?;
        root.set_attr::<&str, Option<String>>("xmlns:xs", None);
        Self::from_elem(&root)
    }
    fn from_string(s: &str) -> Result<Self, Error> {
        let mut r = Reader::from_str(s);
        Self::from_reader(&mut r)
    }
    fn from_path(p: &Path) -> Result<Self, Error> {
        let mut r = Reader::from_file(p)?;
        Self::from_reader(&mut r)
    }
    fn vec_from_children(clds: Children) -> Vec<Self> {
        clds.flat_map(move |cld| Self::from_elem(cld).ok_warn().into_iter())
            .collect()
    }
}