inlineprops.rs 4.0 KB

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