Skip to content

Commit

Permalink
Tidy up documentation and examples
Browse files Browse the repository at this point in the history
  • Loading branch information
rossmacarthur committed Oct 28, 2023
1 parent 39a5542 commit ce1523c
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 78 deletions.
110 changes: 91 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ configurable delimiters.
- [Getting started](#getting-started)
- [Further reading](#further-reading)
- [Features](#features)
- [Examples](#examples)
- [Nested templates](#nested-templates)
- [Render to writer](#render-to-writer)
- [Borrowed templates with short lifetimes](#borrowed-templates-with-short-lifetimes)
- [Custom template store and function](#custom-template-store-and-function)
- [Benchmarks](#benchmarks)
- [License](#license)

Expand Down Expand Up @@ -91,22 +96,14 @@ engine.add_template("hello", "Hello {{ user.name }}!")?;
```

Finally, the template is rendered by fetching it using
[`get_template`][get_template] and calling
[`render`][render].
`template(..)`, calling
`render(..)` and rendering to a string.

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

If the lifetime of the template source is shorter than the engine lifetime
or you don’t need to store the compiled template then you can also use the
[`compile`][compile] function to return the template directly.

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

Expand All @@ -115,8 +112,9 @@ assert_eq!(result, "Hello John Smith!");
- The [`syntax`][syntax] module documentation outlines the template syntax.
- The [`filters`][filters] module documentation describes filters and how they work.
- The [`fmt`][fmt] module documentation contains information on value formatters.
- The [`examples/`](https://github.com/rossmacarthur/upon/tree/trunk/examples) directory in the repository contains concrete
code examples.
- In addition to the examples in the current document, the
[`examples/`](https://github.com/rossmacarthur/upon/tree/trunk/examples) directory in the repository constains some more
concrete code examples.

## Features

Expand Down Expand Up @@ -147,6 +145,83 @@ like. For example to use **`serde`** but disable **`filters`** and
upon = { version = "...", default-features = false, features = ["serde"] }
```

## Examples

### Nested templates

You can include other templates by name using `{% include .. %}`.

```rust
let mut engine = upon::Engine::new();
engine.add_template("hello", "Hello {{ user.name }}!")?;
engine.add_template("goodbye", "Goodbye {{ user.name }}!")?;
engine.add_template("nested", "{% include \"hello\" %}\n{% include \"goodbye\" %}")?;

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

### Render to writer

Instead of rendering to a string it is possible to render the template to
any [`std::io::Write`][stdiowrite] implementor using
`to_writer(..)`.

```rust
use std::io;

let mut engine = upon::Engine::new();
engine.add_template("hello", "Hello {{ user.name }}!")?;

let mut stdout = io::BufWriter::new(io::stdout());
engine
.template("hello")
.render(upon::value!{ user: { name: "John Smith" }})
.to_writer(&mut stdout)?;
// Prints: Hello John Smith!
```

### Borrowed templates with short lifetimes

If the lifetime of the template source is shorter than the engine lifetime
or you don’t need to store the compiled template then you can also use the
`compile(..)` function to return the template directly.

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

### Custom template store and function

The `compile(..)` function can also be used in
conjunction with a custom template store which can allow for more advanced
use cases. For example: relative template paths or controlling template
access.

```rust
let mut store = std::collections::HashMap::<&str, upon::Template>::new();
store.insert("hello", engine.compile("Hello {{ user.name }}!")?);
store.insert("goodbye", engine.compile("Goodbye {{ user.name }}!")?);
store.insert("nested", engine.compile("{% include \"hello\" %}\n{% include \"goodbye\" %}")?);

let result = store.get("nested")
.unwrap()
.render(&engine, upon::value!{ user: { name: "John Smith" }})
.with_template_fn(|name| {
store
.get(name)
.ok_or_else(|| String::from("template not found"))
})
.to_string()?;
assert_eq!(result, "Hello John Smith!\nGoodbye John Smith!");
```

[Handlebars]: https://crates.io/crates/handlebars
[Tera]: https://crates.io/crates/tera
[TinyTemplate]: https://crates.io/crates/tinytemplate
Expand Down Expand Up @@ -194,14 +269,11 @@ at your option.


[add_template]: https://docs.rs/upon/latest/upon/struct.Engine.html#method.add_template
[compile]: https://docs.rs/upon/latest/upon/struct.Engine.html#method.compile
[engine]: https://docs.rs/upon/latest/upon/struct.Engine.html
[engineadd_filter]: https://docs.rs/upon/latest/upon/struct.Engine.html#method.add_filter
[engineadd_formatter]: https://docs.rs/upon/latest/upon/struct.Engine.html#method.add_formatter
[filters]: https://docs.rs/upon/latest/upon/filters/index.html
[fmt]: https://docs.rs/upon/latest/upon/fmt/index.html
[get_template]: https://docs.rs/upon/latest/upon/struct.Engine.html#method.get_template
[render]: https://docs.rs/upon/latest/upon/struct.TemplateRef.html#method.render
[render_from]: https://docs.rs/upon/latest/upon/struct.TemplateRef.html#method.render_from
[serde]: https://crates.io/crates/serde
[stdiowrite]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
Expand Down
8 changes: 1 addition & 7 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@ Examples can be run from anywhere in the repo using the following.
cargo run --example <name>
```

- [quick](quick.rs): Demonstrates compiling and rendering a template in one line
of code.

- [serde](serde.rs): Demonstrates rendering a simple template using a custom
global context that implements `serde::Serialize`.

- [syntax](syntax.rs): Demonstrates how to configure the template engine with
- [custom_syntax](custom_syntax.rs): Demonstrates how to configure the template engine with
custom syntax delimiters.

- [escape_html](escape_html.rs): Demonstrates how to configure a _value
Expand All @@ -29,9 +26,6 @@ cargo run --example <name>
file loader. Files are are loaded at _runtime_ from the `templates/` directory
and added to the engine.

- [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
Expand Down
File renamed without changes.
12 changes: 0 additions & 12 deletions examples/quick.rs

This file was deleted.

16 changes: 0 additions & 16 deletions examples/render_to_writer.rs

This file was deleted.

7 changes: 4 additions & 3 deletions src/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
//! All formatter functions must have the following signature.
//!
//! ```text
//! Fn(&mut Formatter<'_>, &Value) -> Result;
//! use upon::{Value, fmt};
//! Fn(&mut fmt::Formatter<'_>, &Value) -> fmt::Result;
//! ```
//!
//! Since [`Error`] implements `From<String>` and `From<&str>` it is possible
Expand Down Expand Up @@ -54,8 +55,8 @@
//! ### Error on [`Value::None`]
//!
//! The [`default`] value formatter formats [`Value::None`] as an empty string.
//! This example demonstrates how you can configure a default formatter to
//! instead error.
//! This example demonstrates how you can configure a default formatter to error
//! instead.
//!
//! ```
//! use std::fmt::Write;
Expand Down
112 changes: 94 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,26 +78,16 @@
//! ```
//!
//! Finally, the template is rendered by fetching it using
//! [`get_template`][Engine::get_template] and calling
//! [`render`][TemplateRef::render].
//! [`template(..)`][Engine::template], calling
//! [`render(..)`][TemplateRef::render] and rendering to a string.
//!
//! ```
//! # let mut engine = upon::Engine::new();
//! # engine.add_template("hello", "Hello {{ user.name }}!")?;
//! let template = engine.get_template("hello").unwrap();
//! let result = template.render(upon::value!{ user: { name: "John Smith" }}).to_string()?;
//! assert_eq!(result, "Hello John Smith!");
//! # Ok::<(), upon::Error>(())
//! ```
//!
//! If the lifetime of the template source is shorter than the engine lifetime
//! or you don't need to store the compiled template then you can also use the
//! [`compile`][Engine::compile] function to return the template directly.
//!
//! ```
//! # let engine = upon::Engine::new();
//! let template = engine.compile("Hello {{ user.name }}!")?;
//! let result = template.render(&engine, upon::value!{ user: { name: "John Smith" }}).to_string()?;
//! let result = engine
//! .template("hello")
//! .render(upon::value!{ user: { name: "John Smith" }})
//! .to_string()?;
//! assert_eq!(result, "Hello John Smith!");
//! # Ok::<(), upon::Error>(())
//! ```
Expand All @@ -107,8 +97,9 @@
//! - The [`syntax`] module documentation outlines the template syntax.
//! - The [`filters`] module documentation describes filters and how they work.
//! - The [`fmt`] module documentation contains information on value formatters.
//! - The [`examples/`][examples] directory in the repository contains concrete
//! code examples.
//! - In addition to the examples in the current document, the
//! [`examples/`][examples] directory in the repository constains some more
//! concrete code examples.
//!
//! [examples]: https://github.com/rossmacarthur/upon/tree/trunk/examples
//!
Expand Down Expand Up @@ -141,6 +132,89 @@
//! [dependencies]
//! upon = { version = "...", default-features = false, features = ["serde"] }
//! ```
//!
//! # Examples
//!
//! ## Nested templates
//!
//! You can include other templates by name using `{% include .. %}`.
//!
//! ```
//! let mut engine = upon::Engine::new();
//! engine.add_template("hello", "Hello {{ user.name }}!")?;
//! engine.add_template("goodbye", "Goodbye {{ user.name }}!")?;
//! engine.add_template("nested", "{% include \"hello\" %}\n{% include \"goodbye\" %}")?;
//!
//! let result = engine.template("nested")
//! .render(upon::value!{ user: { name: "John Smith" }})
//! .to_string()?;
//! assert_eq!(result, "Hello John Smith!\nGoodbye John Smith!");
//! # Ok::<(), upon::Error>(())
//! ```
//!
//! ## Render to writer
//!
//! Instead of rendering to a string it is possible to render the template to
//! any [`std::io::Write`] implementor using
//! [`to_writer(..)`][crate::Renderer::to_writer].
//!
//! ```
//! use std::io;
//!
//! let mut engine = upon::Engine::new();
//! engine.add_template("hello", "Hello {{ user.name }}!")?;
//!
//! let mut stdout = io::BufWriter::new(io::stdout());
//! engine
//! .template("hello")
//! .render(upon::value!{ user: { name: "John Smith" }})
//! .to_writer(&mut stdout)?;
//! // Prints: Hello John Smith!
//! # Ok::<(), upon::Error>(())
//! ```
//!
//! ## Borrowed templates with short lifetimes
//!
//! If the lifetime of the template source is shorter than the engine lifetime
//! or you don't need to store the compiled template then you can also use the
//! [`compile(..)`][Engine::compile] function to return the template directly.
//!
//! ```
//! # let engine = upon::Engine::new();
//! let template = engine.compile("Hello {{ user.name }}!")?;
//! let result = template
//! .render(&engine, upon::value!{ user: { name: "John Smith" }})
//! .to_string()?;
//! assert_eq!(result, "Hello John Smith!");
//! # Ok::<(), upon::Error>(())
//! ```
//!
//! ## Custom template store and function
//!
//! The [`compile(..)`][Engine::compile] function can also be used in
//! conjunction with a custom template store which can allow for more advanced
//! use cases. For example: relative template paths or controlling template
//! access.
//!
//! ```
//! # let engine = upon::Engine::new();
//! let mut store = std::collections::HashMap::<&str, upon::Template>::new();
//! store.insert("hello", engine.compile("Hello {{ user.name }}!")?);
//! store.insert("goodbye", engine.compile("Goodbye {{ user.name }}!")?);
//! store.insert("nested", engine.compile("{% include \"hello\" %}\n{% include \"goodbye\" %}")?);
//!
//! let result = store.get("nested")
//! .unwrap()
//! .render(&engine, upon::value!{ user: { name: "John Smith" }})
//! .with_template_fn(|name| {
//! store
//! .get(name)
//! .ok_or_else(|| String::from("template not found"))
//! })
//! .to_string()?;
//! assert_eq!(result, "Hello John Smith!\nGoodbye John Smith!");
//! # Ok::<(), upon::Error>(())
//! ```

#![deny(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_cfg))]
Expand Down Expand Up @@ -215,7 +289,9 @@ type ValueFn<'a> = dyn Fn(&[ValueMember]) -> std::result::Result<Value, String>
/// [`render_from_fn`][Template::render_from_fn].
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ValueMember<'a> {
/// The type of member access (direct or optional).
pub op: ValueAccessOp,
/// The index or key being accessed.
pub access: ValueAccess<'a>,
}

Expand Down
1 change: 0 additions & 1 deletion src/render/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use crate::types::program::{Instr, Template};
use crate::value::ValueCow;
use crate::{EngineBoxFn, Error, Result};

/// A renderer that interprets a compiled [`Template`].
#[cfg_attr(internal_debug, derive(Debug))]
pub struct RendererImpl<'render, 'stack> {
pub(crate) inner: RendererInner<'render>,
Expand Down
Loading

0 comments on commit ce1523c

Please sign in to comment.