use alloc::string::{String, ToString};
use alloc::vec::Vec;
use crate::error::{Result, TemplateError};
use crate::parse::ListNode;
use crate::value::Value;
use crate::{FuncMap, MissingKey};
mod content;
mod context;
mod escape;
mod escapers;
mod lex;
mod primitives;
mod transition;
pub use content::{CSS, HTML, HTMLAttr, JS, JSStr, Srcset, URL};
use escape::EscapeSet;
#[cfg(feature = "std")]
type EscapeCache = std::sync::OnceLock<EscapeSet>;
#[cfg(not(feature = "std"))]
type EscapeCache = core::cell::OnceCell<EscapeSet>;
pub struct Template {
inner: crate::Template,
escaped: EscapeCache,
}
impl Template {
pub fn new(name: &str) -> Self {
Template {
inner: crate::Template::new(name),
escaped: EscapeCache::new(),
}
}
#[must_use]
pub fn max_range_iters(mut self, n: u64) -> Self {
self.inner = self.inner.max_range_iters(n);
self
}
#[must_use]
pub fn delims(mut self, left: &str, right: &str) -> Self {
self.inner = self.inner.delims(left, right);
self
}
#[must_use]
pub fn missing_key(mut self, mk: MissingKey) -> Self {
self.inner = self.inner.missing_key(mk);
self
}
#[must_use]
pub fn func(
mut self,
name: &str,
f: impl Fn(&[Value]) -> Result<Value> + Send + Sync + 'static,
) -> Self {
self.inner = self.inner.func(name, f);
self
}
#[must_use]
pub fn funcs(mut self, func_map: FuncMap) -> Self {
self.inner = self.inner.funcs(func_map);
self
}
pub fn parse(mut self, src: &str) -> Result<Self> {
self.check_can_parse()?;
self.inner = self.inner.parse(src)?;
Ok(self)
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn parse_files(mut self, filenames: &[&str]) -> Result<Self> {
self.check_can_parse()?;
self.inner = self.inner.parse_files(filenames)?;
Ok(self)
}
#[cfg(feature = "glob")]
#[cfg_attr(docsrs, doc(cfg(feature = "glob")))]
pub fn parse_glob(mut self, pattern: &str) -> Result<Self> {
self.check_can_parse()?;
self.inner = self.inner.parse_glob(pattern)?;
Ok(self)
}
pub fn add_parse_tree(mut self, name: &str, tree: ListNode) -> Result<Self> {
self.check_can_parse()?;
self.inner = self.inner.add_parse_tree(name, tree);
Ok(self)
}
fn check_can_parse(&self) -> Result<()> {
if self.escaped.get().is_some() {
return Err(TemplateError::Exec(
"html/template: cannot Parse after Execute".to_string(),
));
}
Ok(())
}
fn escape_set(&self) -> Result<&EscapeSet> {
if self.escaped.get().is_none() {
let set = escape::escape(&self.inner)?;
let _ = self.escaped.set(set);
}
match self.escaped.get() {
Some(set) => Ok(set),
None => Err(TemplateError::Exec(
"html/template: escaping did not complete".to_string(),
)),
}
}
pub fn execute_fmt<W: core::fmt::Write>(&self, writer: &mut W, data: &Value) -> Result<()> {
let set = self.escape_set()?;
self.inner
.execute_tree_fmt(writer, &set.entry, &set.templates, &set.funcs, data)
}
pub fn execute_to_string(&self, data: &Value) -> Result<String> {
let mut buf = String::new();
self.execute_fmt(&mut buf, data)?;
Ok(buf)
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn execute<W: std::io::Write>(&self, writer: &mut W, data: &Value) -> Result<()> {
let mut adapter = crate::IoAdapter::new(writer);
self.execute_fmt(&mut adapter, data)
.map_err(adapter.err_mapper())
}
pub fn execute_template_fmt<W: core::fmt::Write>(
&self,
writer: &mut W,
name: &str,
data: &Value,
) -> Result<()> {
let set = self.escape_set()?;
let tree = set
.templates
.get(name)
.ok_or_else(|| TemplateError::UndefinedTemplate(name.to_string()))?;
self.inner
.execute_tree_fmt(writer, tree, &set.templates, &set.funcs, data)
}
pub fn execute_template_to_string(&self, name: &str, data: &Value) -> Result<String> {
let mut buf = String::new();
self.execute_template_fmt(&mut buf, name, data)?;
Ok(buf)
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn execute_template<W: std::io::Write>(
&self,
writer: &mut W,
name: &str,
data: &Value,
) -> Result<()> {
let mut adapter = crate::IoAdapter::new(writer);
self.execute_template_fmt(&mut adapter, name, data)
.map_err(adapter.err_mapper())
}
pub fn name(&self) -> &str {
self.inner.name()
}
pub fn lookup(&self, name: &str) -> Option<&ListNode> {
self.inner.lookup(name)
}
pub fn templates(&self) -> Vec<&str> {
self.inner.templates()
}
pub fn defined_templates(&self) -> String {
self.inner.defined_templates()
}
}
impl Clone for Template {
fn clone(&self) -> Self {
Template {
inner: self.inner.clone(),
escaped: EscapeCache::new(),
}
}
}