cache.rs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. use dioxus_core::prelude::*;
  2. use std::fmt::Write;
  3. use crate::renderer::{str_truthy, BOOL_ATTRS};
  4. #[derive(Debug)]
  5. pub struct StringCache {
  6. pub segments: Vec<Segment>,
  7. pub template: Template,
  8. }
  9. #[derive(Default)]
  10. pub struct StringChain {
  11. pub segments: Vec<Segment>,
  12. }
  13. #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
  14. pub(crate) enum Segment {
  15. Attr(usize),
  16. Node(usize),
  17. PreRendered(String),
  18. /// A marker for where to insert a dynamic styles
  19. StyleMarker {
  20. // If the marker is inside a style tag or not
  21. // This will be true if there are static styles
  22. inside_style_tag: bool,
  23. },
  24. /// A marker for where to insert a dynamic inner html
  25. InnerHtmlMarker,
  26. /// A marker for where to insert a node id for an attribute
  27. AttributeNodeMarker,
  28. /// A marker for where to insert a node id for a root node
  29. RootNodeMarker,
  30. }
  31. impl std::fmt::Write for StringChain {
  32. fn write_str(&mut self, s: &str) -> std::fmt::Result {
  33. match self.segments.last_mut() {
  34. Some(Segment::PreRendered(s2)) => s2.push_str(s),
  35. _ => self.segments.push(Segment::PreRendered(s.to_string())),
  36. }
  37. Ok(())
  38. }
  39. }
  40. impl StringCache {
  41. pub fn from_template(template: &VNode, prerender: bool) -> Result<Self, std::fmt::Error> {
  42. let mut chain = StringChain::default();
  43. let mut cur_path = vec![];
  44. for (root_idx, root) in template.template.get().roots.iter().enumerate() {
  45. Self::recurse(root, &mut cur_path, root_idx, true, prerender, &mut chain)?;
  46. }
  47. Ok(Self {
  48. segments: chain.segments,
  49. template: template.template.get(),
  50. })
  51. }
  52. fn recurse(
  53. root: &TemplateNode,
  54. cur_path: &mut Vec<usize>,
  55. root_idx: usize,
  56. is_root: bool,
  57. prerender: bool,
  58. chain: &mut StringChain,
  59. ) -> Result<(), std::fmt::Error> {
  60. match root {
  61. TemplateNode::Element {
  62. tag,
  63. attrs,
  64. children,
  65. ..
  66. } => {
  67. cur_path.push(root_idx);
  68. write!(chain, "<{tag}")?;
  69. // we need to collect the styles and write them at the end
  70. let mut styles = Vec::new();
  71. // we need to collect the inner html and write it at the end
  72. let mut inner_html = None;
  73. // we need to keep track of if we have dynamic attrs to know if we need to insert a style and inner_html marker
  74. let mut has_dyn_attrs = false;
  75. for attr in *attrs {
  76. match attr {
  77. TemplateAttribute::Static {
  78. name,
  79. value,
  80. namespace,
  81. } => {
  82. if *name == "dangerous_inner_html" {
  83. inner_html = Some(value);
  84. } else if let Some("style") = namespace {
  85. styles.push((name, value));
  86. } else if BOOL_ATTRS.contains(name) {
  87. if str_truthy(value) {
  88. write!(chain, " {name}=\"{value}\"",)?;
  89. }
  90. } else {
  91. write!(chain, " {name}=\"{value}\"")?;
  92. }
  93. }
  94. TemplateAttribute::Dynamic { id: index } => {
  95. let index = *index;
  96. chain.segments.push(Segment::Attr(index));
  97. has_dyn_attrs = true
  98. }
  99. }
  100. }
  101. // write the styles
  102. if !styles.is_empty() {
  103. write!(chain, " style=\"")?;
  104. for (name, value) in styles {
  105. write!(chain, "{name}:{value};")?;
  106. }
  107. chain.segments.push(Segment::StyleMarker {
  108. inside_style_tag: true,
  109. });
  110. write!(chain, "\"")?;
  111. } else if has_dyn_attrs {
  112. chain.segments.push(Segment::StyleMarker {
  113. inside_style_tag: false,
  114. });
  115. }
  116. // write the id if we are prerendering and this is either a root node or a node with a dynamic attribute
  117. if prerender && (has_dyn_attrs || is_root) {
  118. write!(chain, " data-node-hydration=\"")?;
  119. if has_dyn_attrs {
  120. chain.segments.push(Segment::AttributeNodeMarker);
  121. } else if is_root {
  122. chain.segments.push(Segment::RootNodeMarker);
  123. }
  124. write!(chain, "\"")?;
  125. }
  126. if children.is_empty() && tag_is_self_closing(tag) {
  127. write!(chain, "/>")?;
  128. } else {
  129. write!(chain, ">")?;
  130. // Write the static inner html, or insert a marker if dynamic inner html is possible
  131. if let Some(inner_html) = inner_html {
  132. chain.write_str(inner_html)?;
  133. } else if has_dyn_attrs {
  134. chain.segments.push(Segment::InnerHtmlMarker);
  135. }
  136. for child in *children {
  137. Self::recurse(child, cur_path, root_idx, false, prerender, chain)?;
  138. }
  139. write!(chain, "</{tag}>")?;
  140. }
  141. cur_path.pop();
  142. }
  143. TemplateNode::Text { text } => {
  144. // write the id if we are prerendering and this is a root node that may need to be removed in the future
  145. if prerender && is_root {
  146. write!(chain, "<!--node-id")?;
  147. chain.segments.push(Segment::RootNodeMarker);
  148. write!(chain, "-->")?;
  149. }
  150. write!(
  151. chain,
  152. "{}",
  153. askama_escape::escape(text, askama_escape::Html)
  154. )?;
  155. if prerender && is_root {
  156. write!(chain, "<!--#-->")?;
  157. }
  158. }
  159. TemplateNode::Dynamic { id: idx } | TemplateNode::DynamicText { id: idx } => {
  160. chain.segments.push(Segment::Node(*idx))
  161. }
  162. }
  163. Ok(())
  164. }
  165. }
  166. fn tag_is_self_closing(tag: &str) -> bool {
  167. matches!(
  168. tag,
  169. "area"
  170. | "base"
  171. | "br"
  172. | "col"
  173. | "embed"
  174. | "hr"
  175. | "img"
  176. | "input"
  177. | "link"
  178. | "meta"
  179. | "param"
  180. | "source"
  181. | "track"
  182. | "wbr"
  183. )
  184. }