ascii_tree/
lib.rs

1//! Crate to write an ascii tree.
2//! 
3//! ```rust
4//! let l1 = Leaf(vec![String::from("line1"), String::from("line2"), String::from("line3"), String::from("line4")]);
5//! let l2 = Leaf(vec![String::from("only one line")]);
6//! let n1 = Node(String::from("node 1"), vec![l1.clone(), l2.clone()]);
7//! let n2 = Node(String::from("node 2"), vec![l2.clone(), l1.clone(), l2.clone()]);
8//! let n3 = Node(String::from("node 3"), vec![n1.clone(), l1.clone(), l2.clone()]);
9//! let n4 = Node(String::from("node 4"), vec![n1, n2, n3]);
10//! 
11//! let mut output = String::new();
12//! let _ = write_tree(&mut output, &n4);
13//! ```
14//! 
15//! The result would be:
16//! <pre>
17//! node 4
18//! ├─ node 1
19//! │  ├─ line1
20//! │  │  line2
21//! │  │  line3
22//! │  │  line4
23//! │  └─ only one line
24//! ├─ node 2
25//! │  ├─ only one line
26//! │  ├─ line1
27//! │  │  line2
28//! │  │  line3
29//! │  │  line4
30//! │  └─ only one line
31//! └─ node 3
32//!    ├─ node 1
33//!    │  ├─ line1
34//!    │  │  line2
35//!    │  │  line3
36//!    │  │  line4
37//!    │  └─ only one line
38//!    ├─ line1
39//!    │  line2
40//!    │  line3
41//!    │  line4
42//!    └─ only one line
43//! </pre>
44
45use std::fmt;
46use std::fmt::Write;
47
48#[derive(Clone)]
49pub enum Tree {
50    Node(String, Vec<Tree>),
51    Leaf(Vec<String>)
52}
53
54#[inline]
55/// writes a tree in an ascii tree to the writer
56///
57/// ```
58/// let mut output = String::new();
59/// write_tree(&mut output, &tree);
60///
61/// ```
62pub fn write_tree(f: &mut Write, tree: &Tree) -> fmt::Result { write_tree_element(f, tree, &vec![]) }
63
64fn write_tree_element(f: &mut Write, tree: &Tree, level: &Vec<usize>) -> fmt::Result {
65    use Tree::*;
66    const EMPTY: &str = "   ";
67    const EDGE: &str = " └─";
68    const PIPE: &str = " │ ";
69    const BRANCH: &str = " ├─";
70
71    let maxpos = level.len();
72    let mut second_line = String::new();
73    for (pos, l) in level.iter().enumerate() {
74        let last_row = pos == maxpos - 1;
75        if *l == 1 {
76            if !last_row { write!(f, "{}", EMPTY)? } else { write!(f, "{}", EDGE)? }
77            second_line.push_str(EMPTY);
78        } else {
79            if !last_row { write!(f, "{}", PIPE)? } else { write!(f, "{}", BRANCH)? }
80            second_line.push_str(PIPE);
81        }
82    }
83    match tree {
84        Node(title, children) => {
85            let mut d = children.len();
86            write!(f, " {}\n", title)?;
87            for s in children {
88                let mut lnext = level.clone();
89                lnext.push(d);
90                d -= 1;
91                write_tree_element(f, s, &lnext)?;
92            }
93        }
94        Leaf(lines) => {
95            for (i, s) in lines.iter().enumerate() {
96                match i {
97                    0 => writeln!(f, " {}", s)?,
98                    _ => writeln!(f, "{} {}", second_line, s)?
99                }
100            }
101        }
102    }
103    Ok(())
104}