ambiguous.rs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. //! Parse anything that has a pattern of < Ident, Bracket >
  2. //! ========================================================
  3. //!
  4. //! Whenever a `name {}` pattern emerges, we need to parse it into an element, a component, or a fragment.
  5. //! This feature must support:
  6. //! - Namepsaced/pathed components
  7. //! - Differentiating between built-in and custom elements
  8. use super::*;
  9. use proc_macro2::TokenStream as TokenStream2;
  10. use quote::ToTokens;
  11. use syn::{
  12. parse::{Parse, ParseStream},
  13. Error, Ident, LitStr, Result, Token,
  14. };
  15. pub enum AmbiguousElement<const AS: HTML_OR_RSX> {
  16. Element(Element<AS>),
  17. Component(Component<AS>),
  18. }
  19. impl Parse for AmbiguousElement<AS_RSX> {
  20. fn parse(input: ParseStream) -> Result<Self> {
  21. // Try to parse as an absolute path and immediately defer to the componetn
  22. if input.peek(Token![::]) {
  23. return input
  24. .parse::<Component<AS_RSX>>()
  25. .map(|c| AmbiguousElement::Component(c));
  26. }
  27. // If not an absolute path, then parse the ident and check if it's a valid tag
  28. if let Ok(pat) = input.fork().parse::<syn::Path>() {
  29. if pat.segments.len() > 1 {
  30. return input
  31. .parse::<Component<AS_RSX>>()
  32. .map(|c| AmbiguousElement::Component(c));
  33. }
  34. }
  35. if let Ok(name) = input.fork().parse::<Ident>() {
  36. let name_str = name.to_string();
  37. let first_char = name_str.chars().next().unwrap();
  38. if first_char.is_ascii_uppercase() {
  39. input
  40. .parse::<Component<AS_RSX>>()
  41. .map(|c| AmbiguousElement::Component(c))
  42. } else {
  43. input
  44. .parse::<Element<AS_RSX>>()
  45. .map(|c| AmbiguousElement::Element(c))
  46. }
  47. } else {
  48. if input.peek(LitStr) {
  49. panic!("it's actually a litstr");
  50. }
  51. Err(Error::new(input.span(), "Not a valid Html tag"))
  52. }
  53. }
  54. }
  55. impl Parse for AmbiguousElement<AS_HTML> {
  56. fn parse(input: ParseStream) -> Result<Self> {
  57. // Try to parse as an absolute path and immediately defer to the componetn
  58. if input.peek(Token![::]) {
  59. return input
  60. .parse::<Component<AS_HTML>>()
  61. .map(|c| AmbiguousElement::Component(c));
  62. }
  63. // If not an absolute path, then parse the ident and check if it's a valid tag
  64. if let Ok(pat) = input.fork().parse::<syn::Path>() {
  65. if pat.segments.len() > 1 {
  66. return input
  67. .parse::<Component<AS_HTML>>()
  68. .map(|c| AmbiguousElement::Component(c));
  69. }
  70. }
  71. if let Ok(name) = input.fork().parse::<Ident>() {
  72. let name_str = name.to_string();
  73. let first_char = name_str.chars().next().unwrap();
  74. if first_char.is_ascii_uppercase() {
  75. input
  76. .parse::<Component<AS_HTML>>()
  77. .map(|c| AmbiguousElement::Component(c))
  78. } else {
  79. input
  80. .parse::<Element<AS_HTML>>()
  81. .map(|c| AmbiguousElement::Element(c))
  82. }
  83. } else {
  84. if input.peek(LitStr) {
  85. panic!("it's actually a litstr");
  86. }
  87. Err(Error::new(input.span(), "Not a valid Html tag"))
  88. }
  89. }
  90. }
  91. impl<const AS: HTML_OR_RSX> ToTokens for AmbiguousElement<AS> {
  92. fn to_tokens(&self, tokens: &mut TokenStream2) {
  93. match self {
  94. AmbiguousElement::Element(el) => el.to_tokens(tokens),
  95. AmbiguousElement::Component(comp) => comp.to_tokens(tokens),
  96. }
  97. }
  98. }