rustlavel_view/ast.rs
1//! The parsed shape of a template.
2
3use crate::expr::Expr;
4use rustlavel_core::Error;
5use std::collections::BTreeMap;
6
7/// One renderable piece of a template.
8#[derive(Debug, Clone, PartialEq)]
9pub enum Node {
10 /// Literal markup.
11 Text(String),
12 /// `{{ expr }}` when escaped, `{!! expr !!}` when not.
13 Echo { expr: Expr, escaped: bool },
14 /// `@if` / `@elseif` / `@else`: the first branch whose condition is truthy
15 /// wins, otherwise `otherwise` runs if there is one.
16 If { branches: Vec<Branch>, otherwise: Option<Vec<Node>> },
17 /// `@foreach(subject as binding)`.
18 Foreach { subject: Expr, binding: String, body: Vec<Node> },
19 /// `@yield("name")`, with the optional fallback from `@yield("name", expr)`.
20 Yield { section: String, default: Option<Expr> },
21 /// `@include("partial")`.
22 Include(TemplateRef),
23 /// `@lang("auth.sign_in")`, and `@lang("greeting", "name", user.name)` when
24 /// the phrase has placeholders.
25 ///
26 /// The key is resolved when the page is rendered, never when the template
27 /// is parsed. A parsed template is cached and shared between requests, so
28 /// baking one language's words into it would serve those words to
29 /// everybody — see `Engine::translator`.
30 Lang { key: String, replacements: Vec<(String, Expr)> },
31}
32
33/// One arm of an `@if` chain.
34#[derive(Debug, Clone, PartialEq)]
35pub struct Branch {
36 pub condition: Expr,
37 pub body: Vec<Node>,
38}
39
40/// A reference from one template to another.
41///
42/// It keeps the position it was written at, so "that layout does not exist" can
43/// be reported with the same precision as a syntax error instead of surfacing
44/// as a bare file-not-found from somewhere deep in the renderer.
45#[derive(Debug, Clone, PartialEq)]
46pub struct TemplateRef {
47 /// The referenced view name, as written.
48 pub name: String,
49 /// The view the reference was written in.
50 pub file: String,
51 pub line: usize,
52 pub column: usize,
53}
54
55impl TemplateRef {
56 /// Blame this reference for a failure.
57 pub fn error(&self, message: impl Into<String>) -> Error {
58 Error::Template {
59 file: self.file.clone(),
60 line: self.line,
61 column: self.column,
62 message: message.into(),
63 }
64 }
65}
66
67/// A parsed template: what it renders, plus the layout wiring around it.
68#[derive(Debug, Clone, PartialEq)]
69pub struct Template {
70 /// The view name it was loaded under, e.g. `pages.home`.
71 pub name: String,
72 /// Everything outside a `@section`.
73 pub nodes: Vec<Node>,
74 /// The layout named by `@extends`, if any.
75 pub extends: Option<TemplateRef>,
76 /// Section bodies, keyed by name and hoisted out of `nodes`.
77 ///
78 /// Sections are captured rather than rendered in place: they exist to be
79 /// pulled into a layout by `@yield`, so a template that defines one and is
80 /// rendered on its own simply does not emit it.
81 pub sections: BTreeMap<String, Vec<Node>>,
82}