rocal_ui/
html.rs

1use proc_macro2::{Delimiter, TokenStream, TokenTree};
2use syn::{
3    braced,
4    buffer::Cursor,
5    parse::{Parse, ParseBuffer, ParseStream},
6    token::Brace,
7    Expr, Ident, LitStr, Result, Token,
8};
9
10use crate::{data_types::stack::Stack, enums::html_element::HtmlElement};
11
12pub mod to_tokens;
13
14pub fn parse(item: TokenStream) -> Result<Html> {
15    Ok(syn::parse2(item.into())?)
16}
17
18#[derive(Clone, Debug)]
19pub struct Html {
20    children: Vec<Html>,
21    value: Lex,
22}
23
24impl Html {
25    pub fn children(&self) -> &Vec<Html> {
26        &self.children
27    }
28
29    pub fn value(&self) -> &Lex {
30        &self.value
31    }
32}
33
34impl Parse for Html {
35    fn parse(input: ParseStream) -> Result<Self> {
36        let mut stack: Stack<Html> = Stack::new();
37
38        stack.push(Html {
39            children: vec![],
40            value: Lex::Tag {
41                element: HtmlElement::Fragment,
42                attributes: vec![],
43            },
44        });
45
46        while !input.is_empty() {
47            if input.peek(Token![<]) && input.peek2(Ident) {
48                input.parse::<Token![<]>()?;
49                let element: HtmlElement = input.parse()?;
50                let mut attrs: Vec<Attribute> = vec![];
51
52                while !(input.peek(Token![>]) || input.peek(Token![/])) {
53                    if input.peek(Ident)
54                        || input.peek(Token![type])
55                        || input.peek(Token![async])
56                        || input.peek(Token![for])
57                    {
58                        let attr: Attribute = input.parse()?;
59                        attrs.push(attr);
60                    }
61
62                    if input.is_empty() {
63                        return Err(syn::Error::new(
64                            input.span(),
65                            "Unexpected end of input in start tag",
66                        ));
67                    }
68
69                    if input.peek(Token![<]) {
70                        return Err(syn::Error::new(input.span(), "< shouldn't be here"));
71                    }
72                }
73
74                if input.peek(Token![/]) {
75                    input.parse::<Token![/]>()?;
76                }
77
78                input.parse::<Token![>]>()?;
79
80                let tag = Html {
81                    children: vec![],
82                    value: Lex::Tag {
83                        element: element.clone(),
84                        attributes: attrs,
85                    },
86                };
87
88                if element.is_void() {
89                    if let Some(mut parent) = stack.pop() {
90                        parent.children.push(tag);
91                        stack.push(parent);
92                    } else {
93                        stack.push(tag);
94                    }
95                } else {
96                    stack.push(tag);
97                }
98            } else if input.peek(Token![<]) && input.peek2(Token![/]) && input.peek3(Ident) {
99                input.parse::<Token![<]>()?;
100                input.parse::<Token![/]>()?;
101
102                let el: HtmlElement = input.parse()?;
103
104                let previous1 = if let Some(previous1) = stack.pop() {
105                    previous1
106                } else {
107                    return Err(syn::Error::new(
108                        input.span(),
109                        &format!("There is no opening tag for </{}>", &el.to_string()),
110                    ));
111                };
112
113                if let Lex::Tag { element, .. } = &previous1.value {
114                    if element.to_string() == el.to_string() {
115                        if let Some(mut previous2) = stack.pop() {
116                            previous2.children.push(previous1);
117                            stack.push(previous2);
118                        } else {
119                            return Err(syn::Error::new(
120                                input.span(),
121                                "A single root is mandatory.",
122                            ));
123                        }
124                    } else {
125                        return Err(syn::Error::new(input.span(), "Invalid syntax"));
126                    }
127                }
128
129                input.parse::<Token![>]>()?;
130            } else if input.peek(Brace) {
131                let content;
132                braced!(content in input);
133
134                if !content.peek(Brace) {
135                    let sanitized_var = Self::extract_variable(&content)?;
136
137                    if let Some(mut parent) = stack.pop() {
138                        parent.children.push(Html {
139                            children: vec![],
140                            value: Lex::SanitizedVar(sanitized_var),
141                        });
142                        stack.push(parent);
143                    } else {
144                        return Err(syn::Error::new(input.span(), "A single root is mandatory"));
145                    }
146                } else {
147                    let var;
148                    braced!(var in content);
149
150                    let var = Self::extract_variable(&var)?;
151
152                    if let Some(mut parent) = stack.pop() {
153                        parent.children.push(Html {
154                            children: vec![],
155                            value: Lex::Var(var),
156                        });
157                        stack.push(parent);
158                    } else {
159                        return Err(syn::Error::new(input.span(), "A single root is mandatory"));
160                    }
161                }
162            } else if input.peek(Token![if]) {
163                input.parse::<Token![if]>()?;
164
165                let condition = Self::extract_condition(input)?;
166
167                let body;
168                braced!(body in input);
169                let body: ParseStream = &body;
170
171                let body: Html = Self::parse(&body)?;
172
173                if let Some(mut previous) = stack.pop() {
174                    previous.children.push(Html {
175                        children: vec![body],
176                        value: Lex::If(condition.to_string()),
177                    });
178                    stack.push(previous);
179                } else {
180                    return Err(syn::Error::new(
181                        input.span(),
182                        "`if` should be used inside of a node",
183                    ));
184                }
185            } else if input.peek(Token![else]) && input.peek2(Token![if]) {
186                input.parse::<Token![else]>()?;
187                input.parse::<Token![if]>()?;
188
189                let condition = Self::extract_condition(input)?;
190
191                let body;
192                braced!(body in input);
193                let body: ParseStream = &body;
194
195                let body: Html = Self::parse(&body)?;
196
197                if let Some(mut previous) = stack.pop() {
198                    previous.children.push(Html {
199                        children: vec![body],
200                        value: Lex::ElseIf(condition.to_string()),
201                    });
202                    stack.push(previous);
203                } else {
204                    return Err(syn::Error::new(
205                        input.span(),
206                        "`else-if` should be used inside of a node",
207                    ));
208                }
209            } else if input.peek(Token![else]) {
210                input.parse::<Token![else]>()?;
211
212                let body;
213                braced!(body in input);
214                let body: ParseStream = &body;
215
216                let body: Html = Self::parse(&body)?;
217
218                if let Some(mut previous) = stack.pop() {
219                    previous.children.push(Html {
220                        children: vec![body],
221                        value: Lex::Else,
222                    });
223                    stack.push(previous);
224                } else {
225                    return Err(syn::Error::new(
226                        input.span(),
227                        "`else` should be used inside of a node",
228                    ));
229                }
230            } else if input.peek(Token![for]) {
231                input.parse::<Token![for]>()?;
232
233                let var: Ident = input.parse()?;
234
235                input.parse::<Token![in]>()?;
236
237                let iter = Self::extract_iter(input)?;
238
239                let body;
240                braced!(body in input);
241                let body: ParseStream = &body;
242
243                let body: Html = Self::parse(&body)?;
244
245                if let Some(mut previous) = stack.pop() {
246                    previous.children.push(Html {
247                        children: vec![body],
248                        value: Lex::For {
249                            var: var.to_string(),
250                            iter: iter.to_string(),
251                        },
252                    });
253                    stack.push(previous);
254                } else {
255                    return Err(syn::Error::new(
256                        input.span(),
257                        "`for-in` should be used inside of a node",
258                    ));
259                }
260            } else if input.peek(Token![<]) && input.peek2(Token![!]) && input.peek3(Ident) {
261                input.parse::<Token![<]>()?;
262                input.parse::<Token![!]>()?;
263
264                let ident: Ident = input.parse()?;
265                let doc_type: Ident = input.parse()?;
266
267                if ident.to_string().to_uppercase() != "DOCTYPE"
268                    || doc_type.to_string().to_lowercase() != "html"
269                {
270                    return Err(syn::Error::new(
271                        input.span(),
272                        "`DOCTYPE html` is expected following by `!`",
273                    ));
274                }
275
276                input.parse::<Token![>]>()?;
277
278                if let Some(mut previous) = stack.pop() {
279                    previous.children.push(Html {
280                        children: vec![],
281                        value: Lex::DocType,
282                    });
283                    stack.push(previous);
284                } else {
285                    return Err(syn::Error::new(
286                        input.span(),
287                        "A single root is mandatory. DOCTYPE cannot be a parent node.",
288                    ));
289                }
290            } else {
291                return Err(syn::Error::new(input.span(), "Invalid token"));
292            }
293        }
294
295        if stack.len != 1 {
296            return Err(syn::Error::new(
297                input.span(),
298                "Error: lack of some closing tags",
299            ));
300        }
301
302        let root = if let Some(root) = stack.pop() {
303            root
304        } else {
305            return Err(syn::Error::new(input.span(), "There is no root tag"));
306        };
307
308        Ok(root)
309    }
310}
311
312impl Html {
313    fn extract_variable(input: &ParseBuffer) -> Result<String> {
314        let variable = input.step(|cursor| {
315            let result: Result<(String, Cursor)> = {
316                let mut rest = *cursor;
317                let mut tokens = String::new();
318
319                while let Some((tt, next)) = rest.token_tree() {
320                    tokens += &tt.to_string();
321                    rest = next;
322                }
323
324                Ok((tokens, rest))
325            };
326
327            result
328        })?;
329
330        Ok(variable)
331    }
332
333    fn extract_iter(input: ParseStream) -> Result<TokenStream> {
334        let iter = input.step(|cursor| {
335            let result: Result<(TokenStream, Cursor)> = {
336                let mut rest = *cursor;
337                let mut tokens: Vec<TokenTree> = vec![];
338
339                while let Some((tt, next)) = rest.token_tree() {
340                    if let TokenTree::Group(g) = &tt {
341                        if g.delimiter() == Delimiter::Brace {
342                            return Ok((tokens.into_iter().collect(), rest));
343                        }
344                    }
345
346                    tokens.push(tt);
347                    rest = next;
348                }
349
350                if tokens.is_empty() {
351                    Err(syn::Error::new(input.span(), "Iter should be here."))
352                } else {
353                    Ok((tokens.into_iter().collect(), *cursor))
354                }
355            };
356
357            result
358        });
359
360        iter
361    }
362
363    fn extract_condition(input: ParseStream) -> Result<TokenStream> {
364        let condition = input.step(|cursor| {
365            let result: Result<(TokenStream, Cursor)> = {
366                let mut rest = *cursor;
367                let mut tokens: Vec<TokenTree> = vec![];
368
369                while let Some((tt, next)) = rest.token_tree() {
370                    if let TokenTree::Group(g) = &tt {
371                        if g.delimiter() == Delimiter::Brace {
372                            return Ok((tokens.into_iter().collect(), rest));
373                        }
374                    }
375
376                    tokens.push(tt);
377                    rest = next;
378                }
379
380                if tokens.is_empty() {
381                    Err(syn::Error::new(input.span(), "Condition shuold be here."))
382                } else {
383                    Ok((tokens.into_iter().collect(), *cursor))
384                }
385            };
386
387            result
388        });
389
390        condition
391    }
392}
393
394#[derive(Clone, Debug)]
395pub enum Lex {
396    Tag {
397        element: HtmlElement,
398        attributes: Vec<Attribute>,
399    },
400    DocType,
401    SanitizedVar(String),
402    Var(String),
403    If(String),
404    ElseIf(String),
405    Else,
406    For {
407        var: String,
408        iter: String,
409    },
410}
411
412#[derive(Debug, Clone)]
413pub struct Attribute(String, Option<AttributeValue>);
414
415#[derive(Debug, Clone)]
416pub enum AttributeValue {
417    Text(String),
418    Var(Expr),
419}
420
421impl Parse for Attribute {
422    fn parse(input: ParseStream) -> Result<Self> {
423        let key = if input.peek(Ident) {
424            let key: Ident = input.parse()?;
425            let mut key = key.to_string();
426
427            while input.peek(Token![-]) {
428                input.parse::<Token![-]>()?;
429                key += "-";
430
431                if input.peek(Ident) {
432                    key += &input.parse::<Ident>()?.to_string();
433                } else if input.peek(Token![async]) {
434                    input.parse::<Token![async]>()?;
435                    key += "async";
436                } else if input.peek(Token![type]) {
437                    input.parse::<Token![type]>()?;
438                    key += "type";
439                } else if input.peek(Token![for]) {
440                    input.parse::<Token![for]>()?;
441                    key += "for";
442                } else {
443                    return Err(syn::Error::new(input.span(), "Cannot be used as attribute"));
444                }
445            }
446
447            key
448        } else if input.peek(Token![async]) {
449            input.parse::<Token![async]>()?;
450            "async".to_string()
451        } else if input.peek(Token![type]) {
452            input.parse::<Token![type]>()?;
453            "type".to_string()
454        } else if input.peek(Token![for]) {
455            input.parse::<Token![for]>()?;
456            "for".to_string()
457        } else {
458            return Err(syn::Error::new(
459                input.span(),
460                "Some attributes should be here.",
461            ));
462        };
463
464        if input.peek(Token![=]) {
465            input.parse::<Token![=]>()?;
466
467            if input.peek(Brace) {
468                let mut value;
469                braced!(value in input);
470                braced!(value in value);
471                let value: Expr = value.parse()?;
472                return Ok(Attribute(key, Some(AttributeValue::Var(value))));
473            }
474
475            let value: LitStr = input.parse()?;
476            return Ok(Attribute(key, Some(AttributeValue::Text(value.value()))));
477        } else {
478            Ok(Attribute(key, None))
479        }
480    }
481}
482
483impl Attribute {
484    pub fn key(&self) -> &str {
485        &self.0
486    }
487    pub fn value(&self) -> &Option<AttributeValue> {
488        &self.1
489    }
490}