aidoku/helpers/
element.rs

1//! Additional functions for HTML elements.
2use crate::alloc::{String, Vec};
3use crate::imports::html::{Document, Element, ElementList, Html};
4
5pub trait ElementHelpers {
6	/// Get the text of the element(s) and their children.
7	///
8	/// This is different from [Element::text](crate::imports::html::Element::text)
9	/// in that `<p>` and `<br>` are considered and linebreaks will be inserted.
10	fn text_with_newlines(&self) -> Option<String>;
11}
12
13/// Macro to implement the ElementHelpers trait for types that provide
14/// select(), html(), and other HTML element operations
15macro_rules! impl_element_helpers {
16	($type:ty) => {
17		impl ElementHelpers for $type {
18			fn text_with_newlines(&self) -> Option<String> {
19				let node = self
20					.html()
21					// replace <br /> with newline marker
22					.and_then(|html| Html::parse_fragment(html.replace("<br />", "\\n")).ok())
23					.and_then(|html| html.select_first("body"));
24				// add newlines surrounding paragraphs
25				if let Some(elements) = node.as_ref().and_then(|node| node.select("p")) {
26					for mut p in elements.into_iter() {
27						let mut new_text = String::from("\\n");
28						new_text.push_str(&p.text().unwrap_or_default());
29						new_text.push_str("\\n");
30						_ = p.set_text(new_text);
31					}
32				}
33				node.and_then(|node| node.text()).map(|text| {
34					text
35						// replace newline markers with newlines
36						.replace("\\n", "\n")
37						// normalize lines (remove leading and trailing whitespace)
38						.lines()
39						.map(str::trim)
40						.collect::<Vec<_>>()
41						.join("\n")
42				})
43			}
44		}
45	};
46}
47
48// Implement ElementHelpers for both Element and ElementList
49impl_element_helpers!(Element);
50impl_element_helpers!(ElementList);
51
52impl ElementHelpers for Document {
53	fn text_with_newlines(&self) -> Option<String> {
54		self.0.text_with_newlines()
55	}
56}