lib.rs 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  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. mod component;
  14. mod element;
  15. mod node;
  16. pub mod pretty;
  17. // Re-export the namespaces into each other
  18. pub use component::*;
  19. pub use element::*;
  20. pub use node::*;
  21. // imports
  22. use proc_macro2::TokenStream as TokenStream2;
  23. use quote::{quote, ToTokens, TokenStreamExt};
  24. use syn::{
  25. parse::{Parse, ParseStream},
  26. Ident, Result, Token,
  27. };
  28. pub struct CallBody {
  29. pub custom_context: Option<Ident>,
  30. pub roots: Vec<BodyNode>,
  31. }
  32. impl Parse for CallBody {
  33. fn parse(input: ParseStream) -> Result<Self> {
  34. let custom_context = if input.peek(Ident) && input.peek2(Token![,]) {
  35. let name = input.parse::<Ident>()?;
  36. input.parse::<Token![,]>()?;
  37. Some(name)
  38. } else {
  39. None
  40. };
  41. let mut roots = Vec::new();
  42. while !input.is_empty() {
  43. let node = input.parse::<BodyNode>()?;
  44. if input.peek(Token![,]) {
  45. let _ = input.parse::<Token![,]>();
  46. }
  47. roots.push(node);
  48. }
  49. Ok(Self {
  50. custom_context,
  51. roots,
  52. })
  53. }
  54. }
  55. /// Serialize the same way, regardless of flavor
  56. impl ToTokens for CallBody {
  57. fn to_tokens(&self, out_tokens: &mut TokenStream2) {
  58. let inner = if self.roots.len() == 1 {
  59. let inner = &self.roots[0];
  60. quote! { #inner }
  61. } else {
  62. let childs = &self.roots;
  63. quote! { __cx.fragment_root([ #(#childs),* ]) }
  64. };
  65. match &self.custom_context {
  66. // The `in cx` pattern allows directly rendering
  67. Some(ident) => out_tokens.append_all(quote! {
  68. #ident.render(LazyNodes::new(move |__cx: NodeFactory| -> VNode {
  69. use dioxus_elements::{GlobalAttributes, SvgAttributes};
  70. #inner
  71. }))
  72. }),
  73. // Otherwise we just build the LazyNode wrapper
  74. None => out_tokens.append_all(quote! {
  75. LazyNodes::new(move |__cx: NodeFactory| -> VNode {
  76. use dioxus_elements::{GlobalAttributes, SvgAttributes};
  77. #inner
  78. })
  79. }),
  80. };
  81. }
  82. }