diff --git a/examples/README.md b/examples/README.md index 9e99cab6..ac0be27d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -34,6 +34,7 @@ the `cargo run` command. Alternatively you can do `cargo run -p example-name`. * [render-macro](render-macro): minimal Hello World example using the `render!` macro. * [render-template](render-template): CLI app that renders templates from string. * [render-value](render-value): Demonstrates how `Value` can be passed as `Serialize` as context. +* [self-referential-context](self-referential-context): Shows a helper that allows self-referential contexts. * [stack-ref](stack-ref): Shows how the `minijinja-stack-ref` crate can be used to reference stack values. * [value-tracking](value-tracking): Shows how you can track values that are referenced at runtime. * [wasm-yew](wasm-yew): Shows how to use MiniJinja with WASM in the browser. diff --git a/examples/self-referential-context/Cargo.toml b/examples/self-referential-context/Cargo.toml new file mode 100644 index 00000000..b0a05e22 --- /dev/null +++ b/examples/self-referential-context/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "self-referential-context" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +minijinja = { path = "../../minijinja" } diff --git a/examples/self-referential-context/README.md b/examples/self-referential-context/README.md new file mode 100644 index 00000000..32792723 --- /dev/null +++ b/examples/self-referential-context/README.md @@ -0,0 +1,10 @@ +# self-referential-context + +A context wrapper that acts self-referential. This allows variables passed to +`render` to be also reachable via a `CONTEXT` alias. In short: a template +can render a variable `name` via `{{ name }}` or also `{{ CONTEXT.name }}`. +This would permit things such as `{{ CONTEXT|tojson }}`. + +```console +$ cargo run +``` diff --git a/examples/self-referential-context/src/main.rs b/examples/self-referential-context/src/main.rs new file mode 100644 index 00000000..b9670819 --- /dev/null +++ b/examples/self-referential-context/src/main.rs @@ -0,0 +1,49 @@ +use std::sync::Arc; + +use minijinja::value::{StructObject, Value, ValueKind}; +use minijinja::{context, Environment}; + +struct SelfReferentialContext { + ctx: Value, +} + +impl StructObject for SelfReferentialContext { + fn get_field(&self, name: &str) -> Option { + if name == "CONTEXT" { + return Some(self.ctx.clone()); + } + self.ctx.get_attr(name).ok().filter(|x| !x.is_undefined()) + } + + fn fields(&self) -> Vec> { + if self.ctx.kind() == ValueKind::Map { + if let Ok(keys) = self.ctx.try_iter() { + return keys.filter_map(|x| Arc::::try_from(x).ok()).collect(); + } + } + Vec::new() + } +} + +pub fn make_self_referential(ctx: Value) -> Value { + Value::from_struct_object(SelfReferentialContext { ctx }) +} + +static TEMPLATE: &str = r#" +name: {{ name }} +CONTEXT.name: {{ CONTEXT.name }} +CONTEXT.CONTEXT is undefined: {{ CONTEXT.CONTEXT is undefined }} +CONTEXT: {{ CONTEXT }} +"#; + +fn main() { + let env = Environment::new(); + let template = env.template_from_str(TEMPLATE).unwrap(); + + let ctx = make_self_referential(context! { + name => "John", + other_value => 42, + }); + + println!("{}", template.render(ctx).unwrap()); +}