wacore_binary/
node.rs

1use crate::attrs::{AttrParser, AttrParserRef};
2use std::borrow::Cow;
3use std::collections::HashMap;
4
5pub type Attrs = HashMap<String, String>;
6pub type AttrsRef<'a> = Vec<(Cow<'a, str>, Cow<'a, str>)>;
7
8pub type NodeVec<'a> = Vec<NodeRef<'a>>;
9
10#[derive(Debug, Clone, PartialEq)]
11pub enum NodeContent {
12    Bytes(Vec<u8>),
13    String(String),
14    Nodes(Vec<Node>),
15}
16
17#[derive(Debug, Clone, PartialEq)]
18pub enum NodeContentRef<'a> {
19    Bytes(Cow<'a, [u8]>),
20    String(Cow<'a, str>),
21    Nodes(Box<NodeVec<'a>>),
22}
23
24#[derive(Debug, Clone, PartialEq, Default)]
25pub struct Node {
26    pub tag: String,
27    pub attrs: Attrs,
28    pub content: Option<NodeContent>,
29}
30
31#[derive(Debug, Clone, PartialEq)]
32pub struct NodeRef<'a> {
33    pub tag: Cow<'a, str>,
34    pub attrs: AttrsRef<'a>,
35    pub content: Option<Box<NodeContentRef<'a>>>,
36}
37
38impl Node {
39    pub fn new(tag: &str, attrs: Attrs, content: Option<NodeContent>) -> Self {
40        Self {
41            tag: tag.to_string(),
42            attrs,
43            content,
44        }
45    }
46
47    pub fn children(&self) -> Option<&[Node]> {
48        match &self.content {
49            Some(NodeContent::Nodes(nodes)) => Some(nodes),
50            _ => None,
51        }
52    }
53
54    pub fn attrs(&self) -> AttrParser<'_> {
55        AttrParser::new(self)
56    }
57
58    pub fn get_optional_child_by_tag<'a>(&'a self, tags: &[&str]) -> Option<&'a Node> {
59        let mut current_node = self;
60        for &tag in tags {
61            if let Some(children) = current_node.children() {
62                if let Some(found) = children.iter().find(|c| c.tag == tag) {
63                    current_node = found;
64                } else {
65                    return None;
66                }
67            } else {
68                return None;
69            }
70        }
71        Some(current_node)
72    }
73
74    pub fn get_children_by_tag(&self, tag: &str) -> Vec<&Node> {
75        if let Some(children) = self.children() {
76            children.iter().filter(|c| c.tag == tag).collect()
77        } else {
78            Vec::new()
79        }
80    }
81
82    pub fn get_optional_child(&self, tag: &str) -> Option<&Node> {
83        self.children()
84            .and_then(|nodes| nodes.iter().find(|node| node.tag == tag))
85    }
86}
87
88impl<'a> NodeRef<'a> {
89    pub fn new(
90        tag: Cow<'a, str>,
91        attrs: AttrsRef<'a>,
92        content: Option<NodeContentRef<'a>>,
93    ) -> Self {
94        Self {
95            tag,
96            attrs,
97            content: content.map(Box::new),
98        }
99    }
100
101    pub fn attr_parser(&'a self) -> AttrParserRef<'a> {
102        AttrParserRef::new(self)
103    }
104
105    pub fn children(&self) -> Option<&[NodeRef<'a>]> {
106        match self.content.as_deref() {
107            Some(NodeContentRef::Nodes(nodes)) => Some(nodes.as_slice()),
108            _ => None,
109        }
110    }
111
112    pub fn get_attr(&self, key: &str) -> Option<&Cow<'a, str>> {
113        self.attrs.iter().find(|(k, _)| k == key).map(|(_, v)| v)
114    }
115
116    pub fn attrs_iter(&self) -> impl Iterator<Item = (&Cow<'a, str>, &Cow<'a, str>)> {
117        self.attrs.iter().map(|(k, v)| (k, v))
118    }
119
120    pub fn get_optional_child_by_tag(&self, tags: &[&str]) -> Option<&NodeRef<'a>> {
121        let mut current_node = self;
122        for &tag in tags {
123            if let Some(children) = current_node.children() {
124                if let Some(found) = children.iter().find(|c| c.tag == tag) {
125                    current_node = found;
126                } else {
127                    return None;
128                }
129            } else {
130                return None;
131            }
132        }
133        Some(current_node)
134    }
135
136    pub fn get_children_by_tag(&self, tag: &str) -> Vec<&NodeRef<'a>> {
137        if let Some(children) = self.children() {
138            children.iter().filter(|c| c.tag == tag).collect()
139        } else {
140            Vec::new()
141        }
142    }
143
144    pub fn get_optional_child(&self, tag: &str) -> Option<&NodeRef<'a>> {
145        self.children()
146            .and_then(|nodes| nodes.iter().find(|node| node.tag == tag))
147    }
148
149    pub fn to_owned(&self) -> Node {
150        Node {
151            tag: self.tag.to_string(),
152            attrs: self
153                .attrs
154                .iter()
155                .map(|(k, v)| (k.to_string(), v.to_string()))
156                .collect::<HashMap<String, String>>(),
157            content: self.content.as_deref().map(|c| match c {
158                NodeContentRef::Bytes(b) => NodeContent::Bytes(b.to_vec()),
159                NodeContentRef::String(s) => NodeContent::String(s.to_string()),
160                NodeContentRef::Nodes(nodes) => {
161                    NodeContent::Nodes(nodes.iter().map(|n| n.to_owned()).collect())
162                }
163            }),
164        }
165    }
166}