captuered_context.rs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. use std::collections::HashSet;
  2. use dioxus_core::{Listener, VNode};
  3. use dioxus_rsx::{
  4. BodyNode, CallBody, Component, ElementAttr, ElementAttrNamed, IfmtInput, Segment,
  5. };
  6. use quote::{quote, ToTokens, TokenStreamExt};
  7. use syn::{Expr, Ident, LitStr, Result};
  8. use crate::{attributes::attrbute_to_static_str, CodeLocation};
  9. #[derive(Default)]
  10. pub struct CapturedContextBuilder {
  11. pub ifmted: Vec<IfmtInput>,
  12. pub components: Vec<Component>,
  13. pub iterators: Vec<BodyNode>,
  14. pub captured_expressions: Vec<Expr>,
  15. pub listeners: Vec<ElementAttrNamed>,
  16. pub custom_context: Option<Ident>,
  17. pub custom_attributes: HashSet<LitStr>,
  18. }
  19. impl CapturedContextBuilder {
  20. pub fn extend(&mut self, other: CapturedContextBuilder) {
  21. self.ifmted.extend(other.ifmted);
  22. self.components.extend(other.components);
  23. self.iterators.extend(other.iterators);
  24. self.listeners.extend(other.listeners);
  25. self.captured_expressions.extend(other.captured_expressions);
  26. self.custom_attributes.extend(other.custom_attributes);
  27. }
  28. pub fn from_call_body(body: CallBody) -> Result<Self> {
  29. let mut new = Self {
  30. custom_context: body.custom_context,
  31. ..Default::default()
  32. };
  33. for node in body.roots {
  34. new.extend(Self::find_captured(node)?);
  35. }
  36. Ok(new)
  37. }
  38. fn find_captured(node: BodyNode) -> Result<Self> {
  39. let mut captured = CapturedContextBuilder::default();
  40. match node {
  41. BodyNode::Element(el) => {
  42. for attr in el.attributes {
  43. match attr.attr {
  44. ElementAttr::AttrText { value, .. } => {
  45. let value_tokens = value.to_token_stream();
  46. let formated: IfmtInput = syn::parse2(value_tokens)?;
  47. captured.ifmted.push(formated);
  48. }
  49. ElementAttr::CustomAttrText { value, name } => {
  50. captured.custom_attributes.insert(name);
  51. let value_tokens = value.to_token_stream();
  52. let formated: IfmtInput = syn::parse2(value_tokens)?;
  53. captured.ifmted.push(formated);
  54. }
  55. ElementAttr::AttrExpression { name: _, value } => {
  56. captured.captured_expressions.push(value);
  57. }
  58. ElementAttr::CustomAttrExpression { name, value } => {
  59. captured.custom_attributes.insert(name);
  60. captured.captured_expressions.push(value);
  61. }
  62. ElementAttr::EventTokens { .. } => captured.listeners.push(attr),
  63. }
  64. }
  65. if let Some(key) = el.key {
  66. let value_tokens = key.to_token_stream();
  67. let formated: IfmtInput = syn::parse2(value_tokens)?;
  68. captured.ifmted.push(formated);
  69. }
  70. for child in el.children {
  71. captured.extend(Self::find_captured(child)?);
  72. }
  73. }
  74. BodyNode::Component(comp) => {
  75. captured.components.push(comp);
  76. }
  77. BodyNode::Text(t) => {
  78. let tokens = t.to_token_stream();
  79. let formated: IfmtInput = syn::parse2(tokens).unwrap();
  80. captured.ifmted.push(formated);
  81. }
  82. BodyNode::RawExpr(_) => captured.iterators.push(node),
  83. }
  84. Ok(captured)
  85. }
  86. }
  87. impl ToTokens for CapturedContextBuilder {
  88. fn to_tokens(&self, tokens: &mut quote::__private::TokenStream) {
  89. let CapturedContextBuilder {
  90. ifmted,
  91. components,
  92. iterators,
  93. captured_expressions,
  94. listeners,
  95. custom_context: _,
  96. custom_attributes,
  97. } = self;
  98. let listeners_str = listeners
  99. .iter()
  100. .map(|comp| comp.to_token_stream().to_string());
  101. let compontents_str = components
  102. .iter()
  103. .map(|comp| comp.to_token_stream().to_string());
  104. let iterators_str = iterators.iter().map(|node| match node {
  105. BodyNode::RawExpr(expr) => expr.to_token_stream().to_string(),
  106. _ => unreachable!(),
  107. });
  108. let captured: Vec<_> = ifmted
  109. .iter()
  110. .flat_map(|input| input.segments.iter())
  111. .filter_map(|seg| match seg {
  112. Segment::Formatted {
  113. format_args,
  114. segment,
  115. } => {
  116. let expr = segment.to_token_stream();
  117. let as_string = expr.to_string();
  118. let format_expr = if format_args.is_empty() {
  119. "{".to_string() + &format_args + "}"
  120. } else {
  121. "{".to_string() + ":" + &format_args + "}"
  122. };
  123. Some(quote! {
  124. FormattedArg{
  125. expr: #as_string,
  126. format_args: #format_args,
  127. result: format!(#format_expr, #expr)
  128. }
  129. })
  130. }
  131. _ => None,
  132. })
  133. .collect();
  134. let captured_attr_expressions_text = captured_expressions
  135. .iter()
  136. .map(|e| format!("{}", e.to_token_stream()));
  137. let custom_attributes_iter = custom_attributes.iter();
  138. tokens.append_all(quote! {
  139. CapturedContext {
  140. captured: IfmtArgs{
  141. named_args: vec![#(#captured),*]
  142. },
  143. components: vec![#((#compontents_str, #components)),*],
  144. iterators: vec![#((#iterators_str, #iterators)),*],
  145. expressions: vec![#((#captured_attr_expressions_text, #captured_expressions.to_string())),*],
  146. listeners: vec![#((#listeners_str, #listeners)),*],
  147. custom_attributes: &[#(#custom_attributes_iter),*],
  148. location: code_location.clone()
  149. }
  150. })
  151. }
  152. }
  153. pub struct CapturedContext<'a> {
  154. // map of the variable name to the formated value
  155. pub captured: IfmtArgs,
  156. // map of the attribute name and element path to the formated value
  157. // pub captured_attribute_values: IfmtArgs,
  158. // the only thing we can update in component is the children
  159. pub components: Vec<(&'static str, VNode<'a>)>,
  160. // we can't reasonably interpert iterators, so they are staticly inserted
  161. pub iterators: Vec<(&'static str, VNode<'a>)>,
  162. // map expression to the value resulting from the expression
  163. pub expressions: Vec<(&'static str, String)>,
  164. // map listener code to the resulting listener
  165. pub listeners: Vec<(&'static str, Listener<'a>)>,
  166. // used to map custom attrbutes form &'a str to &'static str
  167. pub custom_attributes: &'static [&'static str],
  168. // used to provide better error messages
  169. pub location: CodeLocation,
  170. }
  171. impl<'a> CapturedContext<'a> {
  172. pub fn attrbute_to_static_str(
  173. &self,
  174. attr: &str,
  175. tag: &'static str,
  176. ns: Option<&'static str>,
  177. literal: bool,
  178. ) -> Option<(&'static str, Option<&'static str>)> {
  179. if let Some(attr) = attrbute_to_static_str(attr, tag, ns) {
  180. Some(attr)
  181. } else if literal {
  182. self.custom_attributes
  183. .iter()
  184. .find(|attribute| attr == **attribute)
  185. .map(|attribute| (*attribute, None))
  186. } else {
  187. None
  188. }
  189. }
  190. }
  191. pub struct IfmtArgs {
  192. // All expressions that have been resolved
  193. pub named_args: Vec<FormattedArg>,
  194. }
  195. /// A formated segment that has been resolved
  196. pub struct FormattedArg {
  197. pub expr: &'static str,
  198. pub format_args: &'static str,
  199. pub result: String,
  200. }