use crate::{Html, HtmlContainer, HtmlElement, HtmlTag};
use std::fmt::{self, Display};
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
#[non_exhaustive]
pub enum ContainerType {
Address,
Article,
#[default]
Div,
Footer,
Header,
Main,
OrderedList,
UnorderedList,
Nav,
Section,
}
impl From<ContainerType> for HtmlTag {
fn from(value: ContainerType) -> Self {
match value {
ContainerType::Address => HtmlTag::Address,
ContainerType::Article => HtmlTag::Article,
ContainerType::Div => HtmlTag::Div,
ContainerType::Footer => HtmlTag::Footer,
ContainerType::Header => HtmlTag::Header,
ContainerType::Main => HtmlTag::Main,
ContainerType::OrderedList => HtmlTag::OrderedList,
ContainerType::UnorderedList => HtmlTag::UnorderedList,
ContainerType::Nav => HtmlTag::Navigation,
ContainerType::Section => HtmlTag::Section,
}
}
}
impl Display for ContainerType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
HtmlTag::from(*self).fmt(f)
}
}
#[derive(Debug)]
pub struct Container(HtmlElement);
impl Default for Container {
fn default() -> Self {
Self::new(Default::default())
}
}
impl Html for Container {
fn to_html_string(&self) -> String {
self.0.to_html_string()
}
}
impl HtmlContainer for Container {
fn add_html<H: Html>(&mut self, content: H) {
match self.0.tag {
HtmlTag::OrderedList | HtmlTag::UnorderedList => self.0.add_child(
HtmlElement::new(HtmlTag::ListElement)
.with_html(content)
.into(),
),
_ => self.0.add_html(content),
};
}
}
impl Container {
pub fn new(tag: ContainerType) -> Self {
Self(HtmlElement::new(tag.into()))
}
pub fn with_attributes<A, S>(mut self, attributes: A) -> Self
where
A: IntoIterator<Item = (S, S)>,
S: ToString,
{
for (k, v) in attributes {
self.0.add_attribute(k, v);
}
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_content() {
let content = concat!(
r#"<h1 id="main-header">header</h1>"#,
r#"<img src="myimage.png" alt="test image"/>"#,
r#"<a href="rust-lang.org">Rust Home</a>"#,
r#"<p class="red-text">Sample Text</p>"#,
r#"<pre class="code">Text</pre>"#
);
let sut = Container::new(ContainerType::Article)
.with_header_attr(1, "header", [("id", "main-header")])
.with_image("myimage.png", "test image")
.with_link("rust-lang.org", "Rust Home")
.with_paragraph_attr("Sample Text", [("class", "red-text")])
.with_preformatted_attr("Text", [("class", "code")]);
assert_eq!(
sut.to_html_string(),
format!(
"<{tag}>{content}</{tag}>",
tag = ContainerType::Article,
content = content
)
)
}
#[test]
fn test_list() {
let content = concat!(
r#"<li><h1 id="main-header">header</h1></li>"#,
r#"<li><img src="myimage.png" alt="test image"/></li>"#,
r#"<li><a href="rust-lang.org">Rust Home</a></li>"#,
r#"<li><p class="red-text">Sample Text</p></li>"#,
r#"<li><pre class="code">Text</pre></li>"#
);
let sut = Container::new(ContainerType::OrderedList)
.with_header_attr(1, "header", [("id", "main-header")])
.with_image("myimage.png", "test image")
.with_link("rust-lang.org", "Rust Home")
.with_paragraph_attr("Sample Text", [("class", "red-text")])
.with_preformatted_attr("Text", [("class", "code")]);
assert_eq!(
sut.to_html_string(),
format!(
"<{tag}>{content}</{tag}>",
tag = ContainerType::OrderedList,
content = content
)
)
}
#[test]
fn test_nesting() {
let container = Container::new(ContainerType::Main)
.with_paragraph("paragraph")
.with_container(
Container::new(ContainerType::OrderedList)
.with_container(Container::default().with_paragraph(1))
.with_container(Container::default().with_paragraph('2'))
.with_container(Container::default().with_paragraph("3")),
)
.with_paragraph("done");
assert_eq!(
container.to_html_string(),
concat!(
"<main><p>paragraph</p><ol>",
"<li><div><p>1</p></div></li>",
"<li><div><p>2</p></div></li>",
"<li><div><p>3</p></div></li>",
"</ol><p>done</p></main>"
)
)
}
}