2022-11-15 16:05:22 -08:00
|
|
|
use dioxus_core::prelude::*;
|
|
|
|
use std::fmt::Write;
|
|
|
|
|
2023-08-08 10:56:41 -07:00
|
|
|
use crate::renderer::{str_truthy, BOOL_ATTRS};
|
|
|
|
|
2022-12-07 15:29:32 -08:00
|
|
|
#[derive(Debug)]
|
2022-11-15 16:05:22 -08:00
|
|
|
pub struct StringCache {
|
|
|
|
pub segments: Vec<Segment>,
|
2022-11-30 11:24:13 -05:00
|
|
|
pub template: Template<'static>,
|
2022-11-15 16:05:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct StringChain {
|
|
|
|
pub segments: Vec<Segment>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
|
|
pub enum Segment {
|
|
|
|
Attr(usize),
|
|
|
|
Node(usize),
|
|
|
|
PreRendered(String),
|
2023-03-22 10:10:18 -05:00
|
|
|
/// A marker for where to insert a dynamic styles
|
|
|
|
StyleMarker {
|
|
|
|
// If the marker is inside a style tag or not
|
|
|
|
// This will be true if there are static styles
|
|
|
|
inside_style_tag: bool,
|
|
|
|
},
|
2023-04-07 09:35:32 -05:00
|
|
|
/// A marker for where to insert a dynamic inner html
|
|
|
|
InnerHtmlMarker,
|
2023-12-19 16:02:07 -06:00
|
|
|
/// A marker for where to insert a node id for an attribute
|
2023-12-27 10:17:18 -06:00
|
|
|
AttributeNodeMarker,
|
2023-12-19 16:02:07 -06:00
|
|
|
/// A marker for where to insert a node id for a root node
|
2023-12-27 10:17:18 -06:00
|
|
|
RootNodeMarker,
|
2022-11-15 16:05:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Write for StringChain {
|
|
|
|
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
|
|
|
match self.segments.last_mut() {
|
|
|
|
Some(Segment::PreRendered(s2)) => s2.push_str(s),
|
|
|
|
_ => self.segments.push(Segment::PreRendered(s.to_string())),
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl StringCache {
|
2023-12-19 16:02:07 -06:00
|
|
|
pub fn from_template(template: &VNode, prerender: bool) -> Result<Self, std::fmt::Error> {
|
2022-11-15 16:05:22 -08:00
|
|
|
let mut chain = StringChain::default();
|
|
|
|
|
|
|
|
let mut cur_path = vec![];
|
|
|
|
|
2022-12-19 19:28:44 -06:00
|
|
|
for (root_idx, root) in template.template.get().roots.iter().enumerate() {
|
2023-12-19 16:02:07 -06:00
|
|
|
Self::recurse(root, &mut cur_path, root_idx, true, prerender, &mut chain)?;
|
2022-11-15 16:05:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
segments: chain.segments,
|
2022-12-19 19:28:44 -06:00
|
|
|
template: template.template.get(),
|
2022-11-15 16:05:22 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn recurse(
|
|
|
|
root: &TemplateNode,
|
|
|
|
cur_path: &mut Vec<usize>,
|
|
|
|
root_idx: usize,
|
2023-12-19 16:02:07 -06:00
|
|
|
is_root: bool,
|
|
|
|
prerender: bool,
|
2022-11-15 16:05:22 -08:00
|
|
|
chain: &mut StringChain,
|
|
|
|
) -> Result<(), std::fmt::Error> {
|
|
|
|
match root {
|
|
|
|
TemplateNode::Element {
|
|
|
|
tag,
|
|
|
|
attrs,
|
|
|
|
children,
|
|
|
|
..
|
|
|
|
} => {
|
|
|
|
cur_path.push(root_idx);
|
2023-01-27 20:35:46 -06:00
|
|
|
write!(chain, "<{tag}")?;
|
2023-03-22 10:10:18 -05:00
|
|
|
// we need to collect the styles and write them at the end
|
|
|
|
let mut styles = Vec::new();
|
2023-04-07 09:35:32 -05:00
|
|
|
// we need to collect the inner html and write it at the end
|
|
|
|
let mut inner_html = None;
|
|
|
|
// we need to keep track of if we have dynamic attrs to know if we need to insert a style and inner_html marker
|
2023-12-27 10:17:18 -06:00
|
|
|
let mut has_dyn_attrs = false;
|
2022-11-15 16:05:22 -08:00
|
|
|
for attr in *attrs {
|
|
|
|
match attr {
|
2023-03-22 10:10:18 -05:00
|
|
|
TemplateAttribute::Static {
|
|
|
|
name,
|
|
|
|
value,
|
|
|
|
namespace,
|
|
|
|
} => {
|
2023-04-07 09:35:32 -05:00
|
|
|
if *name == "dangerous_inner_html" {
|
|
|
|
inner_html = Some(value);
|
|
|
|
} else if let Some("style") = namespace {
|
2023-03-22 10:10:18 -05:00
|
|
|
styles.push((name, value));
|
2023-08-08 11:39:36 -07:00
|
|
|
} else if BOOL_ATTRS.contains(name) {
|
2023-08-08 10:56:41 -07:00
|
|
|
if str_truthy(value) {
|
|
|
|
write!(chain, " {name}=\"{value}\"",)?;
|
|
|
|
}
|
2023-03-22 10:10:18 -05:00
|
|
|
} else {
|
|
|
|
write!(chain, " {name}=\"{value}\"")?;
|
|
|
|
}
|
2022-11-15 16:05:22 -08:00
|
|
|
}
|
2022-12-05 16:47:04 -08:00
|
|
|
TemplateAttribute::Dynamic { id: index } => {
|
2023-12-19 16:02:07 -06:00
|
|
|
let index = *index;
|
|
|
|
chain.segments.push(Segment::Attr(index));
|
2023-12-27 10:17:18 -06:00
|
|
|
has_dyn_attrs = true
|
2022-11-15 16:05:22 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-22 10:10:18 -05:00
|
|
|
|
|
|
|
// write the styles
|
|
|
|
if !styles.is_empty() {
|
|
|
|
write!(chain, " style=\"")?;
|
|
|
|
for (name, value) in styles {
|
|
|
|
write!(chain, "{name}:{value};")?;
|
|
|
|
}
|
|
|
|
chain.segments.push(Segment::StyleMarker {
|
|
|
|
inside_style_tag: true,
|
|
|
|
});
|
|
|
|
write!(chain, "\"")?;
|
2023-12-27 10:17:18 -06:00
|
|
|
} else if has_dyn_attrs {
|
2023-03-22 10:10:18 -05:00
|
|
|
chain.segments.push(Segment::StyleMarker {
|
|
|
|
inside_style_tag: false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-12-19 16:02:07 -06:00
|
|
|
// write the id if we are prerendering and this is either a root node or a node with a dynamic attribute
|
2023-12-27 10:17:18 -06:00
|
|
|
if prerender && (has_dyn_attrs || is_root) {
|
2023-12-19 16:02:07 -06:00
|
|
|
write!(chain, " data-node-hydration=\"")?;
|
2023-12-27 10:17:18 -06:00
|
|
|
if has_dyn_attrs {
|
|
|
|
chain.segments.push(Segment::AttributeNodeMarker);
|
2023-12-19 16:02:07 -06:00
|
|
|
} else if is_root {
|
2023-12-27 10:17:18 -06:00
|
|
|
chain.segments.push(Segment::RootNodeMarker);
|
2023-12-19 16:02:07 -06:00
|
|
|
}
|
|
|
|
write!(chain, "\"")?;
|
|
|
|
}
|
|
|
|
|
2022-12-06 17:44:29 -08:00
|
|
|
if children.is_empty() && tag_is_self_closing(tag) {
|
2022-11-15 16:05:22 -08:00
|
|
|
write!(chain, "/>")?;
|
|
|
|
} else {
|
|
|
|
write!(chain, ">")?;
|
2023-04-07 09:35:32 -05:00
|
|
|
// Write the static inner html, or insert a marker if dynamic inner html is possible
|
|
|
|
if let Some(inner_html) = inner_html {
|
|
|
|
chain.write_str(inner_html)?;
|
2023-12-27 10:17:18 -06:00
|
|
|
} else if has_dyn_attrs {
|
2023-04-07 09:35:32 -05:00
|
|
|
chain.segments.push(Segment::InnerHtmlMarker);
|
|
|
|
}
|
|
|
|
|
2022-11-15 16:05:22 -08:00
|
|
|
for child in *children {
|
2023-12-19 16:02:07 -06:00
|
|
|
Self::recurse(child, cur_path, root_idx, false, prerender, chain)?;
|
2022-11-15 16:05:22 -08:00
|
|
|
}
|
2023-01-27 20:35:46 -06:00
|
|
|
write!(chain, "</{tag}>")?;
|
2022-11-15 16:05:22 -08:00
|
|
|
}
|
|
|
|
cur_path.pop();
|
|
|
|
}
|
2023-02-14 08:44:01 -06:00
|
|
|
TemplateNode::Text { text } => {
|
|
|
|
write!(
|
|
|
|
chain,
|
|
|
|
"{}",
|
|
|
|
askama_escape::escape(text, askama_escape::Html)
|
|
|
|
)?;
|
|
|
|
}
|
2022-12-05 16:08:41 -08:00
|
|
|
TemplateNode::Dynamic { id: idx } | TemplateNode::DynamicText { id: idx } => {
|
2022-11-15 16:05:22 -08:00
|
|
|
chain.segments.push(Segment::Node(*idx))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tag_is_self_closing(tag: &str) -> bool {
|
2022-12-06 17:44:29 -08:00
|
|
|
matches!(
|
|
|
|
tag,
|
|
|
|
"area"
|
|
|
|
| "base"
|
|
|
|
| "br"
|
|
|
|
| "col"
|
|
|
|
| "embed"
|
|
|
|
| "hr"
|
|
|
|
| "img"
|
|
|
|
| "input"
|
|
|
|
| "link"
|
|
|
|
| "meta"
|
|
|
|
| "param"
|
|
|
|
| "source"
|
|
|
|
| "track"
|
|
|
|
| "wbr"
|
|
|
|
)
|
2022-11-15 16:05:22 -08:00
|
|
|
}
|