captuered_context.rs 6.5 KB

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