use std::fmt::Display;
use crate::{
styles::{convert_to_styles, sanitize_styles, Class, Style, StyleSheet},
tags::TagType,
};
#[derive(Clone, PartialEq, Eq)]
pub struct HtmlTag {
pub pre_content: Option<String>,
pub tag_type: TagType,
pub class_names: Vec<String>,
pub id: Option<String>,
pub body: Option<String>,
pub children: Option<Vec<HtmlTag>>,
pub custom_attributes: Option<Vec<(String, String)>>,
}
impl HtmlTag {
pub fn new(tag_type: &str) -> HtmlTag {
HtmlTag {
pre_content: None,
tag_type: TagType::from(tag_type),
class_names: Vec::new(),
id: None,
body: None,
children: None,
custom_attributes: None,
}
}
pub fn fresh(tag_type: TagType, body: Option<&str>, class_names: Vec<&str>) -> HtmlTag {
HtmlTag {
pre_content: None,
tag_type,
class_names: class_names.iter().map(|s| s.to_string()).collect(),
id: None,
body: body.map(|s| s.to_string()),
children: None,
custom_attributes: None,
}
}
pub fn add_child(&mut self, child: HtmlTag) {
if let Some(children) = &mut self.children {
children.push(child);
} else {
self.children = Some(vec![child]);
}
}
pub fn add_class(&mut self, class_name: &str) {
self.class_names.push(class_name.to_string());
}
pub fn set_body(&mut self, body: &str) {
self.body = Some(body.to_string());
}
pub fn set_id(&mut self, id: &str) {
self.id = Some(id.to_string());
}
pub fn set_href(&mut self, href: &str) {
self.add_attribute("href", href);
}
pub fn set_style(&mut self, key: &str, value: &str) {
self.add_attribute("style", &format!("{}: {};", key, value));
}
pub fn add_styles(&mut self, styles: Class) {
self.add_attribute("style", convert_to_styles(styles).as_str());
}
pub fn with_styles(mut self, styles: Class) -> Self {
self.add_styles(styles);
self
}
pub fn with_class(mut self, class_name: &str) -> Self {
self.add_class(class_name);
self
}
pub fn with_body(mut self, body: &str) -> Self {
self.set_body(body);
self
}
pub fn with_id(mut self, id: &str) -> Self {
self.set_id(id);
self
}
pub fn with_href(mut self, href: &str) -> Self {
self.set_href(href);
self
}
pub fn with_style(mut self, key: &str, value: &str) -> Self {
self.set_style(key, value);
self
}
pub fn with_child(mut self, child: HtmlTag) -> Self {
self.add_child(child);
self
}
pub fn with_attribute(mut self, key: &str, value: &str) -> Self {
self.add_attribute(key, value);
self
}
pub fn set_pre_content(&mut self, body: &str) {
self.pre_content = Some(body.to_string());
}
pub fn embed_style_sheet(mut self, style_sheet: &StyleSheet) -> Self {
self.set_pre_content(sanitize_styles(style_sheet.get_with_tag()).as_str());
self
}
fn get_tags(tag_type: &TagType) -> (String, String) {
let tag = format!("<{}", tag_type.html());
let closing_tag = format!("</{}>", tag_type.html());
(tag, closing_tag)
}
fn partial_convert(&self) -> String {
let mut html_to_return = if let Some(pre_tag) = &self.pre_content {
pre_tag.to_string()
} else {
String::new()
};
let (opening_tag, _) = HtmlTag::get_tags(&self.tag_type);
html_to_return.push_str(&opening_tag);
if let Some(id) = &self.id {
html_to_return.push_str(&format!(" id=\"{}\"", id));
}
if !self.class_names.is_empty() {
html_to_return.push_str(&format!(" class=\"{}\"", self.class_names.join(" ")));
}
if let Some(custom_attributes) = &self.custom_attributes {
for (key, value) in custom_attributes {
html_to_return.push_str(&format!(" {}=\"{}\"", key, value));
}
}
html_to_return
}
pub fn add_attribute(&mut self, key: &str, value: &str) {
match key {
"class" => self.add_class(value),
"id" => self.set_id(value),
_ => self.add_custom_attribute(key, value),
}
}
fn add_custom_attribute(&mut self, key: &str, value: &str) {
if let Some(custom_attributes) = &mut self.custom_attributes {
custom_attributes.push((key.to_string(), value.to_string()));
} else {
self.custom_attributes = Some(vec![(key.to_string(), value.to_string())]);
}
}
pub fn add_custom(&mut self, attributes: Vec<(&str, &str)>) {
let mut custom_attributes = Vec::new();
for (key, value) in attributes {
custom_attributes.push((key.to_string(), value.to_string()));
}
self.custom_attributes = Some(custom_attributes);
}
pub fn to_html(&self) -> String {
let mut html = self.partial_convert();
let (_, closing_tag) = HtmlTag::get_tags(&self.tag_type);
if let Some(body) = &self.body {
html.push_str(&format!(">{}</{}>", body, self.tag_type.html()));
return html;
} else {
html.push('>');
}
if let Some(children) = &self.children {
for child in children {
html.push_str(&child.to_html());
}
}
html.push_str(&closing_tag);
html
}
pub fn construct(&self) -> String {
self.to_html()
}
}
impl Display for HtmlTag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_html())
}
}