node.rs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. use super::*;
  2. use proc_macro2::{Span, TokenStream as TokenStream2};
  3. use quote::{quote, ToTokens, TokenStreamExt};
  4. use syn::{
  5. parse::{Parse, ParseStream},
  6. spanned::Spanned,
  7. token, Block, Expr, ExprIf, LitStr, Pat, Result,
  8. };
  9. /*
  10. Parse
  11. -> div {}
  12. -> Component {}
  13. -> component()
  14. -> "text {with_args}"
  15. -> (0..10).map(|f| rsx!("asd")), // <--- notice the comma - must be a complete expr
  16. */
  17. #[derive(PartialEq, Eq, Clone, Debug, Hash)]
  18. pub enum BodyNode {
  19. Element(Element),
  20. Component(Component),
  21. ForLoop(ForLoop),
  22. IfChain(ExprIf),
  23. Text(IfmtInput),
  24. RawExpr(Expr),
  25. }
  26. impl BodyNode {
  27. pub fn is_litstr(&self) -> bool {
  28. matches!(self, BodyNode::Text(_))
  29. }
  30. pub fn span(&self) -> Span {
  31. match self {
  32. BodyNode::Element(el) => el.name.span(),
  33. BodyNode::Component(component) => component.name.span(),
  34. BodyNode::Text(text) => text.source.span(),
  35. BodyNode::RawExpr(exp) => exp.span(),
  36. BodyNode::ForLoop(fl) => fl.for_token.span(),
  37. BodyNode::IfChain(f) => f.if_token.span(),
  38. }
  39. }
  40. }
  41. impl Parse for BodyNode {
  42. fn parse(stream: ParseStream) -> Result<Self> {
  43. if stream.peek(LitStr) {
  44. return Ok(BodyNode::Text(stream.parse()?));
  45. }
  46. let body_stream = stream.fork();
  47. if let Ok(path) = body_stream.parse::<syn::Path>() {
  48. // this is an Element if path match of:
  49. // - one ident
  50. // - followed by `{`
  51. // - 1st char is lowercase
  52. // - no underscores (reserved for components)
  53. //
  54. // example:
  55. // div {}
  56. if let Some(ident) = path.get_ident() {
  57. let el_name = ident.to_string();
  58. let first_char = el_name.chars().next().unwrap();
  59. if body_stream.peek(token::Brace)
  60. && first_char.is_ascii_lowercase()
  61. && !el_name.contains('_')
  62. {
  63. return Ok(BodyNode::Element(stream.parse::<Element>()?));
  64. }
  65. }
  66. // Otherwise this should be Component, allowed syntax:
  67. // - syn::Path
  68. // - PathArguments can only apper in last segment
  69. // - followed by `{` or `(`, note `(` cannot be used with one ident
  70. //
  71. // example
  72. // Div {}
  73. // ::Div {}
  74. // crate::Div {}
  75. // component {} <-- already handled by elements
  76. // ::component {}
  77. // crate::component{}
  78. // Input::<InputProps<'_, i32> {}
  79. // crate::Input::<InputProps<'_, i32> {}
  80. if body_stream.peek(token::Brace) {
  81. Component::validate_component_path(&path)?;
  82. return Ok(BodyNode::Component(stream.parse()?));
  83. }
  84. }
  85. // Transform for loops into into_iter calls
  86. if stream.peek(Token![for]) {
  87. let _f = stream.parse::<Token![for]>()?;
  88. let pat = stream.parse::<Pat>()?;
  89. let _i = stream.parse::<Token![in]>()?;
  90. let expr = stream.parse::<Box<Expr>>()?;
  91. let body = stream.parse::<Block>()?;
  92. return Ok(BodyNode::ForLoop(ForLoop {
  93. for_token: _f,
  94. pat,
  95. in_token: _i,
  96. expr,
  97. body,
  98. }));
  99. }
  100. // Transform unterminated if statements into terminated optional if statements
  101. if stream.peek(Token![if]) {
  102. return Ok(BodyNode::IfChain(stream.parse()?));
  103. }
  104. Ok(BodyNode::RawExpr(stream.parse::<Expr>()?))
  105. }
  106. }
  107. impl ToTokens for BodyNode {
  108. fn to_tokens(&self, tokens: &mut TokenStream2) {
  109. match &self {
  110. BodyNode::Element(el) => el.to_tokens(tokens),
  111. BodyNode::Component(comp) => comp.to_tokens(tokens),
  112. BodyNode::Text(txt) => tokens.append_all(quote! {
  113. __cx.text(#txt)
  114. }),
  115. BodyNode::RawExpr(exp) => tokens.append_all(quote! {
  116. __cx.fragment_from_iter(#exp)
  117. }),
  118. BodyNode::ForLoop(exp) => {
  119. let ForLoop {
  120. pat, expr, body, ..
  121. } = exp;
  122. tokens.append_all(quote! {
  123. __cx.fragment_from_iter(
  124. (#expr).into_iter().map(|#pat| {
  125. #body
  126. })
  127. )
  128. })
  129. }
  130. BodyNode::IfChain(chain) => {
  131. if is_if_chain_terminated(chain) {
  132. tokens.append_all(quote! {
  133. __cx.fragment_from_iter(#chain)
  134. });
  135. } else {
  136. let ExprIf {
  137. cond,
  138. then_branch,
  139. else_branch,
  140. ..
  141. } = chain;
  142. let mut body = TokenStream2::new();
  143. body.append_all(quote! {
  144. if #cond {
  145. Some(#then_branch)
  146. }
  147. });
  148. let mut elif = else_branch;
  149. while let Some((_, ref branch)) = elif {
  150. match branch.as_ref() {
  151. Expr::If(ref eelif) => {
  152. let ExprIf {
  153. cond,
  154. then_branch,
  155. else_branch,
  156. ..
  157. } = eelif;
  158. body.append_all(quote! {
  159. else if #cond {
  160. Some(#then_branch)
  161. }
  162. });
  163. elif = else_branch;
  164. }
  165. _ => {
  166. body.append_all(quote! {
  167. else {
  168. #branch
  169. }
  170. });
  171. break;
  172. }
  173. }
  174. }
  175. body.append_all(quote! {
  176. else { None }
  177. });
  178. tokens.append_all(quote! {
  179. __cx.fragment_from_iter(#body)
  180. });
  181. }
  182. }
  183. }
  184. }
  185. }
  186. #[derive(PartialEq, Eq, Clone, Debug, Hash)]
  187. pub struct ForLoop {
  188. pub for_token: Token![for],
  189. pub pat: Pat,
  190. pub in_token: Token![in],
  191. pub expr: Box<Expr>,
  192. pub body: Block,
  193. }
  194. fn is_if_chain_terminated(chain: &ExprIf) -> bool {
  195. let mut current = chain;
  196. loop {
  197. if let Some((_, else_block)) = &current.else_branch {
  198. if let Expr::If(else_if) = else_block.as_ref() {
  199. current = else_if;
  200. } else {
  201. return true;
  202. }
  203. } else {
  204. return false;
  205. }
  206. }
  207. }