component.rs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. //! Parse components into the VComponent VNode
  2. //! ==========================================
  3. //!
  4. //! This parsing path emerges from [`AmbiguousElement`] which supports validation of the vcomponent format.
  5. //! We can be reasonably sure that whatever enters this parsing path is in the right format.
  6. //! This feature must support
  7. //! - [x] Namespaced components
  8. //! - [x] Fields
  9. //! - [x] Componentbuilder synax
  10. //! - [x] Optional commas
  11. //! - [ ] Children
  12. //! - [ ] Keys
  13. //! - [ ] Properties spreading with with `..` syntax
  14. use super::*;
  15. use proc_macro2::TokenStream as TokenStream2;
  16. use quote::{quote, ToTokens, TokenStreamExt};
  17. use syn::{
  18. ext::IdentExt,
  19. parse::{Parse, ParseBuffer, ParseStream},
  20. token, Expr, Ident, LitStr, Result, Token,
  21. };
  22. pub struct Component {
  23. name: syn::Path,
  24. body: Vec<ComponentField>,
  25. children: Vec<BodyNode>,
  26. manual_props: Option<Expr>,
  27. }
  28. impl Parse for Component {
  29. fn parse(stream: ParseStream) -> Result<Self> {
  30. let name = syn::Path::parse_mod_style(stream)?;
  31. let content: ParseBuffer;
  32. // if we see a `{` then we have a block
  33. // else parse as a function-like call
  34. if stream.peek(token::Brace) {
  35. syn::braced!(content in stream);
  36. } else {
  37. syn::parenthesized!(content in stream);
  38. }
  39. let mut body = Vec::new();
  40. let mut children = Vec::new();
  41. let mut manual_props = None;
  42. while !content.is_empty() {
  43. // if we splat into a component then we're merging properties
  44. if content.peek(Token![..]) {
  45. content.parse::<Token![..]>()?;
  46. manual_props = Some(content.parse::<Expr>()?);
  47. } else if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
  48. body.push(content.parse::<ComponentField>()?);
  49. } else {
  50. children.push(content.parse::<BodyNode>()?);
  51. }
  52. if content.peek(Token![,]) {
  53. let _ = content.parse::<Token![,]>();
  54. }
  55. }
  56. Ok(Self {
  57. name,
  58. body,
  59. children,
  60. manual_props,
  61. })
  62. }
  63. }
  64. impl ToTokens for Component {
  65. fn to_tokens(&self, tokens: &mut TokenStream2) {
  66. let name = &self.name;
  67. let mut has_key = None;
  68. let builder = match &self.manual_props {
  69. Some(manual_props) => {
  70. let mut toks = quote! {
  71. let mut __manual_props = #manual_props;
  72. };
  73. for field in &self.body {
  74. if field.name == "key" {
  75. has_key = Some(field);
  76. } else {
  77. let name = &field.name;
  78. let val = &field.content;
  79. toks.append_all(quote! {
  80. __manual_props.#name = #val;
  81. });
  82. }
  83. }
  84. toks.append_all(quote! {
  85. __manual_props
  86. });
  87. quote! {{
  88. #toks
  89. }}
  90. }
  91. None => {
  92. let mut toks = quote! { fc_to_builder(#name) };
  93. for field in &self.body {
  94. match field.name.to_string().as_str() {
  95. "key" => {
  96. //
  97. has_key = Some(field);
  98. }
  99. _ => toks.append_all(quote! {#field}),
  100. }
  101. }
  102. if !self.children.is_empty() {
  103. let childs = &self.children;
  104. toks.append_all(quote! {
  105. .children(__cx.create_children([ #( #childs ),* ]))
  106. });
  107. }
  108. toks.append_all(quote! {
  109. .build()
  110. });
  111. toks
  112. }
  113. };
  114. let key_token = match has_key {
  115. Some(field) => {
  116. let inners = &field.content;
  117. quote! { Some(format_args_f!(#inners)) }
  118. }
  119. None => quote! { None },
  120. };
  121. tokens.append_all(quote! {
  122. __cx.component(
  123. #name,
  124. #builder,
  125. #key_token,
  126. )
  127. })
  128. }
  129. }
  130. // the struct's fields info
  131. pub struct ComponentField {
  132. name: Ident,
  133. content: ContentField,
  134. }
  135. enum ContentField {
  136. ManExpr(Expr),
  137. Formatted(LitStr),
  138. OnHandlerRaw(Expr),
  139. }
  140. impl ToTokens for ContentField {
  141. fn to_tokens(&self, tokens: &mut TokenStream2) {
  142. match self {
  143. ContentField::ManExpr(e) => e.to_tokens(tokens),
  144. ContentField::Formatted(s) => tokens.append_all(quote! {
  145. __cx.raw_text(format_args_f!(#s)).0
  146. }),
  147. ContentField::OnHandlerRaw(e) => tokens.append_all(quote! {
  148. __cx.bump().alloc(#e)
  149. }),
  150. }
  151. }
  152. }
  153. impl Parse for ComponentField {
  154. fn parse(input: ParseStream) -> Result<Self> {
  155. let name = Ident::parse_any(input)?;
  156. input.parse::<Token![:]>()?;
  157. if name.to_string().starts_with("on") {
  158. let content = ContentField::OnHandlerRaw(input.parse()?);
  159. return Ok(Self { name, content });
  160. }
  161. if name.to_string() == "key" {
  162. let content = ContentField::ManExpr(input.parse()?);
  163. return Ok(Self { name, content });
  164. }
  165. if input.peek(LitStr) && input.peek2(Token![,]) {
  166. let t: LitStr = input.fork().parse()?;
  167. if is_literal_foramtted(&t) {
  168. let content = ContentField::Formatted(input.parse()?);
  169. return Ok(Self { name, content });
  170. }
  171. }
  172. if input.peek(LitStr) && input.peek2(LitStr) {
  173. let item = input.parse::<LitStr>().unwrap();
  174. proc_macro_error::emit_error!(item, "This attribute is misisng a trailing comma")
  175. }
  176. let content = ContentField::ManExpr(input.parse()?);
  177. Ok(Self { name, content })
  178. }
  179. }
  180. impl ToTokens for ComponentField {
  181. fn to_tokens(&self, tokens: &mut TokenStream2) {
  182. let ComponentField { name, content, .. } = self;
  183. tokens.append_all(quote! {
  184. .#name(#content)
  185. })
  186. }
  187. }
  188. fn is_literal_foramtted(lit: &LitStr) -> bool {
  189. let s = lit.value();
  190. let mut chars = s.chars();
  191. while let Some(next) = chars.next() {
  192. if next == '{' {
  193. let nen = chars.next();
  194. if nen == Some('{') {
  195. return true;
  196. }
  197. }
  198. }
  199. false
  200. }