bracket/registry.rs
1//! Primary entry point for compiling and rendering templates.
2use serde::Serialize;
3
4#[cfg(feature = "fs")]
5use std::ffi::OsStr;
6#[cfg(feature = "fs")]
7use std::path::Path;
8
9use crate::{
10 escape::{self, EscapeFn},
11 helper::{HandlerRegistry, HelperRegistry},
12 output::{Output, StringOutput},
13 parser::{Parser, ParserOptions},
14 render::CallSite,
15 template::{Template, Templates},
16 Error, Result,
17};
18
19/// Registry is the entry point for compiling and rendering templates.
20///
21/// A template name is always required for error messages.
22pub struct Registry<'reg> {
23 helpers: HelperRegistry<'reg>,
24 handlers: HandlerRegistry<'reg>,
25 templates: Templates,
26 escape: EscapeFn,
27 strict: bool,
28}
29
30impl<'reg> Registry<'reg> {
31 /// Create an empty registry.
32 pub fn new() -> Self {
33 Self {
34 helpers: HelperRegistry::new(),
35 handlers: Default::default(),
36 templates: Default::default(),
37 escape: Box::new(escape::html),
38 strict: false,
39 }
40 }
41
42 /// Set the strict mode.
43 pub fn set_strict(&mut self, strict: bool) {
44 self.strict = strict
45 }
46
47 /// Get the strict mode.
48 pub fn strict(&self) -> bool {
49 self.strict
50 }
51
52 /// Set the escape function for rendering.
53 pub fn set_escape(&mut self, escape: EscapeFn) {
54 self.escape = escape;
55 }
56
57 /// The escape function to use for rendering.
58 pub fn escape(&self) -> &EscapeFn {
59 &self.escape
60 }
61
62 /// Helper registry.
63 pub fn helpers(&self) -> &HelperRegistry<'reg> {
64 &self.helpers
65 }
66
67 /// Mutable reference to the helper registry.
68 pub fn helpers_mut(&mut self) -> &mut HelperRegistry<'reg> {
69 &mut self.helpers
70 }
71
72 /// Event handler registry.
73 pub fn handlers(&self) -> &HandlerRegistry<'reg> {
74 &self.handlers
75 }
76
77 /// Mutable reference to the event handler registry.
78 pub fn handlers_mut(&mut self) -> &mut HandlerRegistry<'reg> {
79 &mut self.handlers
80 }
81
82 /// Templates collection.
83 pub fn templates(&self) -> &Templates {
84 &self.templates
85 }
86
87 /// Mutable reference to the templates.
88 pub fn templates_mut(&mut self) -> &mut Templates {
89 &mut self.templates
90 }
91
92 /// Get a named template.
93 #[deprecated(since = "0.9.29", note = "Use get() instead.")]
94 pub fn get_template(&self, name: &str) -> Option<&Template> {
95 self.templates.get(name)
96 }
97
98 /// Get a named template.
99 pub fn get<S>(&self, name: S) -> Option<&Template> where S: AsRef<str> {
100 self.templates.get(name.as_ref())
101 }
102
103 /// Remove a named template.
104 pub fn remove<S>(&mut self, name: S) -> Option<Template> where S: AsRef<str> {
105 self.templates.remove(name.as_ref())
106 }
107
108 /// Insert a named string template.
109 pub fn insert<N, C>(&mut self, name: N, content: C) -> Result<()>
110 where
111 N: AsRef<str>,
112 C: AsRef<str>,
113 {
114 let name = name.as_ref().to_owned();
115 let template = self.compile(
116 content.as_ref().to_owned(),
117 ParserOptions::new(name.clone(), 0, 0),
118 )?;
119 self.templates.insert(name, template);
120 Ok(())
121 }
122
123 /// Add a named template from a file.
124 ///
125 /// Requires the `fs` feature.
126 #[cfg(feature = "fs")]
127 pub fn add<P>(&mut self, name: String, file: P) -> Result<()>
128 where
129 P: AsRef<Path>,
130 {
131 let file_name = file
132 .as_ref()
133 .to_string_lossy()
134 .into_owned()
135 .to_string();
136
137 let (_, content) = self.read(file)?;
138 let template =
139 self.compile(content, ParserOptions::new(file_name, 0, 0))?;
140 self.templates.insert(name, template);
141 Ok(())
142 }
143
144 /// Load a file and use the file path as the template name.
145 ///
146 /// Requires the `fs` feature.
147 #[cfg(feature = "fs")]
148 pub fn load<P: AsRef<Path>>(&mut self, file: P) -> Result<()> {
149 let file_name = file
150 .as_ref()
151 .to_string_lossy()
152 .into_owned()
153 .to_string();
154
155 let (name, content) = self.read(file)?;
156 let template =
157 self.compile(content, ParserOptions::new(file_name, 0, 0))?;
158 self.templates.insert(name, template);
159 Ok(())
160 }
161
162 /// Load all the files in a target directory that match the
163 /// given extension.
164 ///
165 /// The generated name is the file stem; ie, the name of the file
166 /// once the extension has been removed.
167 ///
168 /// Requires the `fs` feature.
169 #[cfg(feature = "fs")]
170 pub fn read_dir<P: AsRef<Path>>(
171 &mut self,
172 file: P,
173 extension: &str,
174 ) -> Result<()> {
175 let ext = OsStr::new(extension);
176 for entry in std::fs::read_dir(file.as_ref())? {
177 let entry = entry?;
178 let path = entry.path();
179 if path.is_file() {
180 if let Some(extension) = path.extension() {
181 if extension == ext {
182 let file_name = path
183 .to_string_lossy()
184 .into_owned()
185 .to_string();
186
187 let name = path
188 .file_stem()
189 .unwrap()
190 .to_string_lossy()
191 .to_owned()
192 .to_string();
193 let (_, content) = self.read(path)?;
194 let template = self.compile(
195 content,
196 ParserOptions::new(file_name, 0, 0),
197 )?;
198 self.templates.insert(name, template);
199 }
200 }
201 }
202 }
203 Ok(())
204 }
205
206 #[cfg(feature = "fs")]
207 fn read<P: AsRef<Path>>(
208 &self,
209 file: P,
210 ) -> std::io::Result<(String, String)> {
211 let path = file.as_ref();
212 let name = path.to_string_lossy().to_owned().to_string();
213 let content = std::fs::read_to_string(path)?;
214 Ok((name, content))
215 }
216
217 /// Compile a string to a template.
218 ///
219 /// To compile a template and add it to this registry use [insert()](Registry#method.insert),
220 /// [add()](Registry#method.add), [load()](Registry#method.load) or [read_dir()](Registry#method.read_dir).
221 pub fn compile<'a, S>(
222 &self,
223 template: S,
224 options: ParserOptions,
225 ) -> Result<Template>
226 where
227 S: AsRef<str>,
228 {
229 Ok(Template::compile(template.as_ref().to_owned(), options)?)
230 }
231
232 /// Compile a string to a template using the given name.
233 ///
234 /// This is a convenience function for calling [compile()](Registry#method.compile)
235 /// using parser options with the given name.
236 pub fn parse<'a, S>(&self, name: &str, template: S) -> Result<Template>
237 where
238 S: AsRef<str>,
239 {
240 self.compile(template, ParserOptions::new(name.to_string(), 0, 0))
241 }
242
243 /// Lint a template.
244 pub fn lint<S>(&self, name: &str, template: S) -> Result<Vec<Error>>
245 where
246 S: AsRef<str>,
247 {
248 let mut errors: Vec<Error> = Vec::new();
249 let mut parser = Parser::new(
250 template.as_ref(),
251 ParserOptions::new(name.to_string(), 0, 0),
252 );
253 parser.set_errors(&mut errors);
254 for _ in parser {}
255 Ok(errors)
256 }
257
258 /// Render a template without registering it and return
259 /// the result as a string.
260 ///
261 /// This function buffers the template nodes before rendering.
262 pub fn once<T, S>(&self, name: &str, source: S, data: &T) -> Result<String>
263 where
264 T: Serialize,
265 S: AsRef<str>,
266 {
267 let mut writer = StringOutput::new();
268 let template = self.compile(
269 source.as_ref(),
270 ParserOptions::new(name.to_string(), 0, 0),
271 )?;
272 template.render(self, name, data, &mut writer, Default::default())?;
273 Ok(writer.into())
274 }
275
276 /// Render a template without registering it and return
277 /// the result as a string using an existing call stack.
278 ///
279 /// This function buffers the template nodes before rendering.
280 ///
281 /// Use this function if you need to render a string inside a
282 /// helper definition but want to respect the call stack
283 /// of the existing render, for example:
284 ///
285 /// ```ignore
286 /// let result = rc
287 /// .registry()
288 /// .once_stack(template_path, &content, rc.data(), rc.stack().clone())
289 /// .map_err(|e| {
290 /// HelperError::new(e.to_string())
291 /// })?;
292 /// rc.write(&result)?;
293 /// ```
294 pub(crate) fn once_stack<T, S>(
295 &self,
296 name: &str,
297 source: S,
298 data: &T,
299 stack: Vec<CallSite>,
300 ) -> Result<String>
301 where
302 T: Serialize,
303 S: AsRef<str>,
304 {
305 let mut writer = StringOutput::new();
306 let template = self.compile(
307 source.as_ref(),
308 ParserOptions::new(name.to_string(), 0, 0),
309 )?;
310 template.render(self, name, data, &mut writer, stack)?;
311 Ok(writer.into())
312 }
313
314 /*
315
316 /// Stream a dynamic template and buffer the result to a string.
317 ///
318 /// Requires the `stream` feature.
319 #[cfg(feature = "stream")]
320 pub fn stream<T>(
321 &self,
322 name: &str,
323 source: &str,
324 data: &T,
325 ) -> Result<String>
326 where
327 T: Serialize,
328 {
329 let mut writer = StringOutput::new();
330 let options = ParserOptions::new(name.to_string());
331 self.stream_to_write(name, source, data, &mut writer, options)?;
332 Ok(writer.into())
333 }
334
335 /// Stream a dynamic template to a writer.
336 ///
337 /// Requires the `stream` feature.
338 #[cfg(feature = "stream")]
339 pub fn stream_to_write<T>(
340 &self,
341 name: &str,
342 source: &str,
343 data: &T,
344 writer: &mut impl Output,
345 options: ParserOptions,
346 ) -> Result<()>
347 where
348 T: Serialize,
349 {
350 let mut buffer: Vec<Node<'_>> = Vec::new();
351 let mut rc = Render::new(
352 self.strict(),
353 self.escape(),
354 self.helpers(),
355 self.templates(),
356 source,
357 data,
358 Box::new(writer),
359 )?;
360
361 // FIXME: implement this, currently not working as we store the
362 // FIXME: next and previous nodes in the renderer which means
363 // FIXME: node is not living long enough for the renderer to
364 // FIXME: do it's job.
365 let parser = Parser::new(source, options);
366 let hint: Option<TrimHint> = Default::default();
367 for node in parser {
368 let node = node?;
369 //let node = buffer.last().unwrap();
370 for event in node.iter().trim(hint) {
371 println!("{:#?}", event.node);
372 //rc.render_node(event.node, event.trim)?;
373 }
374 //buffer.push(node);
375 }
376
377 drop(buffer);
378
379 Ok(())
380 }
381 */
382
383 /// Render a named template and buffer the result to a string.
384 ///
385 /// The named template must exist in the templates collection.
386 pub fn render<T>(&self, name: &str, data: &T) -> Result<String>
387 where
388 T: Serialize,
389 {
390 let mut writer = StringOutput::new();
391 self.render_to_write(name, data, &mut writer)?;
392 Ok(writer.into())
393 }
394
395 /// Render a compiled template without registering it and
396 /// buffer the result to a string.
397 pub fn render_template<'a, T>(
398 &self,
399 name: &str,
400 template: &Template,
401 data: &T,
402 ) -> Result<String>
403 where
404 T: Serialize,
405 {
406 let mut writer = StringOutput::new();
407 template.render(self, name, data, &mut writer, Default::default())?;
408 Ok(writer.into())
409 }
410
411 /// Render a named template to a writer.
412 ///
413 /// The named template must exist in the templates collection.
414 pub fn render_to_write<T>(
415 &self,
416 name: &str,
417 data: &T,
418 writer: &mut impl Output,
419 ) -> Result<()>
420 where
421 T: Serialize,
422 {
423 let tpl = self
424 .templates
425 .get(name)
426 .ok_or_else(|| Error::TemplateNotFound(name.to_string()))?;
427 tpl.render(self, name, data, writer, Default::default())?;
428
429 Ok(())
430 }
431}