templr_parser/
lib.rs

1use proc_macro2::{extra::DelimSpan, TokenStream};
2use quote::{ToTokens, TokenStreamExt};
3use syn::{
4    braced, bracketed, parenthesized,
5    parse::{Parse, ParseStream},
6    token::{self, Brace, Bracket, Group, Paren},
7    Token,
8};
9
10pub mod attrs;
11pub mod call;
12pub mod doctype;
13pub mod element;
14pub mod entity;
15pub mod for_stmt;
16pub mod if_stmt;
17pub mod let_stmt;
18pub mod match_stmt;
19pub mod name;
20pub mod raw_text;
21
22pub use {
23    attrs::Attr, call::Call, doctype::Doctype, element::Element, entity::Entity, for_stmt::For,
24    if_stmt::If, let_stmt::Let, match_stmt::Match, name::Name, raw_text::RawText,
25};
26
27mod kw {
28    syn::custom_keyword!(context);
29    syn::custom_keyword!(children);
30    syn::custom_keyword!(with);
31}
32
33/// Parses the body of a hash statement, consumes the entirery of its input
34fn parse_to_vec<T: Parse>(input: ParseStream) -> syn::Result<Vec<T>> {
35    let mut body = vec![];
36    while !input.is_empty() {
37        body.push(T::parse(input)?);
38    }
39    Ok(body)
40}
41
42#[derive(Debug, Clone)]
43pub struct Scope<T> {
44    pub pound: token::Pound,
45    pub brace: Brace,
46    pub body: Vec<T>,
47}
48
49impl<T: Parse> Parse for Scope<T> {
50    fn parse(input: ParseStream) -> syn::Result<Self> {
51        let content;
52        Ok(Self {
53            pound: input.parse()?,
54            brace: braced!(content in input),
55            body: parse_to_vec(&content)?,
56        })
57    }
58}
59
60impl<T: ToTokens> ToTokens for Scope<T> {
61    fn to_tokens(&self, tokens: &mut TokenStream) {
62        self.pound.to_tokens(tokens);
63        self.brace.surround(tokens, |tokens| {
64            tokens.append_all(&self.body);
65        });
66    }
67}
68
69#[derive(Debug, Clone)]
70pub enum Block {
71    Valid(syn::Block),
72    Invalid { brace: Brace, body: TokenStream },
73}
74
75impl Block {
76    pub fn brace_span(&self) -> DelimSpan {
77        match self {
78            Block::Valid(block) => block.brace_token.span,
79            Block::Invalid { brace, .. } => brace.span,
80        }
81    }
82}
83
84impl Parse for Block {
85    fn parse(input: ParseStream) -> syn::Result<Self> {
86        let content;
87        match input.parse() {
88            Ok(block) => Ok(Self::Valid(block)),
89            Err(_) => Ok(Self::Invalid {
90                brace: braced!(content in input),
91                body: content.parse()?,
92            }),
93        }
94    }
95}
96
97impl ToTokens for Block {
98    fn to_tokens(&self, tokens: &mut TokenStream) {
99        match self {
100            Self::Valid(block) => block.to_tokens(tokens),
101            Self::Invalid { brace, body } => {
102                brace.surround(tokens, |tokens| body.to_tokens(tokens))
103            }
104        }
105    }
106}
107
108#[derive(Debug, Clone)]
109pub struct With {
110    pub pound: Token![#],
111    pub with: kw::with,
112    pub context: kw::context,
113    pub ty: Option<(Token![:], Box<syn::Type>)>,
114    pub eq: Token![=],
115    pub expr: Box<syn::Expr>,
116    pub brace: Brace,
117    pub nodes: Vec<Node>,
118}
119
120impl Parse for With {
121    fn parse(input: ParseStream) -> syn::Result<Self> {
122        let content;
123        Ok(Self {
124            pound: input.parse()?,
125            with: input.parse()?,
126            context: input.parse()?,
127            ty: {
128                let lookahead1 = input.lookahead1();
129                if lookahead1.peek(Token![:]) {
130                    Some((
131                        input.parse()?,
132                        Box::new(From::from(syn::TypeReference {
133                            and_token: input.parse()?,
134                            lifetime: None,
135                            mutability: None,
136                            elem: input.parse()?,
137                        })),
138                    ))
139                } else if lookahead1.peek(Token![=]) {
140                    None
141                } else {
142                    return Err(lookahead1.error());
143                }
144            },
145            eq: input.parse()?,
146            expr: Box::new(syn::Expr::parse_without_eager_brace(input)?),
147            brace: braced!(content in input),
148            nodes: parse_to_vec(&content)?,
149        })
150    }
151}
152
153impl ToTokens for With {
154    fn to_tokens(&self, tokens: &mut TokenStream) {
155        self.pound.to_tokens(tokens);
156        self.with.to_tokens(tokens);
157        self.context.to_tokens(tokens);
158        if let Some((colon, ty)) = &self.ty {
159            colon.to_tokens(tokens);
160            ty.to_tokens(tokens);
161        }
162        self.eq.to_tokens(tokens);
163        self.expr.to_tokens(tokens);
164        self.brace.surround(tokens, |tokens| {
165            tokens.append_all(&self.nodes);
166        });
167    }
168}
169
170// #use context as ctx: &u32;
171// #with context: &u32 = &0 {
172//     something
173// }
174
175#[derive(Debug, Clone)]
176pub enum Node {
177    Entity(Entity),
178    Doctype(Doctype),
179    Element(Element),
180    RawText(RawText),
181    Paren(Paren, Vec<Node>),
182    Bracket(Bracket, Vec<Node>),
183    Expr(Block),
184    If(If<Self>),
185    Match(Match<Self>),
186    For(For<Self>),
187    Scope(Scope<Self>),
188    Let(Let),
189    Call(Call),
190    With(With),
191}
192
193impl Parse for Node {
194    fn parse(input: ParseStream) -> syn::Result<Self> {
195        if input.peek(Token![&]) {
196            Ok(Self::Entity(input.parse()?))
197        } else if input.peek(Token![<]) {
198            if input.peek2(Token![!]) {
199                Ok(Self::Doctype(input.parse()?))
200            } else {
201                Ok(Self::Element(input.parse()?))
202            }
203        } else if input.peek(Brace) {
204            Ok(Self::Expr(input.parse()?))
205        } else if input.peek(Token![#]) {
206            if input.peek2(Token![if]) {
207                Ok(Self::If(input.parse()?))
208            } else if input.peek2(Token![match]) {
209                Ok(Self::Match(input.parse()?))
210            } else if input.peek2(Token![for]) {
211                Ok(Self::For(input.parse()?))
212            } else if input.peek2(Token![let]) {
213                Ok(Self::Let(input.parse()?))
214            } else if input.peek2(Brace) {
215                Ok(Self::Scope(input.parse()?))
216            } else if input.peek2(kw::with) {
217                Ok(Self::With(input.parse()?))
218            } else {
219                Ok(Self::Call(input.parse()?))
220            }
221        } else if input.peek(Paren) {
222            let content;
223            Ok(Self::Paren(
224                parenthesized!(content in input),
225                parse_to_vec(&content)?,
226            ))
227        } else if input.peek(Bracket) {
228            let content;
229            Ok(Self::Bracket(
230                bracketed!(content in input),
231                parse_to_vec(&content)?,
232            ))
233        } else if input.peek(Group) {
234            Err(input.error("unexpected none deliminated group"))
235        } else if input.is_empty() {
236            Err(input.error("expected a templr node"))
237        } else {
238            Ok(Self::RawText(input.parse()?))
239        }
240    }
241}
242
243impl ToTokens for Node {
244    fn to_tokens(&self, tokens: &mut TokenStream) {
245        match self {
246            Self::Entity(slf) => slf.to_tokens(tokens),
247            Self::Doctype(slf) => slf.to_tokens(tokens),
248            Self::Element(slf) => slf.to_tokens(tokens),
249            Self::RawText(slf) => slf.to_tokens(tokens),
250            Self::Paren(paren, nodes) => paren.surround(tokens, |tokens| {
251                tokens.append_all(nodes);
252            }),
253            Self::Bracket(bracket, nodes) => bracket.surround(tokens, |tokens| {
254                tokens.append_all(nodes);
255            }),
256            Self::Expr(slf) => slf.to_tokens(tokens),
257            Self::If(slf) => slf.to_tokens(tokens),
258            Self::Match(slf) => slf.to_tokens(tokens),
259            Self::For(slf) => slf.to_tokens(tokens),
260            Self::Scope(slf) => slf.to_tokens(tokens),
261            Self::Let(slf) => slf.to_tokens(tokens),
262            Self::Call(slf) => slf.to_tokens(tokens),
263            Self::With(slf) => slf.to_tokens(tokens),
264        }
265    }
266}
267
268#[derive(Debug, Clone)]
269pub struct UseContext {
270    pub pound: Token![#],
271    pub use_token: Token![use],
272    pub context: kw::context,
273    pub as_pat: Option<(Token![as], Box<syn::Pat>)>,
274    pub colon_ty: Option<(Token![:], Box<syn::Type>)>,
275    pub semi: Token![;],
276}
277
278impl Parse for UseContext {
279    fn parse(input: ParseStream) -> syn::Result<Self> {
280        Ok(Self {
281            pound: input.parse()?,
282            use_token: input.parse()?,
283            context: input.parse()?,
284            as_pat: {
285                let lookahead1 = input.lookahead1();
286                match lookahead1.peek(Token![as]) {
287                    true => Some((input.parse()?, Box::new(syn::Pat::parse_single(input)?))),
288                    false if lookahead1.peek(Token![:]) || lookahead1.peek(Token![;]) => None,
289                    false => return Err(lookahead1.error()),
290                }
291            },
292            colon_ty: {
293                let lookahead1 = input.lookahead1();
294                match lookahead1.peek(Token![:]) {
295                    true => Some((
296                        input.parse()?,
297                        Box::new(From::from(syn::TypeReference {
298                            and_token: input.parse()?,
299                            lifetime: None,
300                            mutability: None,
301                            elem: input.parse()?,
302                        })),
303                    )),
304                    false if lookahead1.peek(Token![;]) => None,
305                    false => return Err(lookahead1.error()),
306                }
307            },
308            semi: input.parse()?,
309        })
310    }
311}
312
313impl ToTokens for UseContext {
314    fn to_tokens(&self, tokens: &mut TokenStream) {
315        self.pound.to_tokens(tokens);
316        self.use_token.to_tokens(tokens);
317        self.context.to_tokens(tokens);
318        if let Some((as_token, pat)) = &self.as_pat {
319            as_token.to_tokens(tokens);
320            pat.to_tokens(tokens);
321        }
322        if let Some((colon, ty)) = &self.colon_ty {
323            colon.to_tokens(tokens);
324            ty.to_tokens(tokens);
325        }
326        self.semi.to_tokens(tokens);
327    }
328}
329
330#[derive(Debug, Clone)]
331pub struct UseChildren {
332    pub pound: Token![#],
333    pub use_token: Token![use],
334    pub children: kw::children,
335    pub as_pat: Option<(Token![as], Box<syn::Pat>)>,
336    pub semi: Token![;],
337}
338
339impl Parse for UseChildren {
340    fn parse(input: ParseStream) -> syn::Result<Self> {
341        Ok(Self {
342            pound: input.parse()?,
343            use_token: input.parse()?,
344            children: input.parse()?,
345            as_pat: {
346                let lookahead1 = input.lookahead1();
347                match lookahead1.peek(Token![as]) {
348                    true => Some((input.parse()?, Box::new(syn::Pat::parse_single(input)?))),
349                    false if lookahead1.peek(Token![;]) => None,
350                    false => return Err(lookahead1.error()),
351                }
352            },
353            semi: input.parse()?,
354        })
355    }
356}
357
358impl ToTokens for UseChildren {
359    fn to_tokens(&self, tokens: &mut TokenStream) {
360        self.pound.to_tokens(tokens);
361        self.use_token.to_tokens(tokens);
362        self.children.to_tokens(tokens);
363        if let Some((as_token, pat)) = &self.as_pat {
364            as_token.to_tokens(tokens);
365            pat.to_tokens(tokens);
366        }
367        self.semi.to_tokens(tokens);
368    }
369}
370
371#[derive(Debug, Clone)]
372pub enum Use {
373    Context(UseContext),
374    Children(UseChildren),
375}
376
377impl Parse for Use {
378    fn parse(input: ParseStream) -> syn::Result<Self> {
379        let fork = input.fork();
380        let _: Token![#] = fork.parse()?;
381        let _: Token![use] = fork.parse()?;
382        let lookahead1 = fork.lookahead1();
383        if lookahead1.peek(kw::children) {
384            Ok(Use::Children(input.parse()?))
385        } else if lookahead1.peek(kw::context) {
386            Ok(Use::Context(input.parse()?))
387        } else {
388            Err(lookahead1.error())
389        }
390    }
391}
392
393impl ToTokens for Use {
394    fn to_tokens(&self, tokens: &mut TokenStream) {
395        match self {
396            Self::Context(slf) => slf.to_tokens(tokens),
397            Self::Children(slf) => slf.to_tokens(tokens),
398        }
399    }
400}
401
402#[derive(Debug, Clone)]
403pub struct TemplBody {
404    pub uses: Vec<Use>,
405    pub nodes: Vec<Node>,
406}
407
408impl TemplBody {
409    pub fn use_children(&self) -> Option<&UseChildren> {
410        self.uses.iter().find_map(|u| match u {
411            Use::Children(u) => Some(u),
412            _ => None,
413        })
414    }
415    pub fn use_context(&self) -> Option<&UseContext> {
416        self.uses.iter().find_map(|u| match u {
417            Use::Context(u) => Some(u),
418            _ => None,
419        })
420    }
421}
422
423impl Parse for TemplBody {
424    fn parse(input: ParseStream) -> syn::Result<Self> {
425        let mut uses = vec![];
426
427        while input.peek(Token![#]) && input.peek2(Token![use]) {
428            uses.push(Use::parse(input)?);
429        }
430        if let Some(u) = uses
431            .iter()
432            .filter_map(|u| match u {
433                Use::Children(u) => Some(u),
434                _ => None,
435            })
436            .nth(1)
437        {
438            return Err(syn::Error::new(
439                u.pound.span,
440                "Cannot redefine `#use children ...`",
441            ));
442        } else if let Some(u) = uses
443            .iter()
444            .filter_map(|u| match u {
445                Use::Context(u) => Some(u),
446                _ => None,
447            })
448            .nth(1)
449        {
450            return Err(syn::Error::new(
451                u.pound.span,
452                "Cannot redefine `#use context ...`",
453            ));
454        }
455
456        Ok(Self {
457            uses,
458            nodes: parse_to_vec(input)?,
459        })
460    }
461}
462
463impl ToTokens for TemplBody {
464    fn to_tokens(&self, tokens: &mut TokenStream) {
465        tokens.append_all(&self.uses);
466        tokens.append_all(&self.nodes);
467    }
468}