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}