inlineprops.rs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. use proc_macro2::{Span, TokenStream as TokenStream2};
  2. use quote::{quote, ToTokens, TokenStreamExt};
  3. use syn::{
  4. parse::{Parse, ParseStream},
  5. punctuated::Punctuated,
  6. *,
  7. };
  8. pub struct InlinePropsBody {
  9. pub attrs: Vec<Attribute>,
  10. pub vis: syn::Visibility,
  11. pub maybe_async: Option<Token![async]>,
  12. pub fn_token: Token![fn],
  13. pub ident: Ident,
  14. pub cx_token: Box<Pat>,
  15. pub generics: Generics,
  16. pub paren_token: token::Paren,
  17. pub inputs: Punctuated<FnArg, Token![,]>,
  18. // pub fields: FieldsNamed,
  19. pub output: ReturnType,
  20. pub where_clause: Option<WhereClause>,
  21. pub block: Box<Block>,
  22. }
  23. /// The custom rusty variant of parsing rsx!
  24. impl Parse for InlinePropsBody {
  25. fn parse(input: ParseStream) -> Result<Self> {
  26. let attrs: Vec<Attribute> = input.call(Attribute::parse_outer)?;
  27. let maybe_async: Option<Token![async]> = input.parse().ok();
  28. let vis: Visibility = input.parse()?;
  29. let fn_token = input.parse()?;
  30. let ident = input.parse()?;
  31. let generics: Generics = input.parse()?;
  32. let content;
  33. let paren_token = syn::parenthesized!(content in input);
  34. let first_arg: FnArg = content.parse()?;
  35. let cx_token = {
  36. match first_arg {
  37. FnArg::Receiver(_) => panic!("first argument must not be a receiver argument"),
  38. FnArg::Typed(f) => f.pat,
  39. }
  40. };
  41. let _: Result<Token![,]> = content.parse();
  42. let inputs = syn::punctuated::Punctuated::parse_terminated(&content)?;
  43. let output = input.parse()?;
  44. let where_clause = input
  45. .peek(syn::token::Where)
  46. .then(|| input.parse())
  47. .transpose()?;
  48. let block = input.parse()?;
  49. Ok(Self {
  50. vis,
  51. maybe_async,
  52. fn_token,
  53. ident,
  54. generics,
  55. paren_token,
  56. inputs,
  57. output,
  58. where_clause,
  59. block,
  60. cx_token,
  61. attrs,
  62. })
  63. }
  64. }
  65. /// Serialize the same way, regardless of flavor
  66. impl ToTokens for InlinePropsBody {
  67. fn to_tokens(&self, out_tokens: &mut TokenStream2) {
  68. let Self {
  69. vis,
  70. ident,
  71. generics,
  72. inputs,
  73. output,
  74. where_clause,
  75. block,
  76. cx_token,
  77. attrs,
  78. maybe_async,
  79. ..
  80. } = self;
  81. let fields = inputs.iter().map(|f| {
  82. quote! { #vis #f }
  83. });
  84. let struct_name = Ident::new(&format!("{ident}Props"), Span::call_site());
  85. let field_names = inputs.iter().filter_map(|f| match f {
  86. FnArg::Receiver(_) => todo!(),
  87. FnArg::Typed(t) => Some(&t.pat),
  88. });
  89. let first_lifetime = if let Some(GenericParam::Lifetime(lt)) = generics.params.first() {
  90. Some(lt)
  91. } else {
  92. None
  93. };
  94. let modifiers = if first_lifetime.is_some() {
  95. quote! { #[derive(Props)] }
  96. } else {
  97. quote! { #[derive(Props, PartialEq)] }
  98. };
  99. let (scope_lifetime, fn_generics, struct_generics) = if let Some(lt) = first_lifetime {
  100. let struct_generics: Punctuated<_, token::Comma> = generics
  101. .params
  102. .iter()
  103. .map(|it| match it {
  104. GenericParam::Type(tp) => {
  105. let mut tp = tp.clone();
  106. tp.bounds.push(parse_quote!( 'a ));
  107. GenericParam::Type(tp)
  108. }
  109. _ => it.clone(),
  110. })
  111. .collect();
  112. (
  113. quote! { #lt, },
  114. generics.clone(),
  115. quote! { <#struct_generics> },
  116. )
  117. } else {
  118. let lifetime: LifetimeDef = parse_quote! { 'a };
  119. let mut fn_generics = generics.clone();
  120. fn_generics
  121. .params
  122. .insert(0, GenericParam::Lifetime(lifetime.clone()));
  123. (quote! { #lifetime, }, fn_generics, quote! { #generics })
  124. };
  125. out_tokens.append_all(quote! {
  126. #modifiers
  127. #[allow(non_camel_case_types)]
  128. #vis struct #struct_name #struct_generics
  129. #where_clause
  130. {
  131. #(#fields),*
  132. }
  133. #(#attrs)*
  134. #maybe_async #vis fn #ident #fn_generics (#cx_token: Scope<#scope_lifetime #struct_name #generics>) #output
  135. #where_clause
  136. {
  137. let #struct_name { #(#field_names),* } = &#cx_token.props;
  138. #block
  139. }
  140. });
  141. }
  142. }