diff options
author | Lars Wirzenius <liw@liw.fi> | 2023-04-09 11:42:54 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2023-04-09 12:07:28 +0300 |
commit | 211df21355263ef6b604478035c1c1053fe48ca4 (patch) | |
tree | fb7a2413e977a482b48a5173004785b3fdf3cfb1 | |
parent | 3dc7ba8812a4173a3c7ca7d037d5548b5d778200 (diff) | |
download | html-page-211df21355263ef6b604478035c1c1053fe48ca4.tar.gz |
feat: add a representation of a full HTML document
Also fix tag serialization to HTML to use all upper case (instead of
only the first letter), and rename crate to "html-page".
Sponsored-by: author
-rw-r--r-- | Cargo.lock | 2 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/lib.rs | 185 |
3 files changed, 185 insertions, 4 deletions
@@ -12,7 +12,7 @@ dependencies = [ ] [[package]] -name = "html-rs" +name = "html-page" version = "0.1.0" dependencies = [ "html-escape", @@ -1,5 +1,5 @@ [package] -name = "html-rs" +name = "html-page" version = "0.1.0" edition = "2021" @@ -13,6 +13,71 @@ use html_escape::{encode_double_quoted_attribute, encode_safe}; use std::collections::HashMap; use std::fmt::{Display, Formatter}; +/// An HTML document ("page'),consisting of a head and a body element. +/// +/// ~~~ +/// # use html_page::{Document, Element, Tag}; +/// let title = Element::new(Tag::Title).with_text("my page"); +/// let doc = Document::default().with_head_element(&title); +/// assert_eq!(format!("{}", doc), "<!DOCTYPE html>\n<HTML>\n\ +/// <HEAD><TITLE>my page</TITLE></HEAD>\n<BODY/>\n</HTML>\n"); +/// ~~~ +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Document { + head: Element, + body: Element, +} + +impl Default for Document { + fn default() -> Self { + Self { + head: Element::new(Tag::Head), + body: Element::new(Tag::Body), + } + } +} + +impl Document { + /// Append an element to the head. + pub fn push_to_head(&mut self, e: &Element) { + self.head.push_child(e); + } + + /// Append an element to the body. + pub fn push_to_body(&mut self, e: &Element) { + self.body.push_child(e); + } + + /// Append an element to the head, when constructing. + pub fn with_head_element(mut self, e: &Element) -> Self { + self.head.push_child(e); + self + } + + /// Append an element to the body, when constructing. + pub fn with_body_element(mut self, e: &Element) -> Self { + self.body.push_child(e); + self + } + + /// Append text to the body, when constructing. + pub fn with_body_text(mut self, text: &str) -> Self { + self.body.push_text(text); + self + } +} + +impl Display for Document { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + writeln!(f, "<!DOCTYPE html>")?; + writeln!(f, "<{}>", Tag::Html)?; + writeln!(f, "{}", &self.head)?; + writeln!(f, "{}", &self.body)?; + writeln!(f, "</{}>", Tag::Html)?; + Ok(()) + } +} + /// The tag of an HTML5 element. /// /// Note that we only support HTML5 elements, as listed on @@ -134,7 +199,123 @@ pub enum Tag { impl Display for Tag { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { - write!(f, "{:?}", self) + write!(f, "{}", self.as_str()) + } +} + +impl Tag { + fn as_str(&self) -> &str { + match self { + Self::A => "A", + Self::Abbr => "ABBR", + Self::Address => "ADDRESS", + Self::Area => "AREA", + Self::Article => "ARTICLE", + Self::Aside => "ASIDE", + Self::Audio => "AUDIO", + Self::B => "B", + Self::Base => "BASE", + Self::Bdi => "BDI", + Self::Bdo => "BDO", + Self::Blockquote => "BLOCKQUOTE", + Self::Body => "BODY", + Self::Br => "BR", + Self::Button => "BUTTON", + Self::Canvas => "CANVAS", + Self::Caption => "CAPTION", + Self::Cite => "CITE", + Self::Code => "CODE", + Self::Col => "COL", + Self::ColGroup => "COLGROUP", + Self::Data => "DATA", + Self::DataList => "DATALIST", + Self::Dd => "DD", + Self::Del => "DEL", + Self::Details => "DETAILS", + Self::Dfn => "DFN", + Self::Dialog => "DIALOG", + Self::Div => "DIV", + Self::Dl => "DL", + Self::Dt => "DT", + Self::Em => "EM", + Self::Embed => "EMBED", + Self::FieldSet => "FIELDSET", + Self::FigCaption => "FIGCAPTIO", + Self::Figure => "FIGURE", + Self::Footer => "FOOTER", + Self::Form => "FORM", + Self::H1 => "H1", + Self::H2 => "H2", + Self::H3 => "H3", + Self::H4 => "H4", + Self::H5 => "H5", + Self::H6 => "H6", + Self::Head => "HEAD", + Self::Header => "HEADER", + Self::Hr => "HR", + Self::Html => "HTML", + Self::I => "I", + Self::Iframe => "IFRAME", + Self::Img => "IMG", + Self::Input => "INPUT", + Self::Ins => "INS", + Self::Kbd => "KBD", + Self::Label => "LABEL", + Self::Legend => "LEGEND", + Self::Li => "LI", + Self::Link => "LINK", + Self::Main => "MAIN", + Self::Map => "MAP", + Self::Mark => "MARK", + Self::Meta => "META", + Self::Meter => "METER", + Self::Nav => "NAV", + Self::NoScript => "NOSCRIPT", + Self::Object => "OBJECT", + Self::Ol => "OL", + Self::OptGroup => "OPTGROUP", + Self::Option => "OPTION", + Self::Output => "OUTPUT", + Self::P => "P", + Self::Param => "PARAM", + Self::Picture => "PICTURE", + Self::Pre => "PRE", + Self::Progress => "PROGRESS", + Self::Q => "Q", + Self::Rp => "RP", + Self::Rt => "RT", + Self::Ruby => "RUBY", + Self::S => "S", + Self::Samp => "SAMP", + Self::Script => "SCRIPT", + Self::Section => "SECTION", + Self::Select => "SELECT", + Self::Small => "SMALL", + Self::Source => "SOURCE", + Self::Span => "SPAN", + Self::Strong => "STRONG", + Self::Style => "STYLE", + Self::Sub => "SUB", + Self::Summary => "SUMMARY", + Self::Sup => "SUP", + Self::Svg => "SVG", + Self::Table => "TABLE", + Self::Tbody => "TBODY", + Self::Td => "TD", + Self::Template => "TEMPLATE", + Self::TextArea => "TEXTAREA", + Self::Tfoot => "TFOOT", + Self::Th => "TH", + Self::Time => "TIME", + Self::Title => "TITLE", + Self::Tr => "TR", + Self::Track => "TRACK", + Self::U => "U", + Self::Ul => "UL", + Self::Var => "VAR", + Self::Video => "VIDEO", + Self::Wbr => "WBR", + } } } @@ -347,7 +528,7 @@ impl Display for Content { /// recursively visits the children of each child. /// /// ~~~ -/// # use html_rs::{Element, Tag, Visitor}; +/// # use html_page::{Element, Tag, Visitor}; /// #[derive(Default)] /// struct Collector { /// tags: Vec<Tag>, |