node.rs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. use super::*;
  2. use proc_macro2::{Span, TokenStream as TokenStream2};
  3. use quote::{quote, ToTokens, TokenStreamExt};
  4. use syn::{
  5. parse::{Parse, ParseStream},
  6. spanned::Spanned,
  7. token, Expr, LitStr, Result,
  8. };
  9. /*
  10. Parse
  11. -> div {}
  12. -> Component {}
  13. -> component()
  14. -> "text {with_args}"
  15. -> (0..10).map(|f| rsx!("asd")), // <--- notice the comma - must be a complete expr
  16. */
  17. #[derive(PartialEq, Eq)]
  18. pub enum BodyNode {
  19. Element(Element),
  20. Component(Component),
  21. Text(LitStr),
  22. RawExpr(Expr),
  23. }
  24. impl BodyNode {
  25. pub fn is_litstr(&self) -> bool {
  26. matches!(self, BodyNode::Text(_))
  27. }
  28. pub fn span(&self) -> Span {
  29. match self {
  30. BodyNode::Element(el) => el.name.span(),
  31. BodyNode::Component(component) => component.name.span(),
  32. BodyNode::Text(text) => text.span(),
  33. BodyNode::RawExpr(exp) => exp.span(),
  34. }
  35. }
  36. }
  37. impl Parse for BodyNode {
  38. fn parse(stream: ParseStream) -> Result<Self> {
  39. if stream.peek(LitStr) {
  40. return Ok(BodyNode::Text(stream.parse()?));
  41. }
  42. let body_stream = stream.fork();
  43. if let Ok(path) = body_stream.parse::<syn::Path>() {
  44. // this is an Element if path match of:
  45. // - one ident
  46. // - followed by `{`
  47. // - 1st char is lowercase
  48. //
  49. // example:
  50. // div {}
  51. if let Some(ident) = path.get_ident() {
  52. if body_stream.peek(token::Brace)
  53. && ident
  54. .to_string()
  55. .chars()
  56. .next()
  57. .unwrap()
  58. .is_ascii_lowercase()
  59. {
  60. return Ok(BodyNode::Element(stream.parse::<Element>()?));
  61. }
  62. }
  63. // Otherwise this should be Component, allowed syntax:
  64. // - syn::Path
  65. // - PathArguments can only apper in last segment
  66. // - followed by `{` or `(`, note `(` cannot be used with one ident
  67. //
  68. // example
  69. // Div {}
  70. // ::Div {}
  71. // crate::Div {}
  72. // component {} <-- already handled by elements
  73. // ::component {}
  74. // crate::component{}
  75. // Input::<InputProps<'_, i32> {}
  76. // crate::Input::<InputProps<'_, i32> {}
  77. if body_stream.peek(token::Brace) {
  78. Component::validate_component_path(&path)?;
  79. return Ok(BodyNode::Component(stream.parse()?));
  80. }
  81. }
  82. Ok(BodyNode::RawExpr(stream.parse::<Expr>()?))
  83. }
  84. }
  85. impl ToTokens for BodyNode {
  86. fn to_tokens(&self, tokens: &mut TokenStream2) {
  87. match &self {
  88. BodyNode::Element(el) => el.to_tokens(tokens),
  89. BodyNode::Component(comp) => comp.to_tokens(tokens),
  90. BodyNode::Text(txt) => tokens.append_all(quote! {
  91. __cx.text(format_args_f!(#txt))
  92. }),
  93. BodyNode::RawExpr(exp) => tokens.append_all(quote! {
  94. __cx.fragment_from_iter(#exp)
  95. }),
  96. }
  97. }
  98. }