mod.rs 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. //! Parse the root tokens in the rsx!{} macro
  2. //! =========================================
  3. //!
  4. //! This parsing path emerges directly from the macro call, with `RsxRender` being the primary entrance into parsing.
  5. //! This feature must support:
  6. //! - [x] Optionally rendering if the `in XYZ` pattern is present
  7. //! - [x] Fragments as top-level element (through ambiguous)
  8. //! - [x] Components as top-level element (through ambiguous)
  9. //! - [x] Tags as top-level elements (through ambiguous)
  10. //! - [x] Good errors if parsing fails
  11. //!
  12. //! Any errors in using rsx! will likely occur when people start using it, so the first errors must be really helpful.
  13. #[macro_use]
  14. mod errors;
  15. mod component;
  16. mod element;
  17. mod node;
  18. pub mod pretty;
  19. // Re-export the namespaces into each other
  20. pub use component::*;
  21. pub use element::*;
  22. pub use node::*;
  23. // imports
  24. use proc_macro2::TokenStream as TokenStream2;
  25. use quote::{quote, ToTokens, TokenStreamExt};
  26. use syn::{
  27. parse::{Parse, ParseStream},
  28. Ident, Result, Token,
  29. };
  30. pub struct CallBody {
  31. pub custom_context: Option<Ident>,
  32. pub roots: Vec<BodyNode>,
  33. }
  34. impl Parse for CallBody {
  35. fn parse(input: ParseStream) -> Result<Self> {
  36. let custom_context = if input.peek(Ident) && input.peek2(Token![,]) {
  37. let name = input.parse::<Ident>()?;
  38. input.parse::<Token![,]>()?;
  39. Some(name)
  40. } else {
  41. None
  42. };
  43. let mut roots = Vec::new();
  44. while !input.is_empty() {
  45. let node = input.parse::<BodyNode>()?;
  46. if input.peek(Token![,]) {
  47. let _ = input.parse::<Token![,]>();
  48. }
  49. roots.push(node);
  50. }
  51. Ok(Self {
  52. custom_context,
  53. roots,
  54. })
  55. }
  56. }
  57. /// Serialize the same way, regardless of flavor
  58. impl ToTokens for CallBody {
  59. fn to_tokens(&self, out_tokens: &mut TokenStream2) {
  60. let inner = if self.roots.len() == 1 {
  61. let inner = &self.roots[0];
  62. quote! { #inner }
  63. } else {
  64. let childs = &self.roots;
  65. quote! { __cx.fragment_root([ #(#childs),* ]) }
  66. };
  67. match &self.custom_context {
  68. // The `in cx` pattern allows directly rendering
  69. Some(ident) => out_tokens.append_all(quote! {
  70. #ident.render(LazyNodes::new(move |__cx: NodeFactory| -> VNode {
  71. use dioxus_elements::{GlobalAttributes, SvgAttributes};
  72. #inner
  73. }))
  74. }),
  75. // Otherwise we just build the LazyNode wrapper
  76. None => out_tokens.append_all(quote! {
  77. LazyNodes::new(move |__cx: NodeFactory| -> VNode {
  78. use dioxus_elements::{GlobalAttributes, SvgAttributes};
  79. #inner
  80. })
  81. }),
  82. };
  83. }
  84. }