inlineprops.rs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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. token, Block, FnArg, Generics, Ident, Pat, Result, ReturnType, Token, Visibility,
  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 reciver 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 modifiers = if generics.params.is_empty() {
  75. quote! { #[derive(Props, PartialEq)] }
  76. } else {
  77. quote! { #[derive(Props)] }
  78. };
  79. let lifetime = if generics.params.is_empty() {
  80. quote! {}
  81. } else {
  82. quote! { 'a, }
  83. };
  84. out_tokens.append_all(quote! {
  85. #modifiers
  86. #[allow(non_camel_case_types)]
  87. #vis struct #struct_name #generics {
  88. #(#fields),*
  89. }
  90. #vis fn #ident #generics (#cx_token: Scope<#lifetime #struct_name #generics>) #output {
  91. let #struct_name { #(#field_names),* } = &cx.props;
  92. #block
  93. }
  94. });
  95. }
  96. }