tiny_rsx/
ast.rs

1use quote::ToTokens;
2use syn::{punctuated::Punctuated, Expr, Ident, LitStr, Token};
3
4pub(crate) mod kw {
5    use syn::custom_keyword;
6
7    custom_keyword!(DOCTYPE);
8    custom_keyword!(html);
9}
10
11/// An identifier seperated by dashes: `foo-bar-baz`.
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct DashIdent(pub Punctuated<Ident, Token![-]>);
14
15/// An HTML doctype declaration: <!DOCTYPE html>.
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct Doctype {
18    pub lt_sign: Token![<],
19    pub excl_mark: Token![!],
20    pub doctype: kw::DOCTYPE,
21    pub html: kw::html,
22    pub gt_sign: Token![>],
23}
24
25/// A value, either a string literal or a braced expression.
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub enum Value {
28    /// A string literal: `"foo bar"`.
29    LitStr(LitStr),
30    /// A braced expression: `{1 + 2}`.
31    Expr(Expr),
32}
33
34/// An HTML attribute consisting of a key and a value: `foo="bar"`.
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct Attr {
37    pub key: DashIdent,
38    pub eq_sign: Token![=],
39    pub value: Value,
40}
41
42/// An HTML opening or closing tag: `<foo>`, `</foo>`.
43#[derive(Debug, Clone, PartialEq, Eq)]
44pub enum Tag {
45    Opening {
46        lt_sign: Token![<],
47        name: DashIdent,
48        attrs: Vec<Attr>,
49        void_slash: Option<Token![/]>,
50        gt_sign: Token![>],
51    },
52    Closing {
53        lt_sign: Token![<],
54        name: DashIdent,
55        gt_sign: Token![>],
56    },
57}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
60pub enum Node {
61    Doctype(Doctype),
62    Tag(Tag),
63    Value(Value),
64}
65
66#[derive(Debug, Clone, PartialEq, Eq)]
67pub struct NodeTree {
68    pub nodes: Box<[Node]>,
69}
70
71impl Tag {
72    pub fn is_opening_tag(&self) -> bool {
73        matches!(self, Self::Opening { .. })
74    }
75
76    pub fn is_closing_tag(&self) -> bool {
77        matches!(self, Self::Closing { .. })
78    }
79
80    pub fn is_self_closing(&self) -> bool {
81        matches!(
82            self,
83            Self::Opening {
84                void_slash: Some(_),
85                ..
86            }
87        )
88    }
89}
90
91impl ToTokens for DashIdent {
92    #[inline]
93    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
94        self.0.to_tokens(tokens)
95    }
96}
97
98impl ToTokens for Doctype {
99    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
100        self.lt_sign.to_tokens(tokens);
101        self.excl_mark.to_tokens(tokens);
102        self.doctype.to_tokens(tokens);
103        self.html.to_tokens(tokens);
104        self.gt_sign.to_tokens(tokens);
105    }
106}
107
108impl ToTokens for Value {
109    #[inline]
110    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
111        match self {
112            Value::LitStr(lit_str) => lit_str.to_tokens(tokens),
113            Value::Expr(expr) => expr.to_tokens(tokens),
114        }
115    }
116}
117
118impl ToTokens for Attr {
119    #[inline]
120    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
121        self.key.to_tokens(tokens);
122        self.value.to_tokens(tokens);
123    }
124}
125
126impl ToTokens for Tag {
127    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
128        match self {
129            Tag::Opening {
130                lt_sign,
131                name,
132                attrs,
133                void_slash,
134                gt_sign,
135            } => {
136                lt_sign.to_tokens(tokens);
137                name.to_tokens(tokens);
138                for attr in attrs {
139                    attr.to_tokens(tokens);
140                }
141                void_slash.to_tokens(tokens);
142                gt_sign.to_tokens(tokens);
143            }
144            Tag::Closing {
145                lt_sign,
146                name,
147                gt_sign,
148            } => {
149                lt_sign.to_tokens(tokens);
150                name.to_tokens(tokens);
151                gt_sign.to_tokens(tokens);
152            }
153        }
154    }
155}
156
157impl ToTokens for Node {
158    #[inline]
159    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
160        match self {
161            Node::Doctype(doctype) => doctype.to_tokens(tokens),
162            Node::Tag(tag) => tag.to_tokens(tokens),
163            Node::Value(value) => value.to_tokens(tokens),
164        }
165    }
166}
167
168impl ToTokens for NodeTree {
169    #[inline]
170    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
171        for node in &self.nodes {
172            node.to_tokens(tokens);
173        }
174    }
175}