Skip to content

Tree

Tree draws a hierarchy with guide lines — a directory listing, a dependency graph, a nested configuration. Each node has a label and any number of children.

use rich::{Console, Tree};

Building a tree

Tree::new(label) makes the root. add(label) appends a child and returns &mut Tree for it, so you descend by binding the result:

fn basic(console: &Console) {
    let mut tree = Tree::new("rs-rich-cli");

    // `add` returns the new child, so bind it to keep descending.
    let crates = tree.add("crates");
    crates.add("rich");
    let ext = crates.add("rich-ext");
    ext.add("src");
    ext.add("examples");
    crates.add("rich-cli");

    tree.add("docs").add("guide").add("core");
    tree.add("Cargo.toml");

    console.print(&tree);
}

A tree of the repository layout

  • Chaining add goes one level deeper each time: tree.add("docs").add("guide").add("core").
  • To add siblings, keep the parent's &mut Tree in a variable and call add on it repeatedly. Each child borrows the parent mutably, so finish with one branch before starting the next — or build recursively, as below.

Building from data

A recursive function that takes &mut Tree maps any nested structure onto a tree:

/// Anything hierarchical maps onto `add` with a little recursion.
enum Node {
    Dir(&'static str, Vec<Node>),
    File(&'static str),
}

fn attach(parent: &mut Tree, node: &Node) {
    match node {
        Node::File(name) => {
            parent.add(*name);
        }
        Node::Dir(name, children) => {
            let branch = parent.add(format!("{name}/"));
            for child in children {
                attach(branch, child);
            }
        }
    }
}

fn build_from_data(console: &Console) {
    let src = Node::Dir(
        "src",
        vec![
            Node::File("lib.rs"),
            Node::Dir("render", vec![Node::File("mod.rs"), Node::File("table.rs")]),
            Node::File("main.rs"),
        ],
    );
    let mut tree = Tree::new("project/");
    attach(&mut tree, &src);
    attach(&mut tree, &Node::File("README.md"));
    console.print(&tree);
}

A tree built recursively from nested data

Long labels

Labels wrap to the available width, and continuation lines keep the guides of their branch:

fn wrapping(console: &Console) {
    let mut tree = Tree::new("Release checklist");
    let checks = tree.add("Before tagging");
    checks.add("Run the full test suite on every supported platform, including the MSRV");
    checks.add("Regenerate golden fixtures");
    tree.add("After tagging: publish crates in dependency order, core first");
    console.print(&tree);
}

Wrapped labels in a 40-column tree

Trees inside other renderables

A Tree is a renderable like any other: put it in a panel, a table cell or a layout region.

fn in_a_panel(console: &Console) {
    let mut tree = Tree::new("services");
    let api = tree.add("api");
    api.add("v1");
    api.add("v2");
    tree.add("worker");

    let panel = Panel::new(Box::new(tree)).title("Deployment");
    console.print(&Constrain::new(Box::new(panel), Some(30)));
}

A tree inside a titled panel

A tree measures its widest label plus its guides (see measuring), so in a table cell or under Align::center it takes only the room it needs.

Not yet ported

This port covers upstream's default tree: thin guides, with labels given as markup strings, Text or renderables. Not yet available:

  • guide_style and the bold, double and ASCII guide sets.
  • style, hide_root and expanded=False.

See also