component.rs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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, ExprClosure, Ident, Result, Token,
  21. };
  22. pub struct Component {
  23. // accept any path-like argument
  24. name: syn::Path,
  25. body: Vec<ComponentField>,
  26. children: Vec<BodyNode>,
  27. manual_props: Option<Expr>,
  28. }
  29. impl Parse for Component {
  30. fn parse(stream: ParseStream) -> Result<Self> {
  31. // let name = s.parse::<syn::ExprPath>()?;
  32. // todo: look into somehow getting the crate/super/etc
  33. let name = syn::Path::parse_mod_style(stream)?;
  34. // parse the guts
  35. let content: ParseBuffer;
  36. syn::braced!(content in stream);
  37. let mut body: Vec<ComponentField> = Vec::new();
  38. let mut children: Vec<BodyNode> = Vec::new();
  39. let mut manual_props = None;
  40. parse_component_body(
  41. &content,
  42. &BodyParseConfig {
  43. allow_children: true,
  44. allow_fields: true,
  45. allow_manual_props: true,
  46. },
  47. &mut body,
  48. &mut children,
  49. &mut manual_props,
  50. )?;
  51. Ok(Self {
  52. name,
  53. body,
  54. children,
  55. manual_props,
  56. })
  57. }
  58. }
  59. pub struct BodyParseConfig {
  60. pub allow_fields: bool,
  61. pub allow_children: bool,
  62. pub allow_manual_props: bool,
  63. }
  64. // todo: unify this body parsing for both elements and components
  65. // both are style rather ad-hoc, though components are currently more configured
  66. pub fn parse_component_body(
  67. content: &ParseBuffer,
  68. cfg: &BodyParseConfig,
  69. body: &mut Vec<ComponentField>,
  70. children: &mut Vec<BodyNode>,
  71. manual_props: &mut Option<Expr>,
  72. ) -> Result<()> {
  73. 'parsing: loop {
  74. // [1] Break if empty
  75. if content.is_empty() {
  76. break 'parsing;
  77. }
  78. if content.peek(Token![..]) {
  79. if !cfg.allow_manual_props {
  80. return Err(Error::new(
  81. content.span(),
  82. "Props spread syntax is not allowed in this context. \nMake to only use the elipsis `..` in Components.",
  83. ));
  84. }
  85. content.parse::<Token![..]>()?;
  86. *manual_props = Some(content.parse::<Expr>()?);
  87. } else if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
  88. if !cfg.allow_fields {
  89. return Err(Error::new(
  90. content.span(),
  91. "Property fields is not allowed in this context. \nMake to only use fields in Components or Elements.",
  92. ));
  93. }
  94. body.push(content.parse::<ComponentField>()?);
  95. } else {
  96. if !cfg.allow_children {
  97. return Err(Error::new(
  98. content.span(),
  99. "This item is not allowed to accept children.",
  100. ));
  101. }
  102. children.push(content.parse::<BodyNode>()?);
  103. }
  104. // consume comma if it exists
  105. // we don't actually care if there *are* commas between attrs
  106. if content.peek(Token![,]) {
  107. let _ = content.parse::<Token![,]>();
  108. }
  109. }
  110. Ok(())
  111. }
  112. impl ToTokens for Component {
  113. fn to_tokens(&self, tokens: &mut TokenStream2) {
  114. let name = &self.name;
  115. let mut has_key = None;
  116. let builder = match &self.manual_props {
  117. Some(manual_props) => {
  118. let mut toks = quote! {
  119. let mut __manual_props = #manual_props;
  120. };
  121. for field in &self.body {
  122. if field.name.to_string() == "key" {
  123. has_key = Some(field);
  124. } else {
  125. let name = &field.name;
  126. let val = &field.content;
  127. toks.append_all(quote! {
  128. __manual_props.#name = #val;
  129. });
  130. }
  131. }
  132. toks.append_all(quote! {
  133. __manual_props
  134. });
  135. quote! {{
  136. #toks
  137. }}
  138. }
  139. None => {
  140. let mut toks = quote! { fc_to_builder(#name) };
  141. for field in &self.body {
  142. if field.name.to_string() == "key" {
  143. has_key = Some(field);
  144. } else {
  145. toks.append_all(quote! {#field})
  146. }
  147. }
  148. toks.append_all(quote! {
  149. .build()
  150. });
  151. toks
  152. }
  153. };
  154. let key_token = match has_key {
  155. Some(field) => {
  156. let inners = field.content.to_token_stream();
  157. quote! {
  158. Some(#inners)
  159. }
  160. }
  161. None => quote! {None},
  162. };
  163. let childs = &self.children;
  164. let children = quote! {
  165. [ #( #childs ),* ]
  166. };
  167. tokens.append_all(quote! {
  168. __cx.component(
  169. #name,
  170. #builder,
  171. #key_token,
  172. __cx.bump().alloc(#children)
  173. )
  174. })
  175. }
  176. }
  177. // the struct's fields info
  178. pub struct ComponentField {
  179. name: Ident,
  180. content: ContentField,
  181. }
  182. enum ContentField {
  183. ManExpr(Expr),
  184. OnHandler(ExprClosure),
  185. // A handler was provided in {} tokens
  186. OnHandlerRaw(Expr),
  187. }
  188. impl ToTokens for ContentField {
  189. fn to_tokens(&self, tokens: &mut TokenStream2) {
  190. match self {
  191. ContentField::ManExpr(e) => e.to_tokens(tokens),
  192. ContentField::OnHandler(e) => tokens.append_all(quote! {
  193. __cx.bump().alloc(#e)
  194. }),
  195. ContentField::OnHandlerRaw(e) => tokens.append_all(quote! {
  196. __cx.bump().alloc(#e)
  197. }),
  198. }
  199. }
  200. }
  201. impl Parse for ComponentField {
  202. fn parse(input: ParseStream) -> Result<Self> {
  203. let name = Ident::parse_any(input)?;
  204. input.parse::<Token![:]>()?;
  205. let name_str = name.to_string();
  206. let content = if name_str.starts_with("on") {
  207. if input.peek(token::Brace) {
  208. let content;
  209. syn::braced!(content in input);
  210. ContentField::OnHandlerRaw(content.parse()?)
  211. } else {
  212. ContentField::OnHandler(input.parse()?)
  213. }
  214. } else {
  215. ContentField::ManExpr(input.parse::<Expr>()?)
  216. };
  217. Ok(Self { name, content })
  218. }
  219. }
  220. impl ToTokens for ComponentField {
  221. fn to_tokens(&self, tokens: &mut TokenStream2) {
  222. let ComponentField { name, content, .. } = self;
  223. tokens.append_all(quote! {
  224. .#name(#content)
  225. })
  226. }
  227. }