An open API service indexing awesome lists of open source software.

https://github.com/fiji-flo/gtmpl-rust

golang text/template for rust
https://github.com/fiji-flo/gtmpl-rust

golang-template rust template-engine

Last synced: 3 months ago
JSON representation

golang text/template for rust

Awesome Lists containing this project

README

          

# gtmpl-rust – Golang Templates for Rust

[![Latest Version]][crates.io]

[Latest Version]: https://img.shields.io/crates/v/gtmpl.svg
[crates.io]: https://crates.io/crates/gtmpl

---

[gtmpl-rust] provides the [Golang text/template] engine for Rust. This enables
seamless integration of Rust application into the world of devops tools around
[kubernetes], [docker] and whatnot.

## Getting Started

Add the following dependency to your Cargo manifest…
```toml
[dependencies]
gtmpl = "0.7"
```

and look at the docs:
* [gtmpl at crates.io](https://crates.io/crates/gtmpl)
* [gtmpl documentation](https://docs.rs/crate/gtmpl)
* [golang documentation](https://golang.org/pkg/text/template/)

It's not perfect, yet. Help and feedback is more than welcome.

## Some Examples

Basic template:
```rust
use gtmpl;

fn main() {
let output = gtmpl::template("Finally! Some {{ . }} for Rust", "gtmpl");
assert_eq!(&output.unwrap(), "Finally! Some gtmpl for Rust");
}
```

Adding custom functions:
```rust
use gtmpl_value::Function;
use gtmpl::{FuncError, gtmpl_fn, template, Value};

fn main() {
gtmpl_fn!(
fn add(a: u64, b: u64) -> Result {
Ok(a + b)
});
let equal = template(r#"{{ call . 1 2 }}"#, Value::Function(Function { f: add }));
assert_eq!(&equal.unwrap(), "3");
}
```

Passing a struct as context:
```rust
use gtmpl_derive::Gtmpl;

#[derive(Gtmpl)]
struct Foo {
bar: u8
}

fn main() {
let foo = Foo { bar: 42 };
let output = gtmpl::template("The answer is: {{ .bar }}", foo);
assert_eq!(&output.unwrap(), "The answer is: 42");
}
```

Invoking a *method* on a context:
```rust

use gtmpl_derive::Gtmpl;
use gtmpl::{Func, FuncError, Value};

fn plus_one(args: &[Value]) -> Result {
if let Value::Object(ref o) = &args[0] {
if let Some(Value::Number(ref n)) = o.get("num") {
if let Some(i) = n.as_i64() {
return Ok((i +1).into())
}
}
}
Err(anyhow!("integer required, got: {:?}", args))
}

#[derive(Gtmpl)]
struct AddMe {
num: u8,
plus_one: Func
}

fn main() {
let add_me = AddMe { num: 42, plus_one };
let output = gtmpl::template("The answer is: {{ .plus_one }}", add_me);
assert_eq!(&output.unwrap(), "The answer is: 43");
}
```

## Current Limitations

This is work in progress. Currently the following features are not supported:

* complex numbers
* the following functions have not been implemented:
* `html`, `js`
* `printf` is not yet fully stable, but should support all *sane* input

## Enhancements

Even though it was never intended to extend the syntax of Golang text/template
there might be some convenient additions:

### Dynamic Template

Enable `gtmpl_dynamic_template` in your `Cargo.toml`:
```toml
[dependencies.gtmpl]
version = "0.7"
features = ["gtmpl_dynamic_template"]

```

Now you can have dynamic template names for the `template` action.

#### Example

```rust
use gtmpl::{Context, Template};

fn main() {
let mut template = Template::default();
template
.parse(
r#"
{{- define "tmpl1"}} some {{ end -}}
{{- define "tmpl2"}} some other {{ end -}}
there is {{- template (.) -}} template
"#,
)
.unwrap();

let context = Context::from("tmpl2");

let output = template.render(&context);
assert_eq!(output.unwrap(), "there is some other template".to_string());
}
```

The following syntax is used:
```
{{template (pipeline)}}
The template with the name evaluated from the pipeline (parenthesized) is
executed with nil data.

{{template (pipeline) pipeline}}
The template with the name evaluated from the first pipeline (parenthesized)
is executed with dot set to the value of the second pipeline.
```

## Context

We use [gtmpl_value]'s Value as internal data type. [gtmpl_derive] provides a
handy `derive` macro to generate the `From` implementation for `Value`.

See:

* [gtmpl_value at crates.io](https://crates.io/crate/gtmpl_value)
* [gtmpl_value documentation](https://docs.rs/crate/gtmpl_value)
* [gtmpl_derive at crates.io](https://crates.io/crate/gtmpl_derive)
* [gtmpl_derive documentation](https://docs.rs/crate/gtmpl_derive)

## Why do we need this?

Why? Dear god, why? I can already imagine the question coming up why anyone would
ever do this. I wasn't a big fan of Golang templates when i first had to write
some custom formatting strings for **docker**. Learning a new template language
usually isn't something one is looking forward to. Most people avoid it
completely. However, it's really useful for automation if you're looking for
something more lightweight than a full blown DSL.

The main motivation for this is to make it easier to write devops tools in Rust
that feel native. [docker] and [helm] ([kubernetes]) use golang templates and
it feels more native if tooling around them uses the same.

[gtmpl-rust]: https://github.com/fiji-flo/gtmpl-rust
[Golang text/template]: https://golang.org/pkg/text/template/
[kubernetes]: https://kubernetes.io
[helm]: https://github.com/kubernetes/helm/blob/master/docs/chart_best_practices/templates.md
[docker]: https://docker.com
[gtmpl_value]: https://github.com/fiji-flo/gtmpl_value
[gtmpl_derive]: https://github.com/fiji-flo/gtmpl_derive