cache.rs 5.3 KB

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