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}