html2md_rs/
structs.rs

1use std::collections::HashMap;
2
3/// Represents the different types of HTML elements that the library supports.
4#[derive(Debug, PartialEq, Eq, Clone, Default)]
5pub enum NodeType {
6    Html,
7    Head,
8    Style,
9    Link,
10    Script,
11    Meta,
12    Title,
13    Body,
14    H1,
15    H2,
16    H3,
17    H4,
18    H5,
19    H6,
20    P,
21    Div,
22    Strong,
23    Em,
24    A,
25    Ul,
26    Ol,
27    Li,
28    Pre,
29    Code,
30    Hr,
31    Br,
32    Blockquote,
33    #[default]
34    Text,
35    Comment,
36    Unknown(String),
37}
38
39impl NodeType {
40    pub fn is_special_tag(&self) -> bool {
41        use NodeType::*;
42        matches!(self, Blockquote | Ul | Ol)
43    }
44
45    pub fn from_tag_str(input: &str) -> Self {
46        use NodeType::*;
47        match input.to_lowercase().as_str() {
48            "html" => Html,
49            "head" => Head,
50            "style" => Style,
51            "link" => Link,
52            "script" => Script,
53            "meta" => Meta,
54            "title" => Title,
55            "body" => Body,
56            "h1" => H1,
57            "h2" => H2,
58            "h3" => H3,
59            "h4" => H4,
60            "h5" => H5,
61            "h6" => H6,
62            "p" => P,
63            "div" => Div,
64            "strong" => Strong,
65            "em" => Em,
66            "a" => A,
67            "ul" => Ul,
68            "ol" => Ol,
69            "li" => Li,
70            "pre" => Pre,
71            "code" => Code,
72            "hr" => Hr,
73            "br" => Br,
74            "blockquote" => Blockquote,
75            unknown => Unknown(unknown.to_string()),
76        }
77    }
78}
79
80/// Represents a node in the HTML tree.
81#[derive(Debug, PartialEq, Eq, Clone, Default)]
82pub struct Node {
83    pub tag_name: Option<NodeType>,
84    pub value: Option<String>,
85    pub attributes: Option<Attributes>,
86    pub within_special_tag: Option<Vec<NodeType>>,
87    pub children: Vec<Node>,
88}
89
90impl Node {
91    /// Checks whether the node is within any of the special tags passed in
92    pub fn is_in_special_tag(&self, tags: &[NodeType]) -> bool {
93        if let Some(within_special_tag) = &self.within_special_tag {
94            within_special_tag.iter().any(|tag| tags.contains(tag))
95        } else {
96            false
97        }
98    }
99
100    /// Returns the leading spaces if there is any
101    /// This is used to format the output of the unordered and ordered lists
102    pub fn leading_spaces(&self) -> String {
103        let ul_or_ol = &[NodeType::Ul, NodeType::Ol];
104        if let Some(within_special_tag) = &self.within_special_tag {
105            " ".repeat(
106                (within_special_tag
107                    .iter()
108                    .filter(|tag| ul_or_ol.contains(tag))
109                    .count()
110                    - 1)
111                    * 2,
112            )
113        } else {
114            String::new()
115        }
116    }
117
118    /// Creates a new Node from tag_name, value, attributes, within_special_tag and children
119    pub fn new(
120        tag_name: Option<NodeType>,
121        value: Option<String>,
122        attributes: Option<Attributes>,
123        within_special_tag: Option<Vec<NodeType>>,
124        children: Vec<Node>,
125    ) -> Self {
126        Node {
127            tag_name,
128            value,
129            attributes,
130            within_special_tag,
131            children,
132        }
133    }
134}
135
136/// Represents the Attributes of an HTML element.
137#[derive(Debug, PartialEq, Eq, Clone, Default)]
138pub struct Attributes {
139    pub(crate) id: Option<String>,
140    pub(crate) class: Option<String>,
141    pub(crate) href: Option<String>,
142    pub(crate) attributes: HashMap<String, AttributeValues>,
143}
144
145impl Attributes {
146    /// Creates a new Attributes struct from id, class and attributes
147    pub fn new() -> Self {
148        Attributes {
149            id: None,
150            class: None,
151            href: None,
152            attributes: HashMap::new(),
153        }
154    }
155
156    /// Returns the attribute value of the key passed in
157    pub fn get(&self, key: &str) -> Option<AttributeValues> {
158        match key {
159            "id" => self.id.as_ref().map(|id| AttributeValues::from(id.clone())),
160            "class" => self
161                .class
162                .as_ref()
163                .map(|class| AttributeValues::from(class.clone())),
164            _ => self.attributes.get(key).cloned(),
165        }
166    }
167
168    /// Returns the id attribute of the element
169    pub fn get_id(&self) -> Option<&String> {
170        self.id.as_ref()
171    }
172
173    /// Returns the class attribute of the element
174    pub fn get_class(&self) -> Option<&String> {
175        self.class.as_ref()
176    }
177
178    /// Return the href attribute of the element
179    pub fn get_href(&self) -> Option<String> {
180        self.get("href").and_then(|value| match value {
181            AttributeValues::String(href) => Some(href),
182            _ => None,
183        })
184    }
185
186    /// Returns the attributes of the element
187    pub fn contains(&self, key: &str) -> bool {
188        match key {
189            "id" => self.id.is_some(),
190            "class" => self.class.is_some(),
191            _ => self.attributes.contains_key(key),
192        }
193    }
194
195    /// Inserts a new attribute into the element with the key and value passed in
196    pub fn insert(&mut self, key: String, value: AttributeValues) {
197        match key.as_str() {
198            "id" => self.id = Some(value.to_string()),
199            "class" => self.class = Some(value.to_string()),
200            _ => {
201                self.attributes.insert(key, value);
202            }
203        }
204    }
205
206    /// Returns whether the element attributes are empty
207    pub fn is_empty(&self) -> bool {
208        self.id.is_none() && self.class.is_none() && self.attributes.is_empty()
209    }
210
211    /// Inserts attributes into the element from a tuple vector
212    pub fn from(vec: Vec<(String, AttributeValues)>) -> Self {
213        let mut attributes = Attributes::new();
214        for (key, value) in vec {
215            attributes.insert(key, value);
216        }
217        attributes
218    }
219}
220
221/// Represents the different types of attribute values that the library supports.
222#[derive(Debug, PartialEq, Eq, Clone)]
223pub enum AttributeValues {
224    /// Represents a string attribute value.
225    String(String),
226    /// Represents a boolean attribute value.
227    Bool(bool),
228    /// Represents an integer attribute value.
229    Number(i32),
230}
231
232impl AttributeValues {
233    /// Creates a new `AttributeValues` instance from a value that can be converted into `AttributeValues`.
234    ///
235    /// This function uses the `Into` trait to allow for flexible type conversion.
236    ///
237    /// # Examples
238    ///
239    /// ```
240    /// # use html2md_rs::structs::AttributeValues;
241    /// let string_value = AttributeValues::from("Hello, world!");
242    /// let bool_value = AttributeValues::from(true);
243    /// let number_value = AttributeValues::from(42);
244    /// ```
245    pub fn from<T: Into<AttributeValues>>(value: T) -> Self {
246        value.into()
247    }
248}
249
250impl From<String> for AttributeValues {
251    /// Converts a `String` into an `AttributeValues::String`.
252    fn from(value: String) -> Self {
253        AttributeValues::String(value)
254    }
255}
256
257impl From<&str> for AttributeValues {
258    /// Converts a string slice (`&str`) into an `AttributeValues::String`.
259    fn from(value: &str) -> Self {
260        AttributeValues::String(value.to_string())
261    }
262}
263
264impl From<bool> for AttributeValues {
265    /// Converts a `bool` into an `AttributeValues::Bool`.
266    fn from(value: bool) -> Self {
267        AttributeValues::Bool(value)
268    }
269}
270
271impl From<i32> for AttributeValues {
272    /// Converts an `i32` into an `AttributeValues::Number`.
273    fn from(value: i32) -> Self {
274        AttributeValues::Number(value)
275    }
276}
277
278impl std::fmt::Display for AttributeValues {
279    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
280        match self {
281            AttributeValues::String(value) => write!(f, "{}", value),
282            AttributeValues::Bool(value) => write!(f, "{}", value),
283            AttributeValues::Number(value) => write!(f, "{}", value),
284        }
285    }
286}
287
288#[derive(Debug, Default)]
289pub struct ToMdConfig {
290    pub ignore_rendering: Vec<NodeType>,
291}