use std::rc::Rc;
#[derive(Debug, Clone)]
pub enum Element {
HTML, HEAD, LINK, META, STYLE, TITLE, BODY, HEADER, MAIN, FOOTER, ARTICLE, ASIDE, NAV, SECTION, DIV, UL, OL, LI, SPAN, BR, H1, H2, H3, H4, H5, H6, P, A, IMG, AUDIO, VIDEO, TRACK, SOURCE, SVG, CANVAS, SCRIPT,
BUTTON, INPUT, DATALIST, SELECT, OPTION, FORM, LABEL, TEXTAREA, DETAILS, DIALOG, SUMMARY, TEMPLATE
}
#[derive(Debug, Clone)]
enum Closing { TAG, SELF, NONE }
#[derive(Debug, Clone)]
enum Children {
Text(String),
Node(Rc<Node>)
}
#[derive(Debug, Clone)]
pub struct Node {
tag: Element,
attributes: Option<Vec<String>>,
children: Option<Vec<Children>>,
closing_tag: Closing
}
impl Node {
pub fn set_attribute(&mut self, name: &str, value: &str) {
match &mut self.attributes {
Some(attributes) => attributes.push(format!("{}=\"{}\"", name, value)),
None => {
let mut attributes: Vec<String> = Vec::new();
attributes.push(format!("{}=\"{}\"", name, value));
self.attributes = Some(attributes);
},
}
}
pub fn set_attribute_list(&mut self, attributes: Vec<(&str, &str)>) {
for ( name, value ) in attributes {
self.set_attribute(name, value);
}
}
pub fn inner_text(&mut self, value: &str) {
match &mut self.children {
Some(child) => child.push(Children::Text(value.to_string())),
None => {
let mut children: Vec<Children> = Vec::new();
children.push(Children::Text(value.to_string()));
self.children = Some(children);
}
}
}
pub fn append_child(&mut self, node: Node) {
match &mut self.children {
Some(child) => child.push(Children::Node(Rc::new(node))),
None => {
let mut children: Vec<Children> = Vec::new();
children.push(Children::Node(Rc::new(node)));
self.children = Some(children);
}
}
}
pub fn append_child_list(&mut self, children: Vec<Node>) {
for node in children {
self.append_child(node);
}
}
pub fn clone_node(&self) -> Node {
let cloned_attributes = match &self.attributes {
Some(attrs) => Some(attrs.clone()),
None => None,
};
let cloned_children = match &self.children {
Some(children) => {
let mut cloned_children = Vec::new();
for child in children {
match child {
Children::Text(text) => {
cloned_children.push(Children::Text(text.clone()));
},
Children::Node(node) => {
cloned_children.push(Children::Node(Rc::new(node.clone_node())));
},
}
}
Some(cloned_children)
},
None => None,
};
Node {
tag: self.tag.clone(),
attributes: cloned_attributes,
children: cloned_children,
closing_tag: self.closing_tag.clone(),
}
}
pub fn render(&self) -> String {
let tag = format!("{:?}", self.tag).to_lowercase();
let attributes = match &self.attributes {
Some(attrs) => attrs.join(" "),
None => String::new(),
};
let children = match &self.children {
Some(children) => {
let mut children_html = String::new();
for child in children {
match child {
Children::Text(text) => children_html.push_str(text),
Children::Node(node) => children_html.push_str(&node.render()),
}
}
children_html
},
None => String::new(),
};
match &self.closing_tag {
Closing::TAG => format!("<{} {}>{}</{}>", tag, attributes, children, tag),
Closing::SELF => format!("<{} {}/>", tag, attributes),
Closing::NONE => format!("<{} {}>", tag, attributes),
}
}
}
pub struct Document;
impl Document {
pub fn create_element(element: Element) -> Node {
let closing_tag = match element {
Element::HTML => Closing::TAG,
Element::HEAD => Closing::TAG,
Element::LINK => Closing::NONE,
Element::META => Closing::NONE,
Element::STYLE => Closing::TAG,
Element::TITLE => Closing::TAG,
Element::BODY => Closing::TAG,
Element::HEADER => Closing::TAG,
Element::MAIN => Closing::TAG,
Element::FOOTER => Closing::TAG,
Element::ARTICLE => Closing::TAG,
Element::ASIDE => Closing::TAG,
Element::NAV => Closing::TAG,
Element::SECTION => Closing::TAG,
Element::DIV => Closing::TAG,
Element::UL => Closing::TAG,
Element::OL => Closing::TAG,
Element::LI => Closing::TAG,
Element::SPAN => Closing::TAG,
Element::BR => Closing::SELF,
Element::H1 => Closing::TAG,
Element::H2 => Closing::TAG,
Element::H3 => Closing::TAG,
Element::H4 => Closing::TAG,
Element::H5 => Closing::TAG,
Element::H6 => Closing::TAG,
Element::P => Closing::TAG,
Element::A => Closing::TAG,
Element::IMG => Closing::NONE,
Element::AUDIO => Closing::TAG,
Element::VIDEO => Closing::TAG,
Element::TRACK => Closing::SELF,
Element::SOURCE => Closing::SELF,
Element::SVG => Closing::TAG,
Element::CANVAS => Closing::TAG,
Element::SCRIPT => Closing::TAG,
Element::BUTTON => Closing::TAG,
Element::INPUT => Closing::NONE,
Element::DATALIST => Closing::TAG,
Element::SELECT => Closing::TAG,
Element::OPTION => Closing::TAG,
Element::FORM => Closing::TAG,
Element::LABEL => Closing::TAG,
Element::TEXTAREA => Closing::TAG,
Element::DETAILS => Closing::TAG,
Element::DIALOG => Closing::TAG,
Element::SUMMARY => Closing::TAG,
Element::TEMPLATE => Closing::TAG,
};
Node { tag: element, attributes: None, children: None, closing_tag }
}
}