mod.rs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  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 ambiguous;
  14. mod component;
  15. mod element;
  16. mod fragment;
  17. mod node;
  18. // Re-export the namespaces into each other
  19. pub use ambiguous::*;
  20. pub use component::*;
  21. pub use element::*;
  22. pub use fragment::*;
  23. pub use node::*;
  24. use crate::util::is_valid_tag;
  25. use proc_macro2::TokenStream as TokenStream2;
  26. use quote::{quote, ToTokens, TokenStreamExt};
  27. use syn::{
  28. parse::{Parse, ParseStream},
  29. Error, Ident, LitStr, Result, Token,
  30. };
  31. pub struct RsxRender {
  32. custom_context: Option<Ident>,
  33. roots: Vec<Node>,
  34. }
  35. impl Parse for RsxRender {
  36. fn parse(input: ParseStream) -> Result<Self> {
  37. // if input.peek(LitStr) {
  38. // return input.parse::<LitStr>()?.parse::<RsxRender>();
  39. // }
  40. // try to parse the first ident and comma
  41. let custom_context =
  42. if input.peek(Token![in]) && input.peek2(Ident) && input.peek3(Token![,]) {
  43. let _ = input.parse::<Token![in]>()?;
  44. let name = input.parse::<Ident>()?;
  45. if is_valid_tag(&name.to_string()) {
  46. return Err(Error::new(
  47. input.span(),
  48. "Custom context cannot be an html element name",
  49. ));
  50. } else {
  51. input.parse::<Token![,]>().unwrap();
  52. Some(name)
  53. }
  54. } else {
  55. None
  56. };
  57. let mut body = Vec::new();
  58. let mut children = Vec::new();
  59. let mut manual_props = None;
  60. parse_component_body(
  61. input,
  62. &BodyParseConfig {
  63. allow_children: true,
  64. allow_fields: false,
  65. allow_manual_props: false,
  66. },
  67. &mut body,
  68. &mut children,
  69. &mut manual_props,
  70. )?;
  71. Ok(Self {
  72. roots: children,
  73. custom_context,
  74. })
  75. }
  76. }
  77. impl ToTokens for RsxRender {
  78. fn to_tokens(&self, out_tokens: &mut TokenStream2) {
  79. let inner = if self.roots.len() == 1 {
  80. let inner = &self.roots[0];
  81. quote! {#inner}
  82. } else {
  83. let childs = &self.roots;
  84. quote! { __cx.fragment_from_iter([ #(#childs),* ]) }
  85. };
  86. match &self.custom_context {
  87. // The `in cx` pattern allows directly rendering
  88. Some(ident) => out_tokens.append_all(quote! {
  89. #ident.render(dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{
  90. use dioxus_elements::GlobalAttributes;
  91. #inner
  92. }))
  93. }),
  94. // Otherwise we just build the LazyNode wrapper
  95. None => out_tokens.append_all(quote! {
  96. dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{
  97. use dioxus_elements::GlobalAttributes;
  98. #inner
  99. })
  100. }),
  101. };
  102. }
  103. }