Skip to content

Commit

Permalink
Add with_template_fn to Renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
rossmacarthur committed Oct 22, 2023
1 parent 53671a1 commit 39a5542
Show file tree
Hide file tree
Showing 17 changed files with 503 additions and 286 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ or you don’t need to store the compiled template then you can also use the

```rust
let template = engine.compile("Hello {{ user.name }}!")?;
let result = template.render(upon::value!{ user: { name: "John Smith" }}).to_string()?;
let result = template.render(&engine, upon::value!{ user: { name: "John Smith" }}).to_string()?;
assert_eq!(result, "Hello John Smith!");
```

Expand Down
6 changes: 6 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,9 @@ cargo run --example <name>

- [render_to_writer](render_to_writer.rs): Demonstrates how to render directly
to a type implementing `std::io::Write` instead of to a string.

- [custom_template_store](custom_template_store.rs): Demonstrates how to
implement a custom template store. This can allow for things like
- relative template paths
- controlling template access
- lazy loading (if you use interior mutability)
40 changes: 40 additions & 0 deletions examples/custom_template_store.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use std::collections::HashMap;

fn main() -> upon::Result<()> {
let engine = upon::Engine::new();

// Construct our custom template store
let store = {
let mut s = HashMap::<&'static str, upon::Template<'static>>::new();
s.insert(
"index",
engine.compile(include_str!("templates/index.html"))?,
);
s.insert(
"header",
engine.compile(include_str!("templates/header.html"))?,
);
s.insert(
"footer",
engine.compile(include_str!("templates/footer.html"))?,
);
s
};

// Get the template from the store
let template = store.get("index").unwrap();

// Render the template using the provided data
let output = template
.render(&engine, upon::value! { title: "My Webpage!", year: 2022 })
.with_template_fn(|name| {
store
.get(name)
.ok_or_else(|| String::from("template not found"))
})
.to_string()?;

println!("{output}");

Ok(())
}
2 changes: 1 addition & 1 deletion examples/escape_html.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Unescaped: {{ value | unescape }}
value: "'<this>' & \"<that>\"",
};

println!("{}", template.render(ctx).to_string()?);
println!("{}", template.render(&engine, ctx).to_string()?);
Ok(())
}

Expand Down
6 changes: 4 additions & 2 deletions examples/quick.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
fn main() -> upon::Result<()> {
let out = upon::Engine::new()
let engine = upon::Engine::new();

let out = engine
.compile("Hello {{ name }}!")?
.render(upon::value! { name: "World" })
.render(&engine, upon::value! { name: "World" })
.to_string()?;

println!("{out}");
Expand Down
6 changes: 4 additions & 2 deletions examples/render_to_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ use std::io;
fn main() -> upon::Result<()> {
let mut stdout = io::BufWriter::new(io::stdout());

let engine = upon::Engine::new();

let ctx = upon::value! { user: { name: "John Smith" } };

upon::Engine::new()
engine
.compile("Hello {{ user.name }}!\n")?
.render(ctx)
.render(&engine, ctx)
.to_writer(&mut stdout)?;

Ok(())
Expand Down
6 changes: 4 additions & 2 deletions examples/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ struct User {
}

fn main() -> upon::Result<()> {
let engine = upon::Engine::new();

let ctx = Context {
user: User {
name: "John Smith".into(),
},
};

let output = upon::Engine::new()
let output = engine
.compile("Hello {{ user.name }}!")?
.render(ctx)
.render(&engine, ctx)
.to_string()?;

println!("{output}");
Expand Down
19 changes: 12 additions & 7 deletions examples/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ fn main() -> upon::Result<()> {
// .comment("<#", "#>") // excluding a delimiter essentially disables it
.build();

let out = upon::Engine::with_syntax(syntax)
let engine = upon::Engine::with_syntax(syntax);

let out = engine
.compile(
"
<%- if user.is_enabled %>
Expand All @@ -15,12 +17,15 @@ Hello <? user.name ?>!
<% endif -%>
",
)?
.render(upon::value! {
user: {
is_enabled: true,
name: "John Smith",
}
})
.render(
&engine,
upon::value! {
user: {
is_enabled: true,
name: "John Smith",
}
},
)
.to_string()?;

println!("{out}");
Expand Down
4 changes: 2 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ impl Error {
}

/// Attaches a template name to the error, if it is not already set.
pub(crate) fn with_template_name(mut self, name: &str) -> Self {
self.name.get_or_insert(name.into());
pub(crate) fn with_template_name(mut self, name: String) -> Self {
self.name.get_or_insert(name);
self
}

Expand Down
65 changes: 40 additions & 25 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
//! ```
//! # let engine = upon::Engine::new();
//! let template = engine.compile("Hello {{ user.name }}!")?;
//! let result = template.render(upon::value!{ user: { name: "John Smith" }}).to_string()?;
//! let result = template.render(&engine, upon::value!{ user: { name: "John Smith" }}).to_string()?;
//! assert_eq!(result, "Hello John Smith!");
//! # Ok::<(), upon::Error>(())
//! ```
Expand Down Expand Up @@ -240,8 +240,12 @@ pub enum ValueAccessOp {
}

/// A compiled template created using [`Engine::compile`].
pub struct Template<'engine, 'source> {
engine: &'engine Engine<'engine>,
///
/// For convenience this struct's lifetime is not tied to the lifetime of the
/// engine. However, it is considered a logic error to attempt to render this
/// template using a different engine than the one that created it. If that
/// happens the render call may panic or produce incorrect output.
pub struct Template<'source> {
template: program::Template<'source>,
}

Expand Down Expand Up @@ -386,11 +390,13 @@ impl<'engine> Engine<'engine> {
N: Into<Cow<'engine, str>>,
S: Into<Cow<'engine, str>>,
{
let name = name.into();
let source = source.into();
let template = compile::template(self, source).map_err(|e| e.with_template_name(&name))?;
self.templates.insert(name, template);
Ok(())
match compile::template(self, source.into()) {
Ok(template) => {
self.templates.insert(name.into(), template);
Ok(())
}
Err(err) => Err(err.with_template_name(name.into().into())),
}
}

/// Lookup a template by name.
Expand Down Expand Up @@ -435,12 +441,12 @@ impl<'engine> Engine<'engine> {
/// [`.add_template(..)`][Engine::add_template] here is that if the template
/// source is borrowed, it does not need to outlive the engine.
#[inline]
pub fn compile<'source>(&self, source: &'source str) -> Result<Template<'_, 'source>> {
let template = compile::template(self, Cow::Borrowed(source))?;
Ok(Template {
engine: self,
template,
})
pub fn compile<'source, S>(&self, source: S) -> Result<Template<'source>>
where
S: Into<Cow<'source, str>>,
{
let template = compile::template(self, source.into())?;
Ok(Template { template })
}
}

Expand Down Expand Up @@ -477,7 +483,7 @@ impl std::fmt::Debug for EngineBoxFn {
}
}

impl<'render> Template<'render, 'render> {
impl<'render> Template<'render> {
/// Render the template using the provided [`serde`] value.
///
/// The returned struct must be consumed using
Expand All @@ -486,11 +492,11 @@ impl<'render> Template<'render, 'render> {
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
#[inline]
pub fn render<S>(&self, ctx: S) -> Renderer<'_>
pub fn render<S>(&self, engine: &'render Engine<'render>, ctx: S) -> Renderer<'_>
where
S: serde::Serialize,
{
Renderer::with_serde(self.engine, &self.template, ctx)
Renderer::with_serde(engine, &self.template, None, ctx)
}

/// Render the template using the provided value.
Expand All @@ -499,8 +505,12 @@ impl<'render> Template<'render, 'render> {
/// [`.to_string()`][crate::Renderer::to_string] or
/// [`.to_writer(..)`][crate::Renderer::to_writer].
#[inline]
pub fn render_from(&self, ctx: &'render Value) -> Renderer<'_> {
Renderer::with_value(self.engine, &self.template, ctx)
pub fn render_from(
&self,
engine: &'render Engine<'render>,
ctx: &'render Value,
) -> Renderer<'_> {
Renderer::with_value(engine, &self.template, None, ctx)
}

/// Render the using the provided value function.
Expand All @@ -509,11 +519,11 @@ impl<'render> Template<'render, 'render> {
/// [`.to_string()`][crate::Renderer::to_string] or
/// [`.to_writer(..)`][crate::Renderer::to_writer].
#[inline]
pub fn render_from_fn<F>(&self, value_fn: F) -> Renderer<'_>
pub fn render_from_fn<F>(&self, engine: &'render Engine<'render>, value_fn: F) -> Renderer<'_>
where
F: Fn(&[ValueMember<'_>]) -> std::result::Result<Value, String> + 'render,
{
Renderer::with_value_fn(self.engine, &self.template, Box::new(value_fn))
Renderer::with_value_fn(engine, &self.template, None, Box::new(value_fn))
}

/// Returns the original template source.
Expand All @@ -523,7 +533,7 @@ impl<'render> Template<'render, 'render> {
}
}

impl std::fmt::Debug for Template<'_, '_> {
impl std::fmt::Debug for Template<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Template")
.field("engine", &(..))
Expand All @@ -545,7 +555,7 @@ impl<'render> TemplateRef<'render> {
where
S: serde::Serialize,
{
Renderer::with_serde(self.engine, self.template, ctx)
Renderer::with_serde(self.engine, self.template, Some(self.name), ctx)
}

/// Render the template using the provided value.
Expand All @@ -555,7 +565,7 @@ impl<'render> TemplateRef<'render> {
/// [`.to_writer(..)`][crate::Renderer::to_writer].
#[inline]
pub fn render_from(&self, ctx: &'render Value) -> Renderer<'render> {
Renderer::with_value(self.engine, self.template, ctx)
Renderer::with_value(self.engine, self.template, Some(self.name), ctx)
}

/// Render the using the provided value function.
Expand All @@ -568,7 +578,12 @@ impl<'render> TemplateRef<'render> {
where
F: Fn(&[ValueMember<'_>]) -> std::result::Result<Value, String> + 'render,
{
Renderer::with_value_fn(self.engine, self.template, Box::new(value_fn))
Renderer::with_value_fn(
self.engine,
self.template,
Some(self.name),
Box::new(value_fn),
)
}

/// Returns the original template source.
Expand Down
Loading

0 comments on commit 39a5542

Please sign in to comment.