400 lines
9.2 KiB
Rust
400 lines
9.2 KiB
Rust
use std::collections::HashMap;
|
|
|
|
pub trait Build {
|
|
fn build(self) -> String;
|
|
}
|
|
|
|
#[derive(PartialEq)]
|
|
pub enum Tag {
|
|
Empty,
|
|
|
|
Html,
|
|
Head,
|
|
Link,
|
|
Script,
|
|
Title,
|
|
Body,
|
|
Main,
|
|
Break,
|
|
|
|
Details,
|
|
Summary,
|
|
|
|
Dialog,
|
|
Article,
|
|
Header,
|
|
Footer,
|
|
|
|
Div,
|
|
|
|
Table,
|
|
TableHead,
|
|
TableHeader,
|
|
TableBody,
|
|
TableRow,
|
|
TableData,
|
|
|
|
Progress,
|
|
|
|
Form,
|
|
Label,
|
|
FieldSet,
|
|
Input,
|
|
Select,
|
|
Option,
|
|
|
|
Nav,
|
|
|
|
OrderedList,
|
|
UnorderedList,
|
|
ListItem,
|
|
|
|
Anchor,
|
|
Button,
|
|
|
|
HeaderGroup,
|
|
Header1,
|
|
Header2,
|
|
Header3,
|
|
Header4,
|
|
Header5,
|
|
Header6,
|
|
Strong,
|
|
Paragraph,
|
|
JustText,
|
|
}
|
|
|
|
impl Tag {
|
|
fn as_str(&self) -> &'static str {
|
|
match self {
|
|
Self::Empty => "",
|
|
Self::JustText => "",
|
|
|
|
Self::Html => "html",
|
|
Self::Head => "head",
|
|
Self::Link => "link",
|
|
Self::Script => "script",
|
|
Self::Title => "title",
|
|
Self::Body => "body",
|
|
Self::Main => "main",
|
|
Self::Break => "break",
|
|
|
|
Self::Progress => "progress",
|
|
|
|
Self::Details => "details",
|
|
Self::Summary => "summary",
|
|
|
|
Self::Dialog => "dialog",
|
|
Self::Article => "article",
|
|
Self::Header => "header",
|
|
Self::Footer => "footer",
|
|
|
|
Self::Div => "div",
|
|
|
|
Self::Table => "table",
|
|
Self::TableHead => "thead",
|
|
Self::TableHeader => "th",
|
|
Self::TableBody => "tbody",
|
|
Self::TableRow => "tr",
|
|
Self::TableData => "td",
|
|
|
|
Self::Form => "form",
|
|
Self::Label => "label",
|
|
Self::FieldSet => "fieldset",
|
|
Self::Input => "input",
|
|
Self::Select => "select",
|
|
Self::Option => "option",
|
|
|
|
Self::Nav => "nav",
|
|
|
|
Self::OrderedList => "ol",
|
|
Self::UnorderedList => "ul",
|
|
Self::ListItem => "li",
|
|
|
|
Self::Anchor => "a",
|
|
Self::Button => "button",
|
|
|
|
Self::HeaderGroup => "hgroup",
|
|
Self::Header1 => "h1",
|
|
Self::Header2 => "h2",
|
|
Self::Header3 => "h3",
|
|
Self::Header4 => "h4",
|
|
Self::Header5 => "h5",
|
|
Self::Header6 => "h6",
|
|
Self::Strong => "strong",
|
|
Self::Paragraph => "paragraph",
|
|
}
|
|
}
|
|
|
|
fn start(&self) -> String {
|
|
if *self != Self::JustText && *self != Self::Empty {
|
|
format!("<{}>", self.as_str())
|
|
} else {
|
|
String::new()
|
|
}
|
|
}
|
|
|
|
fn end(&self) -> String {
|
|
if *self != Self::JustText
|
|
&& *self != Self::Empty
|
|
&& *self != Self::Link
|
|
&& *self != Self::Input
|
|
{
|
|
format!("</{}>", self.as_str())
|
|
} else {
|
|
String::new()
|
|
}
|
|
}
|
|
}
|
|
|
|
pub enum SwapMethod {
|
|
InnerHtml,
|
|
OuterHtml,
|
|
BeforeEnd,
|
|
Refresh,
|
|
}
|
|
|
|
impl SwapMethod {
|
|
fn as_str(&self) -> &'static str {
|
|
match self {
|
|
SwapMethod::InnerHtml => "innerHTML",
|
|
SwapMethod::OuterHtml => "outerHTML",
|
|
SwapMethod::BeforeEnd => "beforeend",
|
|
SwapMethod::Refresh => "refresh",
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct HtmxBuilder {
|
|
tag: Tag,
|
|
attributes: HashMap<String, String>,
|
|
children: Vec<HtmxBuilder>,
|
|
text: Option<String>,
|
|
}
|
|
|
|
impl Build for HtmxBuilder {
|
|
#[must_use]
|
|
fn build(self) -> String {
|
|
let mut string = String::new();
|
|
|
|
// TODO: do this better
|
|
{
|
|
if self.tag != Tag::JustText && self.tag != Tag::Empty {
|
|
string.push_str(&format!("<{}", self.tag.as_str()));
|
|
}
|
|
|
|
for (attr, value) in self.attributes {
|
|
if value.is_empty() {
|
|
string.push_str(&format!(" {attr} "));
|
|
} else {
|
|
string.push_str(&format!(" {attr}='{value}' "));
|
|
}
|
|
}
|
|
if self.tag != Tag::JustText && self.tag != Tag::Empty {
|
|
string.push_str(">");
|
|
}
|
|
}
|
|
|
|
if let Some(text) = self.text {
|
|
string.push_str(&text);
|
|
}
|
|
|
|
for child in self.children {
|
|
string.push_str(&child.build());
|
|
}
|
|
|
|
string.push_str(&self.tag.end());
|
|
|
|
string
|
|
}
|
|
}
|
|
|
|
impl HtmxBuilder {
|
|
pub fn new(tag: Tag) -> Self {
|
|
Self {
|
|
tag,
|
|
attributes: HashMap::new(),
|
|
children: Vec::new(),
|
|
text: None,
|
|
}
|
|
}
|
|
|
|
pub fn push_builder(mut self, builder: HtmxBuilder) -> Self {
|
|
self.children.push(builder);
|
|
self
|
|
}
|
|
|
|
pub fn attribute(mut self, attr: &str, val: &str) -> Self {
|
|
self.attributes.insert(attr.to_string(), val.to_string());
|
|
self
|
|
}
|
|
|
|
pub fn hx_get(mut self, uri: &str) -> Self {
|
|
self.attribute("hx-get", uri)
|
|
}
|
|
|
|
pub fn hx_post(mut self, uri: &str) -> Self {
|
|
self.attribute("hx-post", uri)
|
|
}
|
|
|
|
pub fn hx_swap(mut self, swap_method: SwapMethod) -> Self {
|
|
self.attribute("hx-swap", swap_method.as_str())
|
|
}
|
|
|
|
pub fn hx_trigger(mut self, trigger: &str) -> Self {
|
|
self.attribute("hx-trigger", trigger)
|
|
}
|
|
|
|
pub fn hx_target(mut self, target: &str) -> Self {
|
|
self.attribute("hx-target", target)
|
|
}
|
|
|
|
pub fn html<F>(mut self, builder_fn: F) -> Self
|
|
where
|
|
F: FnOnce(HtmxBuilder) -> HtmxBuilder,
|
|
{
|
|
self.children.push(builder_fn(HtmxBuilder::new(Tag::Html)));
|
|
self
|
|
}
|
|
|
|
pub fn head<F>(mut self, builder_fn: F) -> Self
|
|
where
|
|
F: FnOnce(HtmxBuilder) -> HtmxBuilder,
|
|
{
|
|
self.children.push(builder_fn(HtmxBuilder::new(Tag::Head)));
|
|
self
|
|
}
|
|
|
|
pub fn title(mut self, text: &str) -> HtmxBuilder {
|
|
self.children.push(HtmxBuilder::new(Tag::Title).text(text));
|
|
self
|
|
}
|
|
|
|
pub fn body<F>(mut self, builder_fn: F) -> Self
|
|
where
|
|
F: FnOnce(HtmxBuilder) -> HtmxBuilder,
|
|
{
|
|
self.children.push(builder_fn(HtmxBuilder::new(Tag::Body)));
|
|
self
|
|
}
|
|
|
|
pub fn script(mut self, src: &str, integrity: Option<&str>) -> Self {
|
|
let mut b = HtmxBuilder::new(Tag::Script).attribute("src", src);
|
|
|
|
if let Some(integrity) = integrity {
|
|
b = b
|
|
.attribute("integrity", integrity)
|
|
.attribute("crossorigin", "anonymous");
|
|
}
|
|
|
|
self.children.push(b);
|
|
self
|
|
}
|
|
|
|
pub fn style_link(mut self, link: &str) -> Self {
|
|
self.children.push(
|
|
HtmxBuilder::new(Tag::Link)
|
|
.attribute("rel", "stylesheet")
|
|
.attribute("href", link),
|
|
);
|
|
self
|
|
}
|
|
|
|
pub fn flag(mut self, flag: &str) -> Self {
|
|
self.attributes.insert(flag.to_string(), "".to_string());
|
|
self
|
|
}
|
|
|
|
pub fn builder<F>(mut self, tag: Tag, builder_fn: F) -> Self
|
|
where
|
|
F: FnOnce(HtmxBuilder) -> HtmxBuilder,
|
|
{
|
|
self.children.push(builder_fn(HtmxBuilder::new(tag)));
|
|
self
|
|
}
|
|
|
|
pub fn builder_text(mut self, tag: Tag, text: &str) -> Self {
|
|
self.children.push(HtmxBuilder::new(tag).text(text));
|
|
self
|
|
}
|
|
|
|
pub fn nav<F>(mut self, builder_fn: F) -> Self
|
|
where
|
|
F: FnOnce(HtmxBuilder) -> HtmxBuilder,
|
|
{
|
|
self.children.push(builder_fn(HtmxBuilder::new(Tag::Nav)));
|
|
self
|
|
}
|
|
|
|
pub fn form<F>(mut self, builder_fn: F) -> HtmxBuilder
|
|
where
|
|
F: FnOnce(HtmxBuilder) -> HtmxBuilder,
|
|
{
|
|
self.children.push(builder_fn(HtmxBuilder::new(Tag::Form)));
|
|
self
|
|
}
|
|
|
|
pub fn label<F>(mut self, builder_fn: F) -> HtmxBuilder
|
|
where
|
|
F: FnOnce(HtmxBuilder) -> HtmxBuilder,
|
|
{
|
|
self.children.push(builder_fn(HtmxBuilder::new(Tag::Label)));
|
|
self
|
|
}
|
|
|
|
pub fn input<F>(mut self, builder_fn: F) -> HtmxBuilder
|
|
where
|
|
F: FnOnce(HtmxBuilder) -> HtmxBuilder,
|
|
{
|
|
self.children.push(builder_fn(HtmxBuilder::new(Tag::Input)));
|
|
self
|
|
}
|
|
|
|
pub fn button<F>(mut self, builder_fn: F) -> HtmxBuilder
|
|
where
|
|
F: FnOnce(HtmxBuilder) -> HtmxBuilder,
|
|
{
|
|
self.children
|
|
.push(builder_fn(HtmxBuilder::new(Tag::Button)));
|
|
self
|
|
}
|
|
|
|
pub fn ul<F>(mut self, builder_fn: F) -> HtmxBuilder
|
|
where
|
|
F: FnOnce(HtmxBuilder) -> HtmxBuilder,
|
|
{
|
|
self.children
|
|
.push(builder_fn(HtmxBuilder::new(Tag::UnorderedList)));
|
|
self
|
|
}
|
|
|
|
pub fn li<F>(mut self, builder_fn: F) -> HtmxBuilder
|
|
where
|
|
F: FnOnce(HtmxBuilder) -> HtmxBuilder,
|
|
{
|
|
self.children
|
|
.push(builder_fn(HtmxBuilder::new(Tag::ListItem)));
|
|
self
|
|
}
|
|
|
|
pub fn link(mut self, text: &str, href: &str) -> HtmxBuilder {
|
|
// TODO: add href attribute
|
|
self.children.push(
|
|
HtmxBuilder::new(Tag::Anchor)
|
|
.text(text)
|
|
.attribute("href", href),
|
|
);
|
|
self
|
|
}
|
|
|
|
pub fn text(mut self, text: &str) -> HtmxBuilder {
|
|
self.text = Some(text.to_string());
|
|
self
|
|
}
|
|
|
|
pub fn strong(mut self, text: &str) -> HtmxBuilder {
|
|
self.children.push(HtmxBuilder::new(Tag::Strong).text(text));
|
|
self
|
|
}
|
|
}
|