Template syntax and rendering

Producing templates

emit templates are string literals with holes for properties between braces. This is an example of a template:

#![allow(unused)]
fn main() {
extern crate emit;
let user = "Rust";

emit::emit!("Hello, {user}");
}

The emit! and #[span] macros use the same syntax.

Properties within templates

Properties in templates appear within braces:

#![allow(unused)]
fn main() {
extern crate emit;
let user = "Rust";
emit::emit!("Hello, {user}");
}

Braces may be escaped by doubling them:

#![allow(unused)]
fn main() {
extern crate emit;
emit::emit!("Hello, {{user}}");
}

Properties use Rust's field value syntax, like you'd write when initializing struct fields. Usually they're a standalone identifer that will capture a property in scope with that name. Properties can also be given a value inline as an expression:

#![allow(unused)]
fn main() {
extern crate emit;
emit::emit!("Hello, {user: \"Rust\"}");
}

Properties may have attributes applied to them:

#![allow(unused)]
fn main() {
extern crate emit;
let user = "Rust";
emit::emit!("Hello, {#[cfg(enabled)] user}")
}

See Property attributes for details on attributes you can apply. Also see Property capturing for details on what types of properties can be captured.

Properties after templates

Complex property expressions are distracting within templates. Attributes and values for properties declared in the template can be written after it using the same field-value syntax:

#![allow(unused)]
fn main() {
extern crate emit;
emit::emit!(
    "Hello, {user}",
    #[cfg(enabled)]
    user: "Rust",
);
}

Properties outside of the template don't need a corresponding hole to be captured:

#![allow(unused)]
fn main() {
extern crate emit;
let user = "Rust";
emit::emit!(
    "Hello, {user}",
    lang: "en",
);
}

Properties before templates

Properties declared before the template aren't captured. They're called control parameters and are used to change the way events are constructed or emitted:

#![allow(unused)]
fn main() {
extern crate emit;
let user = "Rust";
emit::emit!(
    mdl: emit::path!("a::b::c"),
    "Hello, {user}",
)
}

The names and values of control parameters are different between emit! and #[span]. See Control parameters for details.

Rendering templates

Templates are tokenized into sequences of text and holes for property interpolation:

Hello, {user}

When tokenized, this template will look like:

#![allow(unused)]
fn main() {
extern crate emit;
use emit::template::Part;
let tokens = [
    Part::text("Hello, "),
    Part::hole("user"),
];
}

The template can then be fed a value for user and rendered:

#![allow(unused)]
fn main() {
extern crate emit;
use emit::{Template, template::Part};
let tokens = [Part::text("Hello, "), Part::hole("user")];
let template = Template::new_ref(&tokens);

let rendered = template.render(("user", "Rust")).to_string();
assert_eq!("Hello, Rust", rendered);
}

which will produce:

Hello, Rust

Any holes in the template that are rendered without a matching property will reproduce the hole:

#![allow(unused)]
fn main() {
extern crate emit;
use emit::{Template, template::Part};
let tokens = [Part::text("Hello, "), Part::hole("user")];
let template = Template::new_ref(&tokens);

let rendered = template.render(emit::Empty).to_string();
assert_eq!("Hello, {user}", rendered);
}
Hello, {user}

You can control how properties are rendered within templates by implementing the template::Write trait. emit_term uses this for example to render different property types in different colors.