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}
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}