sonor 2.0.0

a library for controlling sonos speakers
Documentation
use crate::Result;
use roxmltree::{Document, Node};

#[doc(hidden)]
#[macro_export]
macro_rules! args {
    ( $( $var:literal: $e:expr ),* ) => { &{
        let mut s = String::new();
        $(
            s.push_str(concat!("<", $var, ">"));
            s.push_str(&$e.to_string());
            s.push_str(concat!("</", $var, ">"));
        )*
        s
    } }
}

pub trait HashMapExt {
    fn extract(&mut self, key: &str) -> Result<String>;
}
impl HashMapExt for std::collections::HashMap<String, String> {
    fn extract(&mut self, key: &str) -> Result<String> {
        self.remove(key).ok_or_else(|| {
            rupnp::Error::XmlMissingElement("UPnP Response".to_string(), key.to_string()).into()
        })
    }
}

pub fn seconds_to_str(seconds_total: i64) -> String {
    let sign = if seconds_total < 0 { "-" } else { "" };
    let seconds_total = seconds_total.abs();

    let seconds = seconds_total % 60;
    let minutes = (seconds_total / 60) % 60;
    let hours = seconds_total / 3600;

    format!("{}{:02}:{:02}:{:02}", sign, hours, minutes, seconds)
}
pub fn seconds_from_str(s: &str) -> Result<u32> {
    let opt = (|| {
        let mut split = s.splitn(3, ':');
        let hours = split.next()?.parse::<u32>().ok()?;
        let minutes = split.next()?.parse::<u32>().ok()?;
        let seconds = split.next()?.parse::<u32>().ok()?;

        Some(hours * 3600 + minutes * 60 + seconds)
    })();

    opt.ok_or(rupnp::Error::ParseError("invalid duration").into())
}

pub fn parse_bool(s: String) -> Result<bool> {
    match s.trim() {
        "0" => Ok(false),
        "1" => Ok(true),
        _ => Err(rupnp::Error::ParseError("bool was neither `0` nor `1`").into()),
    }
}

pub fn find_node_attribute<'n, 'd: 'n>(node: Node<'d, 'n>, attr: &str) -> Result<&'n str> {
    node.attributes()
        .find(|a| a.name().eq_ignore_ascii_case(attr))
        .map(|attr| attr.value())
        .ok_or_else(|| {
            rupnp::Error::XmlMissingElement(node.tag_name().name().to_string(), attr.to_string())
                .into()
        })
}

pub fn find_root_node<'a, 'input: 'a>(
    document: &'input Document<'input>,
    element: &str,
    docname: &str,
) -> Result<Node<'a, 'input>> {
    document
        .descendants()
        .filter(roxmltree::Node::is_element)
        .find(|n| n.tag_name().name().eq_ignore_ascii_case(element))
        .ok_or_else(|| {
            rupnp::Error::XmlMissingElement(docname.to_string(), element.to_string()).into()
        })
}