render_tree

Macro tree

Source
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 an Option<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");