parse-html 0.4.1

A simple Rust project to parse HTML.
Documentation
use crate::node::{ElementNode, Node};

pub fn search_element_by_attr<'a>(
    node: &'a Node,
    attributes_name: &str,
    attributes_value: &str,
) -> Option<&'a ElementNode> {
    if let Node::Element(element) = node {
        for (attr_name, attr_value) in &element.attributes {
            if attr_name == attributes_name && attributes_value == attr_value {
                return Some(element);
            }
        }
        for child in &element.children {
            if let Some(found) = search_element_by_attr(child, attributes_name, attributes_value) {
                return Some(found);
            }
        }
    }
    None
}

pub fn search_all_element_by_attr<'a>(
    node: &'a Node,
    attributes_name: &str,
    attributes_value: &str,
) -> Vec<&'a ElementNode> {
    let mut list_found = vec![];

    if let Node::Element(element) = node {
        for (attr_name, attr_value) in &element.attributes {
            if attr_name == attributes_name && attributes_value == attr_value {
                list_found.push(element);
            }
        }
        for child in &element.children {
            list_found.extend(search_element_by_attr(
                child,
                attributes_name,
                attributes_value,
            ));
        }
    }
    list_found
}

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

    #[test]
    fn should_matching_id() {
        let child_node = ElementNode {
            tag_name: "div".to_string(),
            attributes: vec![("id".to_string(), "child".to_string())],
            children: vec![],
        };

        let parent_node = ElementNode {
            tag_name: "div".to_string(),
            attributes: vec![],
            children: vec![Node::Element(child_node)],
        };

        let node = Node::Element(parent_node);
        let result = search_element_by_attr(&node, "id", "child");
        let result_all = search_all_element_by_attr(&node, "id", "child");
        assert!(result.is_some());
        assert_eq!(result.unwrap().tag_name, "div");
        assert!(result_all.len() == 1);
    }

    #[test]
    fn should_no_matching_id() {
        let child_node = ElementNode {
            tag_name: "div".to_string(),
            attributes: vec![("id".to_string(), "child".to_string())],
            children: vec![],
        };

        let parent_node = ElementNode {
            tag_name: "div".to_string(),
            attributes: vec![],
            children: vec![Node::Element(child_node)],
        };

        let node = Node::Element(parent_node);
        let result = search_element_by_attr(&node, "id", "dasfdsa");
        let result_all = search_all_element_by_attr(&node, "id", "afdaf");
        assert!(result.is_none());
        assert!(result_all.len() == 0);
    }

    #[test]
    fn should_grandchild_matching_id() {
        let grandchild_node = ElementNode {
            tag_name: "span".to_string(),
            attributes: vec![("id".to_string(), "grandchild".to_string())],
            children: vec![],
        };

        let child_node = ElementNode {
            tag_name: "div".to_string(),
            attributes: vec![("id".to_string(), "child".to_string())],
            children: vec![Node::Element(grandchild_node)],
        };

        let parent_node = ElementNode {
            tag_name: "div".to_string(),
            attributes: vec![],
            children: vec![Node::Element(child_node)],
        };

        let node = Node::Element(parent_node);
        let result = search_element_by_attr(&node, "id", "grandchild");
        assert!(result.is_some());
        assert_eq!(result.unwrap().tag_name, "span");
    }

    #[test]
    fn should_matching_class() {
        let child_node = ElementNode {
            tag_name: "div".to_string(),
            attributes: vec![("class".to_string(), "child".to_string())],
            children: vec![],
        };

        let parent_node = ElementNode {
            tag_name: "div".to_string(),
            attributes: vec![],
            children: vec![Node::Element(child_node)],
        };

        let node = Node::Element(parent_node);
        let result = search_element_by_attr(&node, "class", "child");
        let result_all = search_all_element_by_attr(&node, "class", "child");

        assert!(result.is_some());
        assert!(result_all.len() == 1);
        assert_eq!(result.unwrap().tag_name, "div");
    }

    #[test]
    fn should_no_matching_class() {
        let child_node = ElementNode {
            tag_name: "div".to_string(),
            attributes: vec![("class".to_string(), "child".to_string())],
            children: vec![],
        };

        let parent_node = ElementNode {
            tag_name: "div".to_string(),
            attributes: vec![],
            children: vec![Node::Element(child_node)],
        };

        let node = Node::Element(parent_node);
        let result = search_element_by_attr(&node, "class", "dasfdsa");
        let result_all = search_all_element_by_attr(&node, "class", "afdaf");
        assert!(result.is_none());
        assert!(result_all.len() == 0);
    }
}