use crate::attributes::Attributes;
use crate::html_container::HtmlContainer;
use crate::Html;
use std::fmt::{self, Display};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum ContainerType {
Address,
Article,
Div,
Footer,
Header,
Main,
OrderedList,
UnorderedList,
}
impl Display for ContainerType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Address => write!(f, "address"),
Self::Article => write!(f, "article"),
Self::Div => write!(f, "div"),
Self::Footer => write!(f, "footer"),
Self::Header => write!(f, "header"),
Self::Main => write!(f, "main"),
Self::OrderedList => write!(f, "ol"),
Self::UnorderedList => write!(f, "ul"),
}
}
}
#[derive(Debug)]
pub struct Container {
tag: ContainerType,
elements: Vec<String>,
attr: Attributes,
}
impl Html for Container {
fn to_html_string(&self) -> String {
format!(
"<{tag}{attr}>{content}</{tag}>",
tag = self.tag,
attr = self.attr,
content = self.elements.join(""),
)
}
}
impl HtmlContainer for Container {
#[inline]
fn add_html<H: Html>(&mut self, content: H) {
let content: String = match self.tag {
ContainerType::OrderedList | ContainerType::UnorderedList => {
format!("<li>{}</li>", content.to_html_string())
}
_ => content.to_html_string(),
};
self.elements.push(content);
}
}
impl Default for Container {
fn default() -> Self {
Container::new(ContainerType::Div)
}
}
impl Container {
pub fn new(tag: ContainerType) -> Self {
Container {
tag,
elements: Vec::new(),
attr: Attributes::default(),
}
}
pub fn with_attributes<A, S>(mut self, attributes: A) -> Self
where
A: IntoIterator<Item = (S, S)>,
S: ToString,
{
self.attr = Attributes::from(attributes);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use test_case::test_case;
#[test_case(ContainerType::Article; "article")]
#[test_case(ContainerType::Div; "div")]
#[test_case(ContainerType::Main; "main")]
fn test_content(container_type: ContainerType) {
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(container_type)
.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 = container_type,
content = content
)
)
}
#[test_case(ContainerType::OrderedList; "ordered_list")]
#[test_case(ContainerType::UnorderedList; "unordered_list")]
fn test_list(container_type: ContainerType) {
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(container_type)
.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 = container_type,
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>"
)
)
}
}