tour_parser/
syntax.rs

1//! Syntax definition for template statements.
2//!
3//! This only syntax definition for partial statements like `{{ if user.is_admin() }}`.
4//!
5//! For full ast declaration, see [`ast`][super::ast].
6use std::rc::Rc;
7use syn::{
8    ext::IdentExt as _,
9    parse::{Parse, ParseStream},
10    *,
11};
12
13/// Template statments.
14pub enum StmtSyn {
15    // ===== Composition =====
16
17    /// `{{ <layout | extends> <"path"> }}`
18    Layout(LayoutTempl),
19    /// `{{ use <"path"> as <Ident> }}`
20    Use(UseTempl),
21    /// `{{ render <<Ident> | "path"> [block <Ident>] }}`
22    Render(RenderTempl),
23    /// `{{ yield [block <Ident>] }}`
24    Yield(YieldTempl),
25
26    // ===== Scoped =====
27
28    /// `{{ [pub] [static] block <Ident> }}`
29    Block(BlockTempl),
30    /// `{{ if <Expr> }}`
31    If(IfTempl),
32    /// `{{ else [if <Expr>] }}`
33    Else(ElseTempl),
34    /// `{{ for <Pat> in <Expr> }}`
35    For(ForTempl),
36    /// `{{ endblock }}`
37    Endblock(kw::endblock),
38    /// `{{ endif }}`
39    EndIf(kw::endif),
40    /// `{{ endfor }}`
41    EndFor(kw::endfor),
42
43    // ===== Arbitrary =====
44
45    /// `{{ <ItemTempl> }}`
46    Item(Rc<ItemTempl>),
47    /// `{{ <Expr> }}`
48    Expr(Rc<Expr>),
49}
50
51/// `{{ <layout | extends> <"path"> }}`
52pub struct LayoutTempl {
53    pub layout_token: kw::layout,
54    pub path: LitStr,
55}
56
57/// `{{ use <"path"> as <Ident> }}`
58pub struct UseTempl {
59    pub use_token: Token![use],
60    pub path: LitStr,
61    pub as_token: Token![as],
62    pub ident: Ident,
63}
64
65/// `{{ render <<Ident> | "path"> [block <Ident>] }}`
66pub struct RenderTempl {
67    pub render_token: kw::render,
68    pub value: RenderValue,
69    pub block: Option<(kw::block,Ident)>
70}
71
72/// `<Ident> | "path"`
73pub enum RenderValue {
74    Ident(Ident),
75    Path(LitStr),
76}
77
78/// `{{ yield [block <Ident>] }}`
79pub struct YieldTempl {
80    pub yield_token: Token![yield],
81    pub block: Option<(kw::block, Ident)>
82}
83
84/// `{{ [pub] [static] block <Ident> }}`
85pub struct BlockTempl {
86    pub pub_token: Option<Token![pub]>,
87    pub static_token: Option<Token![static]>,
88    pub block_token: kw::block,
89    pub name: Ident,
90}
91
92/// `{{ if <Expr> }}`
93pub struct IfTempl {
94    pub if_token: Token![if],
95    pub cond: Rc<Expr>,
96}
97
98/// `{{ else [if <Expr>] }}`
99pub struct ElseTempl {
100    pub else_token: Token![else],
101    pub elif_branch: Option<(Token![if],Rc<Expr>)>
102}
103
104/// `{{ for <Pat> in <Expr> }}`
105pub struct ForTempl {
106    pub for_token: Token![for],
107    pub pat: Rc<Pat>,
108    pub in_token: Token![in],
109    pub expr: Rc<Expr>,
110}
111
112/// `{{ <ItemTempl> }}`
113pub enum ItemTempl {
114    Use(ItemUse),
115    Const(ItemConst),
116}
117
118// ===== Parse implementation =====
119
120impl Parse for StmtSyn {
121    fn parse(input: ParseStream) -> Result<Self> {
122        match () {
123            _ if input.peek(kw::layout) => input.parse().map(Self::Layout),
124            _ if input.peek(kw::extends) => input.parse().map(Self::Layout),
125            _ if UseTempl::peek(input) => input.parse().map(Self::Use),
126            _ if input.peek(kw::render) => input.parse().map(Self::Render),
127            _ if input.peek(Token![yield]) => input.parse().map(Self::Yield),
128
129            _ if BlockTempl::peek(input) => input.parse().map(Self::Block),
130            _ if input.peek(Token![if]) => input.parse().map(Self::If),
131            _ if input.peek(Token![else]) => input.parse().map(Self::Else),
132            _ if input.peek(Token![for]) => input.parse().map(Self::For),
133            _ if input.peek(kw::endblock) => input.parse().map(Self::Endblock),
134            _ if input.peek(kw::endif) => input.parse().map(Self::EndIf),
135            _ if input.peek(kw::endfor) => input.parse().map(Self::EndFor),
136
137            _ if ItemTempl::peek(input) => input.parse().map(Rc::new).map(Self::Item),
138            _ => input.parse().map(Rc::new).map(Self::Expr),
139        }
140    }
141}
142
143impl UseTempl {
144    pub fn peek(input: ParseStream) -> bool {
145        input.peek(Token![use]) && input.peek2(LitStr)
146    }
147}
148
149impl BlockTempl {
150    pub fn peek(input: ParseStream) -> bool {
151        (input.peek(Token![pub]) && input.peek2(Token![static]) && input.peek3(kw::block)) ||
152        (input.peek(Token![pub]) && input.peek2(kw::block)) ||
153        (input.peek(Token![static]) && input.peek2(kw::block)) ||
154        input.peek(kw::block)
155    }
156}
157
158impl ItemTempl {
159    pub fn peek(input: ParseStream) -> bool {
160        input.peek(Token![use]) ||
161        input.peek(Token![const])
162    }
163}
164
165impl Parse for LayoutTempl {
166    fn parse(input: ParseStream) -> Result<Self> {
167        Ok(Self {
168            layout_token: match () {
169                _ if input.peek(kw::layout) => input.parse()?,
170                _ if input.peek(kw::extends) => kw::layout(input.parse::<kw::extends>()?.span),
171                _ => unreachable!()
172            },
173            path: input.parse()?,
174        })
175    }
176}
177
178impl Parse for UseTempl {
179    fn parse(input: ParseStream) -> Result<Self> {
180        Ok(Self {
181            use_token: input.parse()?,
182            path: input.parse()?,
183            as_token: input.parse()?,
184            ident: input.call(Ident::parse_any)?,
185        })
186    }
187}
188
189impl Parse for RenderTempl {
190    fn parse(input: ParseStream) -> Result<Self> {
191        Ok(Self {
192            render_token: input.parse()?,
193            value: input.parse()?,
194            block: if input.peek(kw::block) {
195                Some((input.parse()?,input.call(Ident::parse_any)?))
196            } else {
197                None
198            },
199        })
200    }
201}
202
203impl Parse for RenderValue {
204    fn parse(input: ParseStream) -> Result<Self> {
205        let look = input.lookahead1();
206        match () {
207            _ if look.peek(LitStr) => input.parse().map(Self::Path),
208            _ if look.peek(Ident::peek_any) => input.call(Ident::parse_any).map(Self::Ident),
209            _ => Err(look.error()),
210        }
211    }
212}
213
214impl Parse for YieldTempl {
215    fn parse(input: ParseStream) -> Result<Self> {
216        Ok(Self {
217            yield_token: input.parse()?,
218            block: if input.peek(kw::block) {
219                Some((input.parse()?,input.call(Ident::parse_any)?))
220            } else {
221                None
222            },
223        })
224    }
225}
226
227impl Parse for BlockTempl {
228    fn parse(input: ParseStream) -> Result<Self> {
229        Ok(Self {
230            pub_token: input.parse()?,
231            static_token: input.parse()?,
232            block_token: input.parse()?,
233            name: input.call(Ident::parse_any)?,
234        })
235    }
236}
237
238impl Parse for IfTempl {
239    fn parse(input: ParseStream) -> Result<Self> {
240        Ok(Self {
241            if_token: input.parse()?,
242            cond: Rc::new(input.parse()?),
243        })
244    }
245}
246
247impl Parse for ElseTempl {
248    fn parse(input: ParseStream) -> Result<Self> {
249        Ok(Self {
250            else_token: input.parse()?,
251            elif_branch: if input.peek(Token![if]) {
252                Some((input.parse()?,Rc::new(input.parse()?)))
253            } else {
254                None
255            },
256        })
257    }
258}
259
260impl Parse for ForTempl {
261    fn parse(input: ParseStream) -> Result<Self> {
262        Ok(Self {
263            for_token: input.parse()?,
264            // this Pat function that is used by syn parse
265            pat: Rc::new(Pat::parse_multi_with_leading_vert(input)?),
266            in_token: input.parse()?,
267            expr: Rc::new(input.parse()?),
268        })
269    }
270}
271
272impl Parse for ItemTempl {
273    fn parse(input: ParseStream) -> Result<Self> {
274        let look = input.lookahead1();
275        match () {
276            _ if look.peek(Token![use]) => input.parse().map(Self::Use),
277            _ if look.peek(Token![const]) => input.parse().map(Self::Const),
278            _ => Err(look.error()),
279        }
280    }
281}
282
283mod kw {
284    syn::custom_keyword!(layout);
285    syn::custom_keyword!(extends);
286    syn::custom_keyword!(block);
287    syn::custom_keyword!(render);
288    syn::custom_keyword!(endblock);
289    syn::custom_keyword!(endif);
290    syn::custom_keyword!(endfor);
291}
292