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
- Host: GitHub
- URL: https://github.com/fiji-flo/gtmpl-rust
- Owner: fiji-flo
- License: mit
- Created: 2017-04-20T07:26:53.000Z (about 9 years ago)
- Default Branch: master
- Last Pushed: 2022-03-30T18:06:34.000Z (over 4 years ago)
- Last Synced: 2026-03-12T23:26:45.621Z (4 months ago)
- Topics: golang-template, rust, template-engine
- Language: Rust
- Homepage:
- Size: 232 KB
- Stars: 90
- Watchers: 2
- Forks: 11
- Open Issues: 7
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGES.md
- License: LICENSE
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