Skip to main content

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    /// `@route("users.show", "id", user.id)` — the path of a named route.
32    Route { name: String, params: Vec<(String, Expr)> },
33}
34
35/// One arm of an `@if` chain.
36#[derive(Debug, Clone, PartialEq)]
37pub struct Branch {
38    pub condition: Expr,
39    pub body: Vec<Node>,
40}
41
42/// A reference from one template to another.
43///
44/// It keeps the position it was written at, so "that layout does not exist" can
45/// be reported with the same precision as a syntax error instead of surfacing
46/// as a bare file-not-found from somewhere deep in the renderer.
47#[derive(Debug, Clone, PartialEq)]
48pub struct TemplateRef {
49    /// The referenced view name, as written.
50    pub name: String,
51    /// The view the reference was written in.
52    pub file: String,
53    pub line: usize,
54    pub column: usize,
55}
56
57impl TemplateRef {
58    /// Blame this reference for a failure.
59    pub fn error(&self, message: impl Into<String>) -> Error {
60        Error::Template {
61            file: self.file.clone(),
62            line: self.line,
63            column: self.column,
64            message: message.into(),
65        }
66    }
67}
68
69/// A parsed template: what it renders, plus the layout wiring around it.
70#[derive(Debug, Clone, PartialEq)]
71pub struct Template {
72    /// The view name it was loaded under, e.g. `pages.home`.
73    pub name: String,
74    /// Everything outside a `@section`.
75    pub nodes: Vec<Node>,
76    /// The layout named by `@extends`, if any.
77    pub extends: Option<TemplateRef>,
78    /// Section bodies, keyed by name and hoisted out of `nodes`.
79    ///
80    /// Sections are captured rather than rendered in place: they exist to be
81    /// pulled into a layout by `@yield`, so a template that defines one and is
82    /// rendered on its own simply does not emit it.
83    pub sections: BTreeMap<String, Vec<Node>>,
84}