File: stencil-mustache.md

package info (click to toggle)
glaze 7.0.2-3
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 9,036 kB
  • sloc: cpp: 142,035; sh: 98; ansic: 26; makefile: 12
file content (251 lines) | stat: -rw-r--r-- 6,973 bytes parent folder | download | duplicates (4)
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# Stencil/Mustache (String Interpolation)

Glaze provides string interpolation for C++ structs through the `stencil` and `mustache` formats. These provide templating mechanisms for formatting structured data into strings, inspired by the Mustache templating language. This enables the generation of dynamic output by combining predefined templates with C++ structs.

## Basic Usage

```cpp
struct person
{
   std::string first_name{};
   std::string last_name{};
   uint32_t age{};
   bool hungry{};
   bool employed{};
};

// Basic interpolation
std::string_view layout = R"({{first_name}} {{last_name}} is {{age}} years old)";
person p{"Henry", "Foster", 34};
auto result = glz::stencil(layout, p);
// Result: "Henry Foster is 34 years old"
```

> [!NOTE]
>
> `result` in these examples is a `std::expected<std::string, glz::error_ctx>`. Like most functions in Glaze (e.g. `glz::write_json`) you can also pass in your own string buffer as the last argument, in which case the return type is `glz::error_ctx`.

## Template Syntax Specification

### Variable Interpolation
- `{{key}}` - Replaces with the value of the specified field from the struct
- `{{{key}}}` - Triple braces for unescaped output (mustache format only)

### Boolean Sections
- `{{#boolean_key}} CONTENT {{/boolean_key}}` - Shows CONTENT if the boolean field is true
- `{{^boolean_key}} CONTENT {{/boolean_key}}` - Shows CONTENT if the boolean field is false (inverted section)

### Container Iteration
- `{{#container_key}} TEMPLATE {{/container_key}}` - Iterates over container elements, applying TEMPLATE to each item
- `{{^container_key}} CONTENT {{/container_key}}` - Shows CONTENT if the container is empty

### Comments
- `{{! This is a comment}}` - Comments are ignored during template processing

## Examples

### Boolean Sections

```cpp
std::string_view layout = R"({{first_name}} {{last_name}} {{#employed}}Status: Employed, Age: {{age}}{{/employed}})";
person p{"Carol", "Davis", 30, true, true};
auto result = glz::stencil(layout, p);
// Result: "Carol Davis Status: Employed, Age: 30"
```

### Inverted Sections

```cpp
std::string_view layout = R"({{first_name}} {{last_name}} {{^hungry}}I'm not hungry{{/hungry}})";
person p{"Henry", "Foster", 34, false}; // hungry is false
auto result = glz::stencil(layout, p);
// Result: "Henry Foster I'm not hungry"
```

### Container Iteration

```cpp
struct TodoItem {
   std::string text;
   bool completed;
   std::string priority;
   size_t id;
};

struct TodoList {
   std::string title;
   std::vector<TodoItem> items;
   bool has_items;
};

std::string_view layout = R"({{title}}: {{#items}}{{text}} ({{priority}}) {{/items}})";
TodoList list{
   "My Tasks", 
   {{"Buy milk", false, "normal", 1}, {"Call mom", true, "high", 2}}, 
   true
};
auto result = glz::stencil(layout, list);
// Result: "My Tasks: Buy milk (normal) Call mom (high) "
```

### Nested Sections

```cpp
std::string_view layout = R"({{#items}}{{text}} {{#completed}}✓{{/completed}}{{^completed}}○{{/completed}} {{/items}})";
TodoList list{"Mixed Tasks", {{"Task 1", false, "high", 1}, {"Task 2", true, "low", 2}}, true};
auto result = glz::stencil(layout, list);
// Result: "Task 1 ○Task 2 ✓"
```

### Empty Container Handling

```cpp
std::string_view layout = R"({{title}}{{^items}} - No items found{{/items}})";
TodoList empty_list{"Empty List", {}, false};
auto result = glz::stencil(layout, empty_list);
// Result: "Empty List - No items found"
```

## Mustache Format

Glaze provides `glz::mustache` with the same interface as `glz::stencil`, but with HTML escaping behavior:

- **Double braces** `{{key}}` - HTML-escaped output (safe for HTML)
- **Triple braces** `{{{key}}}` - Unescaped output (raw HTML)

### HTML Escaping Examples

```cpp
struct html_content {
   std::string title;
   std::string raw_html;
};

// Double braces escape HTML
std::string_view layout = R"(<h1>{{title}}</h1>)";
html_content content{"My <Script> Title", ""};
auto result = glz::mustache(layout, content);
// Result: "<h1>My &lt;Script&gt; Title</h1>"

// Triple braces preserve HTML
std::string_view layout2 = R"(<div>{{{raw_html}}}</div>)";
html_content content2{"", "<strong>Bold text</strong>"};
auto result2 = glz::mustache(layout2, content2);
// Result: "<div><strong>Bold text</strong></div>"
```

### HTML Entities Escaped

The following characters are escaped in mustache double-brace interpolation:
- `<` → `&lt;`
- `>` → `&gt;`
- `&` → `&amp;`
- `"` → `&quot;`
- `'` → `&#x27;`

## Advanced Features

### Complex Template Example

```cpp
std::string_view blog_template = R"(
<!DOCTYPE html>
<html>
<head>
    <title>{{title}}</title>
</head>
<body>
    <h1>{{title}}</h1>
    <p>{{description}}</p>
    <div class="content">
        {{{raw_html}}}
    </div>
    {{#has_items}}
    <ul>
        {{#items}}
        <li class="{{css_class}}">{{text}} {{#completed}}✓{{/completed}}</li>
        {{/items}}
    </ul>
    {{/has_items}}
</body>
</html>)";
```

### Error Handling

The stencil/mustache functions return `std::expected<std::string, glz::error_ctx>`. Common errors include:

- `glz::error_code::unknown_key` - Referenced field doesn't exist in struct
- `glz::error_code::unexpected_end` - Mismatched or missing closing tags
- `glz::error_code::syntax_error` - Malformed template syntax

> [!TIP]
>
> Use `glz::format_error` like the rest of Glaze to generate a nicely formatted error with context. Note that rather than passing the buffer into the formatter, pass in the layout/template string.

```cpp
std::string_view layout = R"({{bad_key}} {{last_name}} {{age}})";

person p{"Henry", "Foster", 34};
auto result = glz::stencil(layout, p);
expect(result.error());
auto error_msg = glz::format_error(result, layout);
```

In this example `error_msg` will look like:

```
1:10: unknown_key
   {{bad_key}} {{last_name}} {{age}}
            ^
```

## StencilCount

Glaze also provides `glz::stencilcount` for automatic numbering in documents.

> [!NOTE]
>
> To use `glz::stencilcount`, you must explicitly include its header:
>
> ```c++
> #include "glaze/stencil/stencilcount.hpp"
> ```

Example usage:
```cpp
std::string_view layout = R"(# About
## {{+}} {{first_name}} {{last_name}}
{{++}} {{first_name}} is {{age}} years old.

## {{+}} Hobbies
{{++}} Outdoor
{{+++}} Running
{{+++}} Hiking
)";

person p{"Henry", "Foster", 34};
auto result = glz::stencilcount(layout, p);
// Result:
// # About
// ## 1. Henry Foster
// 1.1 Henry is 34 years old.
// 
// ## 2. Hobbies
// 2.1 Outdoor
// 2.1.1 Running
// 2.1.2 Hiking
```

### StencilCount Syntax

- `{{+}}` - Major section number (1, 2, 3, ...)
- `{{++}}` - Sub-section number (1.1, 1.2, 2.1, ...)
- `{{+++}}` - Sub-sub-section number (1.1.1, 1.1.2, ...)
- And so on for deeper nesting levels

### Requirements

- Structs must be reflectable (using Glaze reflection) or be glaze objects
- Boolean fields are used for section conditions
- All field names in templates must exist in the struct