pub trait SelectorBase {
    fn from_html(html: String) -> Self;

    fn html(&self) -> String;

    // fn css<'a, T: SelectorBase>(&self, css_selector: &'a str) -> Vec<T> {
    fn css<'a>(&self, css_selector: &'a str) -> Vec<Selector> {
        let html = nipper::Document::from(self.html().as_str());

        let mut output = vec![];

        for item in html.select(css_selector).iter() {
            output.push(Selector::from_html(item.html().to_string()))
        }
        output
    }

    fn css_once<'a>(&self, css_selector: &'a str) -> Option<Selector> {
        self.css(css_selector).pop()
    }

    fn xpath(&self, xpath: &'static str) -> Vec<Selector> {
        match cssifier::cssifier(xpath) {
            Some(css_selector) => {
                if css_selector == "" {
                    Vec::default()
                } else {
                    self.css(css_selector.as_str())
                }
            }
            None => Vec::default(),
        }
    }

    fn xpath_once(&self, xpath: &'static str) -> Option<Selector> {
        self.xpath(xpath).pop()
    }

    fn content(&self) -> String {
        let html = nipper::Document::from(self.html().as_str());
        html.select("body > *")
            .iter()
            .map(|element| element.text().to_string())
            .last()
            .unwrap()
    }

    fn attr(&self, attribute: &'static str) -> Option<String> {
        let html = nipper::Document::from(self.html().as_str());
        match html.select("body > *").attr(attribute) {
            Some(text) => Some(format!("{}", text)),
            None => None,
        }
    }
}

#[derive(Debug)]
pub struct Selector {
    text: String,
}

impl SelectorBase for Selector {
    fn from_html(html: String) -> Self {
        Selector {
            text: html.to_string(),
        }
    }

    fn html(&self) -> String {
        format!("{}", self.text)
    }
}