From 90a5a2e6b35e6cf3f595bcf8eb0a9fd67a3225e1 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 6 Nov 2024 23:35:34 +0100 Subject: [PATCH] Added an example for custom errors --- Cargo.lock | 7 +++ examples/README.md | 1 + examples/custom-error/Cargo.toml | 10 ++++ examples/custom-error/README.md | 8 ++++ examples/custom-error/src/main.rs | 77 +++++++++++++++++++++++++++++++ 5 files changed, 103 insertions(+) create mode 100644 examples/custom-error/Cargo.toml create mode 100644 examples/custom-error/README.md create mode 100644 examples/custom-error/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 889246f9..55028c0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -839,6 +839,13 @@ dependencies = [ "typenum", ] +[[package]] +name = "custom-error" +version = "0.1.0" +dependencies = [ + "minijinja", +] + [[package]] name = "custom-loader" version = "0.1.0" diff --git a/examples/README.md b/examples/README.md index b12f5832..5cf42b15 100644 --- a/examples/README.md +++ b/examples/README.md @@ -10,6 +10,7 @@ the `cargo run` command. Alternatively you can do `cargo run -p example-name`. * [autoreload](autoreload): shows how to use auto reloading. * [build-script](build-script): Demonstrates how to generate Rust code with MiniJinja in build scripts. * [call-block-function](call-block-function): Shows how to use the `{% call %}` block with a custom function. +* [custom-error](custom-error): shows how custom errors can be thrown and detected. * [custom-loader](custom-loader): shows how to load templates dynamically at runtime with a custom loader. * [debug](debug): contains an example showing the built-in `debug()` function. * [deserialize](deserialize): demonstrates how you can deserialize directly from a value. diff --git a/examples/custom-error/Cargo.toml b/examples/custom-error/Cargo.toml new file mode 100644 index 00000000..47cc25f8 --- /dev/null +++ b/examples/custom-error/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "custom-error" +version = "0.1.0" +edition = "2018" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +minijinja = { path = "../../minijinja" } diff --git a/examples/custom-error/README.md b/examples/custom-error/README.md new file mode 100644 index 00000000..4189581f --- /dev/null +++ b/examples/custom-error/README.md @@ -0,0 +1,8 @@ +# custom-error + +This example demonstrates how custom errors can be emitted in templates and +then detected later to customize the rendering of errors. + +```console +$ cargo run +``` diff --git a/examples/custom-error/src/main.rs b/examples/custom-error/src/main.rs new file mode 100644 index 00000000..922ee8c4 --- /dev/null +++ b/examples/custom-error/src/main.rs @@ -0,0 +1,77 @@ +use std::fmt; + +use minijinja::{context, Environment, Error, ErrorKind}; + +#[derive(Debug)] +struct UserError(String, usize); + +impl UserError { + fn code(&self) -> usize { + self.1 + } + + fn message(&self) -> &str { + &self.0 + } +} + +impl fmt::Display for UserError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "user error: {}", self.0) + } +} + +impl std::error::Error for UserError {} + +fn execute() -> Result<(), minijinja::Error> { + let mut env = Environment::new(); + env.set_debug(true); + env.add_function( + "trigger_user_error", + |s: String, i: usize| -> Result<(), Error> { + Err(Error::from(ErrorKind::InvalidOperation).with_source(UserError(s, i))) + }, + ); + env.add_template( + "include.txt", + "{{ trigger_user_error('This really should not happen', 42) }}!", + )?; + env.add_template( + "hello.txt", + r#" + first line + {% for item in seq %} + {% include "include.txt" %} + {% endfor %} + last line + "#, + )?; + let template = env.get_template("hello.txt").unwrap(); + let ctx = context! { + seq => vec![2, 4, 8], + }; + println!("{}", template.render(&ctx)?); + Ok(()) +} + +fn main() { + if let Err(err) = execute() { + eprintln!("template error: {err:#}"); + + let mut err = &err as &dyn std::error::Error; + while let Some(next_err) = err.source() { + if let Some(user_err) = next_err.downcast_ref::() { + eprintln!(); + eprintln!("caused by a well known error:"); + eprintln!(" message: {}", user_err.message()); + eprintln!(" code: {}", user_err.code()); + } else { + eprintln!(); + eprintln!("caused by: {next_err:#}"); + } + err = next_err; + } + + std::process::exit(1); + } +}