aboutsummaryrefslogtreecommitdiff
path: root/lib/render/template.mli
blob: 41184960b026df9a981a58ee86f94f05aa0202a8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
(** A deliberately logic-less template engine (spec section 5).

    A template is DATA, never a program: placeholders, sections, inverted
    sections and comments, and nothing else. There are no partials, no lambdas,
    no expression evaluation, no arithmetic, and no filesystem or process
    access. There is deliberately no "raw" or triple-brace form -- a template
    cannot opt out of its flavour's escaping.

    Anything a calendar needs that this cannot express (a month grid's leading
    blanks, week bucketing) is computed in {!View} and handed in as data. That
    division is the design, not a workaround. *)

(** What a template can be rendered against. *)
type value =
  | Str of string
  | Bool of bool
  | List of value list
  | Obj of (string * value) list

type node =
  | Text of string
  | Var of string list  (** dotted path, e.g. ["name"; "la"] *)
  | Section of string list * node list
  | Inverted of string list * node list

(** Never raises; a malformed template is an [Error] with a human-readable
    reason, because the template is user input. *)
val parse : string -> (node list, string) result

(** Render against a value. Escaping is applied to every interpolated value and
    NEVER to the template's own literal text, which is the author's markup.
    A missing key renders as the empty string -- the one deliberate silence, so
    that a template survives a rite that does not set every optional field. *)
val render : flavour:Escape.flavour -> node list -> value -> string

(** [parse] then [render]. *)
val render_string : flavour:Escape.flavour -> string -> value -> (string, string) result