#![cfg_attr(test, deny(warnings))]
#![forbid(unsafe_code, missing_debug_implementations)]
const DOCTYPE: &str = "<!DOCTYPE html>";
const CHARSET: &str = r#"<meta charset="utf-8">"#;
const VIEWPORT: &str = r#"<meta name="viewport" content="width=device-width, initial-scale=1.0">"#;
const HTML_CLOSE: &str = "</html>";
const HEAD_OPEN: &str = "<head>";
const HEAD_CLOSE: &str = "</head>";
use std::default::Default;
pub fn new<'a>() -> Builder<'a> {
Builder::new()
}
#[derive(Debug, Clone, Default)]
pub struct Builder<'b> {
color: Option<String>,
desc: Option<String>,
lang: &'b str,
favicon: Option<String>,
fonts: Vec<String>,
manifest: Option<String>,
scripts: Vec<String>,
styles: Vec<String>,
title: Option<String>,
body: Option<&'b str>,
has_async_style: bool,
}
impl<'b> Builder<'b> {
pub fn new() -> Self {
Self {
lang: "en-US",
..Default::default()
}
}
pub fn raw_body(mut self, body: &'b str) -> Self {
self.body = Some(body);
self
}
pub fn lang(mut self, lang: &'b str) -> Self {
self.lang = lang;
self
}
pub fn description(mut self, desc: &str) -> Self {
let val = format!(r#"<meta name="description" content="{}">"#, desc);
self.desc = Some(val);
self
}
pub fn theme_color(mut self, color: &str) -> Self {
let val = format!(r#"<meta name="theme-color" content="{}">"#, color);
self.color = Some(val);
self
}
pub fn title(mut self, title: &str) -> Self {
let val = format!(r#"<title>{}</title>"#, title);
self.title = Some(val);
self
}
pub fn script(mut self, src: &str) -> Self {
let val = format!(r#"<script src="{}" defer></script>"#, src);
self.scripts.push(val);
self
}
pub fn module(mut self, src: &str) -> Self {
let val = format!(r#"<script src="{}" type="module"></script>"#, src);
self.scripts.push(val);
self
}
pub fn inline_module(mut self, src: &str) -> Self {
let val = format!(r#"<script type="module">{}</script>"#, src);
self.scripts.push(val);
self
}
pub fn lazy_script(mut self, src: &str) -> Self {
let val = format!(r#"<link rel="prefetch" href="{}">"#, src);
self.scripts.push(val);
self
}
pub fn blocking_script(mut self, src: &str) -> Self {
let val = format!(r#"<script src="{}"></script>"#, src);
self.scripts.push(val);
self
}
pub fn inline_script(mut self, src: &str) -> Self {
let val = format!(r#"<script>{}</script>"#, src);
self.scripts.push(val);
self
}
pub fn style(mut self, src: &str) -> Self {
let val = format!(
r#"<link rel="preload" as="style" href="{}" onload="this.rel='stylesheet'" onerror="this.rel='stylesheet'">"#,
src
);
self.styles.push(val);
if !self.has_async_style {
self = self.inline_script(css_rel_preload::CSS_REL_PRELOAD);
self.has_async_style = true;
}
self
}
pub fn inline_style(mut self, src: &str) -> Self {
let val = format!(r#"<style>{}</style>"#, src);
self.styles.push(val);
self
}
pub fn blocking_style(mut self, src: &str) -> Self {
let val = format!(r#"<link rel="stylesheet" href="{}">"#, src);
self.styles.push(val);
self
}
pub fn favicon(mut self, src: &str) -> Self {
let val = format!(r#"<link rel="icon" type="image/x-icon" href="{}">"#, src);
self.favicon = Some(val);
self
}
pub fn manifest(mut self, src: &str) -> Self {
let val = format!(r#"<link rel="manifest" href="{}">"#, src);
self.manifest = Some(val);
self
}
pub fn font(mut self, src: &str) -> Self {
let val = format!(
r#"<link rel="preload" as="font" crossorigin href="{}">"#,
src
);
self.fonts.push(val);
self
}
pub fn build(self) -> String {
let mut html: String = DOCTYPE.into();
html.push_str(&format!(r#"<html lang="{}">"#, self.lang));
html.push_str(HEAD_OPEN);
html.push_str(CHARSET);
html.push_str(VIEWPORT);
if let Some(title) = self.title {
html.push_str(&title);
}
if let Some(desc) = self.desc {
html.push_str(&desc);
}
for script in self.scripts {
html.push_str(&script);
}
for style in self.styles {
html.push_str(&style);
}
for font in self.fonts {
html.push_str(&font);
}
if let Some(manifest) = self.manifest {
html.push_str(&manifest);
}
if let Some(color) = self.color {
html.push_str(&color);
}
if let Some(favicon) = self.favicon {
html.push_str(&favicon);
}
html.push_str(HEAD_CLOSE);
if let Some(body) = self.body {
html.push_str(&body);
}
html.push_str(HTML_CLOSE);
html
}
}
#[cfg(feature = "http-types")]
impl Into<http_types::Response> for Builder<'_> {
fn into(self) -> http_types::Response {
let mut res = http_types::Response::new(200);
res.set_content_type(http_types::mime::HTML);
res.set_body(self.build());
res
}
}