sn0int-std 0.26.0

sn0int - stdlib
Documentation
use crate::engine::structs::LuaMap;
use crate::errors::*;
use crate::hlua::AnyLuaValue;
use kuchiki::traits::TendrilSink;
use std::collections::HashMap;

#[derive(Debug, PartialEq)]
pub struct Element {
    pub attrs: HashMap<String, String>,
    pub text: String,
    pub html: String,
}

impl From<Element> for AnyLuaValue {
    fn from(elem: Element) -> AnyLuaValue {
        let mut map = LuaMap::new();

        map.insert_str("text", elem.text);
        map.insert("attrs", LuaMap::from(elem.attrs));
        map.insert_str("html", elem.html);

        map.into()
    }
}

fn transform_element(entry: &kuchiki::NodeDataRef<kuchiki::ElementData>) -> Element {
    let text = entry.text_contents();
    let as_node = entry.as_node();

    let mut attrs: HashMap<String, String> = HashMap::new();

    if let Some(element) = as_node.as_element() {
        for (k, v) in &element.attributes.borrow().map {
            attrs.insert(k.local.to_string(), v.value.clone());
        }
    }

    let mut html = Vec::new();
    let html = match as_node.serialize(&mut html) {
        Ok(_) => String::from_utf8_lossy(&html).to_string(),
        Err(_) => {
            debug!("html serialize failed");
            String::new()
        }
    };

    Element { attrs, text, html }
}

pub fn html_select(html: &str, selector: &str) -> Result<Element> {
    let doc = kuchiki::parse_html().one(html);
    match doc.select_first(selector) {
        Ok(x) => Ok(transform_element(&x)),
        Err(_) => bail!("css selector failed"),
    }
}

pub fn html_select_list(html: &str, selector: &str) -> Result<Vec<Element>> {
    let doc = kuchiki::parse_html().one(html);

    match doc.select(selector) {
        Ok(x) => Ok(x.map(|x| transform_element(&x)).collect()),
        Err(_) => bail!("css selector failed"),
    }
}

pub fn html_form(html: &str) -> Result<HashMap<String, String>> {
    let inputs = html_select_list(html, "input")?;

    let mut form = HashMap::new();

    for input in inputs {
        let name = match input.attrs.get("name") {
            Some(x) => x.to_string(),
            None => continue,
        };

        let value = input.attrs.get("value").map(|x| x.to_string());

        let value = match input.attrs.get("type").map(|x| x.as_str()) {
            Some("hidden") => value,
            Some("submit") => value,
            _ => continue,
        };

        if let Some(value) = value {
            form.insert(name, value);
        }
    }

    Ok(form)
}

#[cfg(test)]
mod tests {
    use super::*;
    use maplit::hashmap;

    #[test]
    fn test_html_select() {
        let elems = html_select(r#"<html><div id="yey">content</div></html>"#, "#yey").unwrap();
        assert_eq!(
            elems,
            Element {
                attrs: hashmap! {
                    "id".into() => "yey".into(),
                },
                text: "content".into(),
                html: r#"<div id="yey">content</div>"#.into(),
            }
        );
    }

    #[test]
    fn test_html_select_list() {
        let elems =
            html_select_list(r#"<html><div id="yey">content</div></html>"#, "#yey").unwrap();
        assert_eq!(
            elems,
            vec![Element {
                attrs: hashmap! {
                    "id".into() => "yey".into(),
                },
                text: "content".into(),
                html: r#"<div id="yey">content</div>"#.into(),
            }]
        );
    }
}