This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew_macro/html_tree/
html_dashed_name.rs

1use std::fmt;
2
3use proc_macro2::{Ident, Span, TokenStream};
4use quote::{quote, ToTokens};
5use syn::buffer::Cursor;
6use syn::ext::IdentExt;
7use syn::parse::{Parse, ParseStream};
8use syn::spanned::Spanned;
9use syn::{LitStr, Token};
10
11use crate::stringify::Stringify;
12use crate::{non_capitalized_ascii, Peek};
13
14#[derive(Clone, PartialEq, Eq)]
15pub struct HtmlDashedName {
16    pub name: Ident,
17    pub extended: Vec<(Token![-], Ident)>,
18}
19
20impl HtmlDashedName {
21    /// Checks if this name is equal to the provided item (which can be anything implementing
22    /// `Into<String>`).
23    pub fn eq_ignore_ascii_case<S>(&self, other: S) -> bool
24    where
25        S: Into<String>,
26    {
27        let mut s = other.into();
28        s.make_ascii_lowercase();
29        s == self.to_ascii_lowercase_string()
30    }
31
32    pub fn to_ascii_lowercase_string(&self) -> String {
33        let mut s = self.to_string();
34        s.make_ascii_lowercase();
35        s
36    }
37
38    pub fn to_lit_str(&self) -> LitStr {
39        LitStr::new(&self.to_string(), self.span())
40    }
41}
42
43impl fmt::Display for HtmlDashedName {
44    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45        write!(f, "{}", self.name)?;
46        for (_, ident) in &self.extended {
47            write!(f, "-{ident}")?;
48        }
49        Ok(())
50    }
51}
52
53impl Peek<'_, Self> for HtmlDashedName {
54    fn peek(cursor: Cursor) -> Option<(Self, Cursor)> {
55        let (name, cursor) = cursor.ident()?;
56        if !non_capitalized_ascii(&name.to_string()) {
57            return None;
58        }
59
60        let mut extended = Vec::new();
61        let mut cursor = cursor;
62        loop {
63            if let Some((punct, p_cursor)) = cursor.punct() {
64                if punct.as_char() == '-' {
65                    let (ident, i_cursor) = p_cursor.ident()?;
66                    cursor = i_cursor;
67                    extended.push((Token![-](Span::mixed_site()), ident));
68                    continue;
69                }
70            }
71            break;
72        }
73
74        Some((HtmlDashedName { name, extended }, cursor))
75    }
76}
77
78impl Parse for HtmlDashedName {
79    fn parse(input: ParseStream) -> syn::Result<Self> {
80        let name = input.call(Ident::parse_any)?;
81        let mut extended = Vec::new();
82        while input.peek(Token![-]) {
83            extended.push((input.parse::<Token![-]>()?, input.call(Ident::parse_any)?));
84        }
85
86        Ok(HtmlDashedName { name, extended })
87    }
88}
89
90impl ToTokens for HtmlDashedName {
91    fn to_tokens(&self, tokens: &mut TokenStream) {
92        let HtmlDashedName { name, extended } = self;
93        let dashes = extended.iter().map(|(dash, _)| quote! {#dash});
94        let idents = extended.iter().map(|(_, ident)| quote! {#ident});
95        let extended = quote! { #(#dashes #idents)* };
96        tokens.extend(quote! { #name #extended });
97    }
98}
99
100impl Stringify for HtmlDashedName {
101    fn try_into_lit(&self) -> Option<LitStr> {
102        Some(self.to_lit_str())
103    }
104
105    fn stringify(&self) -> TokenStream {
106        self.to_lit_str().stringify()
107    }
108}
109
110impl From<Ident> for HtmlDashedName {
111    fn from(name: Ident) -> Self {
112        HtmlDashedName {
113            name,
114            extended: vec![],
115        }
116    }
117}