Skip to content

Commit

Permalink
Link an example showing how to call async functions
Browse files Browse the repository at this point in the history
  • Loading branch information
mitsuhiko committed Jun 16, 2024
1 parent dceed4f commit ab41e94
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 1 deletion.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ the `cargo run` command. Alternatively you can do `cargo run -p example-name`.
* [eval-to-state](eval-to-state): Demonstrates what can be done with evaluating to state.
* [expr](expr): demonstrates the expression evaluation support.
* [filters](filters): Shows how to write and use custom filters and global functions.
* [function-using-async](function-using-async): Demonstrates how tokio handle's `block_on` can be used from within a function.
* [generate-yaml](generate-yaml): renders YAML files from Jinja templates.
* [hello](hello): minimal Hello World example.
* [inheritance](inheritance): demonstrates how to use template inheritance.
Expand Down
9 changes: 9 additions & 0 deletions examples/function-using-async/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "object-using-async"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
minijinja = { version = "2.0.2", path = "../../minijinja" }
tokio = { version = "1.37.0", features = ["macros", "rt", "rt-multi-thread"] }
8 changes: 8 additions & 0 deletions examples/function-using-async/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# function-using-async

Shows how a tokio handle's `block_on` method can be used to wait for async
operations from within a template in a callback function.

```console
$ cargo run
```
90 changes: 90 additions & 0 deletions examples/function-using-async/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use std::sync::Arc;

use minijinja::value::{Enumerator, Object, Value, ValueKind};
use minijinja::{context, Environment, Error, State};
use tokio::runtime::Handle;
use tokio::task::spawn_blocking;

/// Utility object to hold a reference to the runtime and the context.
#[derive(Debug)]
struct ContextWithRuntime {
rt: Handle,
ctx: Value,
}

impl Object for ContextWithRuntime {
fn get_value(self: &Arc<Self>, name: &Value) -> Option<Value> {
// $context is a reserved name that templates cannot resolve, but we can
// use to pluck out a reference to ourselves in functions get get the
// state passed.
if name.as_str() == Some("$context") {
return Some(Value::from_dyn_object(self.clone()));
}
self.ctx.get_item(name).ok().filter(|x| !x.is_undefined())
}

fn enumerate(self: &Arc<Self>) -> Enumerator {
if self.ctx.kind() == ValueKind::Map {
if let Ok(keys) = self.ctx.try_iter() {
return Enumerator::Values(keys.collect());
}
}
Enumerator::Empty
}
}

/// Given a context, wraps it so that the runtime is included.
fn capture_runtime_handle(ctx: Value) -> Value {
Value::from_object(ContextWithRuntime {
ctx,
rt: Handle::current(),
})
}

/// Utility function to retrieve the current runtime handle from the template state.
fn get_runtime_handle(state: &State) -> Handle {
let value = state.lookup("$context").unwrap();
value
.downcast_object_ref::<ContextWithRuntime>()
.unwrap()
.rt
.clone()
}

/// This is a function that would access a database etc.
async fn get_config(key: Arc<str>) -> Option<Value> {
// Imagine this goes to an actual database
match &key as &str {
"title" => Some(Value::from("My Title")),
_ => None,
}
}

/// Wrapper function that calls `get_config` from the context of a template.
fn get_config_template(state: &State, key: Arc<str>) -> Result<Value, Error> {
let rt = get_runtime_handle(state);
Ok(Value::from(rt.block_on(get_config(key))))
}

#[tokio::main]
async fn main() {
let mut env = Environment::new();
env.add_function("get_config", get_config_template);
env.add_template("hello", "title: {{ get_config(key) }}")
.unwrap();

// capture the runtime handle in the context
let ctx = capture_runtime_handle(context! {
key => Value::from("title"),
});

// then spawn template rendering in another thread.
let rv = spawn_blocking(move || {
let t = env.get_template("hello").unwrap();
t.render(ctx).unwrap()
})
.await
.unwrap();

println!("{}", rv);
}
2 changes: 1 addition & 1 deletion examples/object-using-async/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "object-using-async"
name = "function-using-async"
version = "0.1.0"
edition = "2021"
publish = false
Expand Down

0 comments on commit ab41e94

Please sign in to comment.