mod.rs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. //! This module is used for parsing a component function into a struct that is subsequently
  2. //! deserialized into something useful using deserializer arguments.
  3. //!
  4. //! Let's break that down with a term glossary and examples which show usage and implementing.
  5. //!
  6. //! # Glossary
  7. //! * `component body` - The [`ComponentBody`] struct. It's used to parse a component function [`proc_macro::TokenStream`]
  8. //! to a reusable struct that deserializers use to modify the token stream.
  9. //! * `deserializer` - A struct that deserializes the [`ComponentBody`] into a [`DeserializerOutput`].
  10. //! It implements the [`DeserializerArgs`] trait, but as you can see, it's called "DeserializerArgs",
  11. //! not "Deserializer". Why?
  12. //! Because "args" makes more sense to the caller of [`ComponentBody::deserialize`], which
  13. //! takes an [`DeserializerArgs`] argument. However, you can think of "DeserializerArgs" as the deserializer.
  14. //! * `deserializer output` - A struct that implements the [`DeserializerOutput`] trait.
  15. //! This struct is what enables deserializers to use each other, since it contains the fields that
  16. //! a deserializer needs to turn a token stream to a different token stream.
  17. //! This means a deserializer can get the output of another deserializer, and use that output,
  18. //! thereby using the functionality of a different deserializer.
  19. //! This struct also implements [`ToTokens`], which means that this is the final stage of the whole process.
  20. //!
  21. //! # Examples
  22. //! *Not all imports might be included.*
  23. //!
  24. //! ## Usage in a procedural macro attribute
  25. //! ```rs,ignore
  26. //! use proc_macro::TokenStream;
  27. //!
  28. //! // Some documentation. You can reuse this in your deserializer structs.
  29. //! /// This attribute changes the name of a component function to whatever the first argument is.
  30. //! #[proc_macro_attribute]
  31. //! pub fn name_changer(args: TokenStream, input: TokenStream) -> TokenStream {
  32. //! // Parse the component body.
  33. //! let component_body = parse_macro_input!(input as ComponentBody);
  34. //!
  35. //! // Parse the first argument, which is going to be the components new name.
  36. //! let new_name: String = match Punctuated::<Path, Token![,]>::parse_terminated.parse(args) {
  37. //! Err(e) => return e.to_compile_error().into(), // Convert to a compile error and return
  38. //! Ok(args) => {
  39. //! // If the argument exists, then convert it to a string
  40. //! if let Some(first) = args.first() {
  41. //! first.to_token_stream().to_string()
  42. //! } else {
  43. //! // If the argument doesn't exist, return an error with the appropriate message.
  44. //! // The "span" is the location of some code.
  45. //! // The error occurred in the "args" token stream, so point the error there.
  46. //! return Error::new(args.span(), "No new name provided").to_compile_error().into();
  47. //! }
  48. //! }
  49. //! };
  50. //!
  51. //! let new_name = &*new_name;
  52. //!
  53. //! // Deserialize the component body to an output with the given args.
  54. //! let output = component_body.deserialize(NameChangerDeserializerArgs { new_name });
  55. //!
  56. //! // Error handling like before, except now you're ready to return the final value.
  57. //! match output {
  58. //! Err(e) => e.to_compile_error().into(),
  59. //! Ok(output) => output.to_token_stream().into(),
  60. //! }
  61. //! }
  62. //! ```
  63. //! ## Using the macro in Dioxus code:
  64. //! ```rs
  65. //! use your_proc_macro_library::name_changer;
  66. //! use dioxus::prelude::*;
  67. //!
  68. //! #[name_changer(CoolName)]
  69. //! pub fn LameName() -> Element {
  70. //! rsx! { "I want a cool name!" }
  71. //! }
  72. //!
  73. //! pub fn App() -> Element {
  74. //! rsx! { CoolName {} } // Renders: "I want a cool name!"
  75. //! }
  76. //! ```
  77. //! ## Implementing a component body deserializer
  78. //! ```rs
  79. //! use syn::{Result, ItemFn, Signature, Ident};
  80. //! use quote::quote;
  81. //!
  82. //! // Create a list of arguments.
  83. //! // If there was no args, just make it empty. The "args" struct is also the deserializer struct.
  84. //! // For the docs, you can basically copy paste this text and replace "name_changer" with your macro path.
  85. //! // Although unfortunately, the link does not work
  86. //! // Just make sure that your macro is well documented.
  87. //! /// The args and deserializing implementation for the [`name_changer`] macro.
  88. //! #[derive(Clone)]
  89. //! pub struct NameChangerDeserializerArgs<'a> {
  90. //! pub new_name: &'a str,
  91. //! }
  92. //!
  93. //! // Create an output struct.
  94. //! // The ItemFn represents a modified component function.
  95. //! // To read what fields should be here, check out the `DeserializerOutput` struct docs.
  96. //! // For the docs, you can basically copy paste this text and replace "name_changer" with your macro path.
  97. //! // Just make sure that your macro is well documented.
  98. //! /// The output fields and [`ToTokens`] implementation for the [`name_changer`] macro.
  99. //! #[derive(Clone)]
  100. //! pub struct NameChangerDeserializerOutput {
  101. //! pub comp_fn: ItemFn,
  102. //! }
  103. //!
  104. //! // Implement `ToTokens`, which is forced by `DeserializerOutput`.
  105. //! // This will usually be very simple like this, even for complex deserializers.
  106. //! // That's because of the way the `DeserializerOutput` is designed.
  107. //! impl ToTokens for NameChangerDeserializerOutput {
  108. //! fn to_tokens(&self, tokens: &mut TokenStream) {
  109. //! let comp_fn = &self.comp_fn;
  110. //!
  111. //! tokens.append_all(quote! {
  112. //! #comp_fn
  113. //! });
  114. //! }
  115. //! }
  116. //!
  117. //! impl DeserializerOutput for NameChangerDeserializerOutput {}
  118. //!
  119. //! // Implement `DeserializerArgs`. This is the core part of deserializers.
  120. //! impl<'a> DeserializerArgs<NameChangerDeserializerOutput> for NameChangerDeserializerArgs<'a> {
  121. //! fn to_output(&self, component_body: &ComponentBody) -> Result<NameChangerDeserializerOutput> {
  122. //! let old_fn = &component_body.item_fn;
  123. //! let old_sig = &old_fn.sig;
  124. //!
  125. //! // For more complex uses, you will probably use `quote::parse_quote!` in combination with
  126. //! // creating the structs manually.
  127. //! // However, create the structs manually if you can.
  128. //! // It's more reliable, because you only modify a certain struct field
  129. //! // and set the others to be the clone of the original component body.
  130. //! // That ensures that no information will be accidentally removed.
  131. //! let new_sig = Signature {
  132. //! ident: Ident::new(self.new_name, old_sig.ident.span()),
  133. //! ..old_sig.clone()
  134. //! };
  135. //! let new_fn = ItemFn {
  136. //! sig: new_sig,
  137. //! ..old_fn.clone()
  138. //! };
  139. //!
  140. //! Ok(NameChangerDeserializerOutput {
  141. //! comp_fn: new_fn
  142. //! })
  143. //! }
  144. //! ```
  145. pub mod utils;
  146. pub use utils::DeserializerArgs;
  147. pub use utils::DeserializerOutput;
  148. use syn::parse::{Parse, ParseStream};
  149. use syn::spanned::Spanned;
  150. use syn::*;
  151. /// General struct for parsing a component body.
  152. /// However, because it's ambiguous, it does not implement [`ToTokens`](quote::to_tokens::ToTokens).
  153. ///
  154. /// Refer to the [module documentation](crate::component_body) for more.
  155. pub struct ComponentBody {
  156. /// The component function definition. You can parse this back into a [`ComponentBody`].
  157. /// For example, you might modify it, parse it into a [`ComponentBody`], and deserialize that
  158. /// using some deserializer. This is how deserializers use other deserializers.
  159. ///
  160. /// **`item_fn.sig.inputs` includes the context argument!**
  161. /// Keep this in mind when creating deserializers, because you often might want to ignore it.
  162. /// That might be annoying, but it would be bad design for this kind of struct to not be parsable from itself.
  163. pub item_fn: ItemFn,
  164. /// If the function has any arguments other than the context.
  165. pub has_extra_args: bool,
  166. }
  167. impl ComponentBody {
  168. /// Deserializes the body into the [`TOutput`] with the specific [`TArgs`].
  169. /// Even if the args are empty, the [`TArg`] type still determines what [`TOutput`] will be generated.
  170. pub fn deserialize<TOutput, TArgs>(&self, args: TArgs) -> Result<TOutput>
  171. where
  172. TOutput: DeserializerOutput,
  173. TArgs: DeserializerArgs<TOutput>,
  174. {
  175. args.to_output(self)
  176. }
  177. }
  178. impl Parse for ComponentBody {
  179. fn parse(input: ParseStream) -> Result<Self> {
  180. let item_fn: ItemFn = input.parse()?;
  181. let element_type_path = "dioxus_core::Element";
  182. if item_fn.sig.output == ReturnType::Default {
  183. return Err(Error::new(
  184. item_fn.sig.output.span(),
  185. format!("Must return a <{}>", element_type_path),
  186. ));
  187. }
  188. let has_extra_args = !item_fn.sig.inputs.is_empty();
  189. Ok(Self {
  190. item_fn,
  191. has_extra_args,
  192. })
  193. }
  194. }