macro_rules! tree { { trace = [ $($trace:tt)* ] rest = [[ < $name:ident $($rest:tt)* ]] } => { ... }; { trace = [ $($trace:tt)* ] rest = [[ < $token:tt $($rest:tt)* ]] } => { ... }; { trace = $trace:tt rest = [[ < ]] } => { ... }; { trace = [ $($trace:tt)* ] rest = [[ $token:tt $($rest:tt)* ]] } => { ... }; { trace = $trace:tt rest = [[ ]] } => { ... }; { trace = $trace:tt rest = [[ $($rest:tt)* ]] } => { ... }; ($($rest:tt)*) => { ... }; }
Expand description
This macro builds a [Document
] using nested syntax.
§Inline values using {...}
syntax
You can insert any [Render
] value into a document using {...}
syntax.
use render_tree::prelude::*;
let hello = "hello";
let world = format!("world");
let title = ". The answer is ";
let answer = 42;
let document = tree! {
{hello} {" "} {world} {". The answer is "} {answer}
};
assert_eq!(document.to_string()?, "hello world. The answer is 42");
Built-in types that implement render include:
- Anything that implements
Display
(including String, &str, the number types, etc.). The text value is inserted into the document. - Other [
Document
]s, which are concatenated onto the document. - A [
SomeValue
] adapter that takes anOption<impl Renderable>
and inserts its inner value if present. - An [
Empty
] value that adds nothing to the document.
§Inline Components
You can create components to encapsulate some logic:
use render_tree::prelude::*;
struct Header {
code: usize,
message: &'static str,
}
impl Render for Header {
fn render(self, document: Document) -> Document {
document.add(tree! {
{self.code} {": "} {self.message}
})
}
}
let code = 1;
let message = "Something went wrong";
let document = tree! {
<Header code={code} message={message}>
};
assert_eq!(document.to_string()?, "1: Something went wrong");
§Block Components
You can also build components that take a block that runs exactly
once (an FnOnce
).
#[macro_use]
extern crate render_tree;
use render_tree::prelude::*;
struct Message {
code: usize,
message: &'static str,
trailing: &'static str,
}
impl BlockComponent for Message {
fn append(
self,
block: impl FnOnce(Document) -> Document,
mut document: Document,
) -> Document {
document = document.add(tree! {
{self.code} {": "} {self.message} {" "}
});
document = block(document);
document = document.add(tree! {
{self.trailing}
});
document
}
}
let code = 1;
let message = "Something went wrong";
let document = tree! {
<Message code={code} message={message} trailing={" -- yikes!"} as {
{"!!! It's really quite bad !!!"}
}>
};
assert_eq!(document.to_string()?, "1: Something went wrong !!! It's really quite bad !!! -- yikes!");
§Iterators
Finally, you can create components that take a block and call the block multiple times (an iterator).
use render_tree::prelude::*;
use std::io;
pub struct UpcaseAll<Iterator: IntoIterator<Item = String>> {
pub items: Iterator,
}
impl<Iterator: IntoIterator<Item = String>> IterBlockComponent for UpcaseAll<Iterator> {
type Item = String;
fn append(
self,
mut block: impl FnMut(String, Document) -> Document,
mut document: Document,
) -> Document {
for item in self.items {
document = block(item.to_uppercase(), document);
}
document
}
}
let list = vec![format!("Hello"), format!("World")];
let document = tree! {
<UpcaseAll items={list} as |item| {
{"upcase:"} {item}
}>
};
assert_eq!(document.to_string()?, "upcase:HELLOupcase:WORLD");