ambiguous.rs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  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, Result, Token,
  14. };
  15. #[allow(clippy::large_enum_variant)]
  16. pub enum AmbiguousElement<const AS: HtmlOrRsx> {
  17. Element(Element<AS>),
  18. Component(Component<AS>),
  19. }
  20. impl Parse for AmbiguousElement<AS_RSX> {
  21. fn parse(input: ParseStream) -> Result<Self> {
  22. // Try to parse as an absolute path and immediately defer to the componetn
  23. if input.peek(Token![::]) {
  24. return input
  25. .parse::<Component<AS_RSX>>()
  26. .map(AmbiguousElement::Component);
  27. }
  28. // If not an absolute path, then parse the ident and check if it's a valid tag
  29. if let Ok(pat) = input.fork().parse::<syn::Path>() {
  30. if pat.segments.len() > 1 {
  31. return input
  32. .parse::<Component<AS_RSX>>()
  33. .map(AmbiguousElement::Component);
  34. }
  35. }
  36. use syn::ext::IdentExt;
  37. if let Ok(name) = input.fork().call(Ident::parse_any) {
  38. let name_str = name.to_string();
  39. let first_char = name_str.chars().next().unwrap();
  40. if first_char.is_ascii_uppercase() {
  41. input
  42. .parse::<Component<AS_RSX>>()
  43. .map(AmbiguousElement::Component)
  44. } else {
  45. input
  46. .parse::<Element<AS_RSX>>()
  47. .map(AmbiguousElement::Element)
  48. }
  49. } else {
  50. Err(Error::new(input.span(), "Not a valid Html tag"))
  51. }
  52. }
  53. }
  54. impl Parse for AmbiguousElement<AS_HTML> {
  55. fn parse(input: ParseStream) -> Result<Self> {
  56. if input.peek(Token![<]) {
  57. let forked = input.fork();
  58. forked.parse::<Token![<]>().unwrap();
  59. let tag = forked.parse::<Ident>()?;
  60. let name_str = tag.to_string();
  61. let first_char = name_str.chars().next().unwrap();
  62. if first_char.is_ascii_uppercase() {
  63. input
  64. .parse::<Component<AS_HTML>>()
  65. .map(AmbiguousElement::Component)
  66. } else {
  67. input
  68. .parse::<Element<AS_HTML>>()
  69. .map(AmbiguousElement::Element)
  70. }
  71. } else {
  72. Err(Error::new(input.span(), "Not a valid Html tag"))
  73. }
  74. }
  75. }
  76. impl<const AS: HtmlOrRsx> ToTokens for AmbiguousElement<AS> {
  77. fn to_tokens(&self, tokens: &mut TokenStream2) {
  78. match self {
  79. AmbiguousElement::Element(el) => el.to_tokens(tokens),
  80. AmbiguousElement::Component(comp) => comp.to_tokens(tokens),
  81. }
  82. }
  83. }