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#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct DashIdent(pub Punctuated<Ident, Token![-]>);
14
15#[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#[derive(Debug, Clone, PartialEq, Eq)]
27pub enum Value {
28 LitStr(LitStr),
30 Expr(Expr),
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct Attr {
37 pub key: DashIdent,
38 pub eq_sign: Token![=],
39 pub value: Value,
40}
41
42#[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}