use core::fmt;
use std::collections::HashMap;
use std::path;
use std::sync::Mutex;
mod comment;
mod doctype;
mod element;
mod fragment;
mod text;
lazy_static::lazy_static! {
pub static ref MEMOIZE_FILE: Mutex<
HashMap<path::PathBuf, String>
> = Mutex::new(HashMap::default());
}
pub enum Node {
Comment(comment::CommentNode),
Doctype(doctype::DoctypeNode),
Fragment(fragment::FragmentNode),
Element(element::ElementNode),
Text(text::TextNode),
Json(text::JsonTextNode),
UnsafeHtml(text::DangerousTextNode),
}
impl Node {
pub fn create_comment(comment: impl ToString) -> Self {
Self::Comment(comment::CommentNode {
content: comment.to_string(),
})
}
}
impl Node {
pub fn create_doctype(public_identifier: impl ToString) -> Self {
Self::Doctype(doctype::DoctypeNode {
public_identifier: public_identifier.to_string(),
})
}
}
impl Node {
pub fn create_fragment(children: Vec<Node>) -> Self {
Self::Fragment(fragment::FragmentNode { children })
}
}
impl Node {
pub fn create_element(
tag_name: String,
attributes: Vec<(String, Option<String>)>,
children: Option<Vec<Node>>,
) -> Self {
Self::Element(element::ElementNode {
tag_name,
attributes,
children,
})
}
}
impl Node {
pub fn create_text(text: impl ToString) -> Self {
Self::Text(text::TextNode {
text: text.to_string(),
})
}
pub fn create_json(text: impl ToString) -> Self {
Self::Json(text::JsonTextNode {
text: text.to_string(),
})
}
}
impl Node {
pub fn create_unsafe_html(raw_text: impl ToString) -> Self {
Self::UnsafeHtml(text::DangerousTextNode {
raw_text: raw_text.to_string(),
})
}
pub fn create_unsafe_html_from_file(file: impl AsRef<path::Path>) -> Self {
let mut memoize = MEMOIZE_FILE.lock().expect("cache guard");
if let Some(content_of_file) = memoize.get(file.as_ref()) {
return Self::UnsafeHtml(text::DangerousTextNode {
raw_text: content_of_file.to_owned(),
});
}
let raw_text = std::fs::read_to_string(&file).unwrap_or_else(|_| {
panic!("le fichier « {} » n'existe pas.", file.as_ref().display())
});
memoize.insert(file.as_ref().to_owned(), raw_text.clone());
Self::UnsafeHtml(text::DangerousTextNode { raw_text })
}
}
fn with_children(
f: &mut fmt::Formatter<'_>,
children: &[Node],
is_fragment: bool,
) -> fmt::Result {
if f.alternate() {
let mut children = children.iter();
if is_fragment {
if let Some(first_child) = children.next() {
write!(f, "{first_child:#}")?;
for child in children {
write!(f, "\n{child:#}")?;
}
}
} else {
for child in children.map(|child| format!("{child:#}")) {
for line in child.lines() {
write!(f, "\n\t{line}")?;
}
}
writeln!(f)?;
}
} else {
use std::fmt::Display;
for child in children {
child.fmt(f)?;
}
}
Ok(())
}
impl Default for Node {
fn default() -> Self {
Self::create_fragment(vec![])
}
}
impl fmt::Display for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
| Self::Comment(comment) => comment.fmt(f),
| Self::Doctype(doctype) => doctype.fmt(f),
| Self::Fragment(fragment) => fragment.fmt(f),
| Self::Element(element) => element.fmt(f),
| Self::Text(text) => text.fmt(f),
| Self::Json(text) => text.fmt(f),
| Self::UnsafeHtml(danger) => danger.fmt(f),
}
}
}
impl<It, F> From<It> for Node
where
It: IntoIterator<Item = F>,
F: Into<Self>,
{
fn from(it: It) -> Self {
Self::Fragment(it.into())
}
}
impl From<comment::CommentNode> for Node {
fn from(comment_node: comment::CommentNode) -> Self {
Self::Comment(comment_node)
}
}
impl From<doctype::DoctypeNode> for Node {
fn from(doctype_node: doctype::DoctypeNode) -> Self {
Self::Doctype(doctype_node)
}
}
impl From<fragment::FragmentNode> for Node {
fn from(fragment_node: fragment::FragmentNode) -> Self {
Self::Fragment(fragment_node)
}
}
impl From<element::ElementNode> for Node {
fn from(element_node: element::ElementNode) -> Self {
Self::Element(element_node)
}
}
impl From<text::TextNode> for Node {
fn from(text_node: text::TextNode) -> Self {
Self::Text(text_node)
}
}