bidir_termtree/
lib.rs

1#![allow(clippy::branches_sharing_code)]
2
3#[cfg(test)]
4mod tests;
5
6use std::collections::VecDeque;
7use std::fmt::{self, Display};
8use std::marker::PhantomData;
9use std::rc::Rc;
10use std::fmt::Write;
11
12pub trait Direction {
13    
14}
15
16#[derive(Clone, Copy)]
17pub struct Up;
18
19#[derive(Clone, Copy)]
20pub struct Down;
21
22impl Direction for Up {
23    
24}
25impl Direction for Down {
26    
27}
28
29
30/// a simple recursive type which is able to render its
31/// components in a tree-like format
32#[derive(Debug, Clone)]
33pub struct Tree<D: Display, V: Direction> {
34    pub root: D,
35    pub leaves: Vec<Tree<D, V>>,
36    multiline: bool,
37    glyphs: GlyphPalette,
38    _dir: PhantomData<V>,
39}
40
41impl<D: Display, V: Direction> Tree<D, V> {
42    pub fn new(root: D) -> Self {
43        Tree {
44            root,
45            leaves: Vec::new(),
46            multiline: false,
47            glyphs: GlyphPalette::new(),
48            _dir: PhantomData,
49        }
50    }
51
52    pub fn with_leaves(mut self, leaves: impl IntoIterator<Item = impl Into<Tree<D, V>>>) -> Self {
53        self.leaves = leaves.into_iter().map(Into::into).collect();
54        self
55    }
56
57    /// Ensure all lines for `root` are indented
58    pub fn with_multiline(mut self, yes: bool) -> Self {
59        self.multiline = yes;
60        self
61    }
62
63    /// Customize the rendering of this node
64    pub fn with_glyphs(mut self, glyphs: GlyphPalette) -> Self {
65        self.glyphs = glyphs;
66        self
67    }
68}
69
70impl<D: Display, V: Direction> Tree<D, V> {
71    /// Ensure all lines for `root` are indented
72    pub fn set_multiline(&mut self, yes: bool) -> &mut Self {
73        self.multiline = yes;
74        self
75    }
76
77    /// Customize the rendering of this node
78    pub fn set_glyphs(&mut self, glyphs: GlyphPalette) -> &mut Self {
79        self.glyphs = glyphs;
80        self
81    }
82}
83
84impl<D: Display, V: Direction> Tree<D, V> {
85    pub fn push(&mut self, leaf: impl Into<Tree<D, V>>) -> &mut Self {
86        self.leaves.push(leaf.into());
87        self
88    }
89}
90
91impl<D: Display, V: Direction> From<D> for Tree<D, V> {
92    fn from(inner: D) -> Self {
93        Self::new(inner)
94    }
95}
96
97impl<D: Display, V: Direction> Extend<D> for Tree<D, V> {
98    fn extend<T: IntoIterator<Item = D>>(&mut self, iter: T) {
99        self.leaves.extend(iter.into_iter().map(Into::into))
100    }
101}
102
103impl<D: Display, V: Direction> Extend<Tree<D, V>> for Tree<D, V> {
104    fn extend<T: IntoIterator<Item = Tree<D, V>>>(&mut self, iter: T) {
105        self.leaves.extend(iter)
106    }
107}
108
109fn print_leaf_down<D: Display>(
110    leaf: &Tree<D, Down>,
111    last: bool,
112    f: &mut fmt::Formatter,
113    spaces: &Rc<Vec<bool>>,
114    self_glyphs: &GlyphPalette,
115) -> fmt::Result{
116    let mut prefix = (
117        if last {
118            leaf.glyphs.last_item
119        } else {
120            leaf.glyphs.middle_item
121        },
122        leaf.glyphs.item_indent,
123    );
124
125    if leaf.multiline {
126        let rest_prefix = (
127            if last {
128                leaf.glyphs.last_skip
129            } else {
130                leaf.glyphs.middle_skip
131            },
132            leaf.glyphs.skip_indent,
133        );
134        debug_assert_eq!(prefix.0.chars().count(), rest_prefix.0.chars().count());
135        debug_assert_eq!(prefix.1.chars().count(), rest_prefix.1.chars().count());
136
137        let root = if f.alternate() {
138            format!("{:#}", leaf.root)
139        } else {
140            format!("{:}", leaf.root)
141        };
142        for line in root.lines() {
143            // print single line
144            for s in spaces.as_slice() {
145                if *s {
146                    self_glyphs.last_skip.fmt(f)?;
147                    self_glyphs.skip_indent.fmt(f)?;
148                } else {
149                    self_glyphs.middle_skip.fmt(f)?;
150                    self_glyphs.skip_indent.fmt(f)?;
151                }
152            }
153            prefix.0.fmt(f)?;
154            prefix.1.fmt(f)?;
155            line.fmt(f)?;
156            writeln!(f)?;
157            prefix = rest_prefix;
158        }
159    } else {
160        // print single line
161        for s in spaces.as_slice() {
162            if *s {
163                self_glyphs.last_skip.fmt(f)?;
164                self_glyphs.skip_indent.fmt(f)?;
165            } else {
166                self_glyphs.middle_skip.fmt(f)?;
167                self_glyphs.skip_indent.fmt(f)?;
168            }
169        }
170        prefix.0.fmt(f)?;
171        prefix.1.fmt(f)?;
172        leaf.root.fmt(f)?; // Pass along `f.alternate()`
173        writeln!(f)?;
174    }
175    Ok(())
176}
177
178impl<D: Display> Display for Tree<D, Down> {
179    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180        self.root.fmt(f)?; // Pass along `f.alternate()`
181        writeln!(f)?;
182        let mut queue = DisplauQueue::new();
183        let no_space = Rc::new(Vec::new());
184
185        enqueue_leaves(&mut queue, self, no_space);
186
187        while let Some((last, leaf, spaces)) = queue.pop_front() {
188            print_leaf_down(leaf, last, f, &spaces, &self.glyphs)?;
189            // recurse
190            if !leaf.leaves.is_empty() {
191
192                let s: &Vec<bool> = &spaces;
193                let mut child_spaces = s.clone();
194                child_spaces.push(last);
195                let child_spaces = Rc::new(child_spaces);
196
197                enqueue_leaves(&mut queue, leaf, child_spaces);
198            }
199        }
200        Ok(())
201    }
202}
203
204fn print_leaf_up<D: Display>(
205    leaf: &Tree<D, Up>,
206    last: bool,
207    ask_fmt: &mut fmt::Formatter, 
208    spaces: &Rc<Vec<bool>>,
209    self_glyphs: &GlyphPalette,
210) -> Result<String, std::fmt::Error>{
211    let mut result = String::new();
212    let mut prefix = (
213        if last {
214            leaf.glyphs.last_item_up
215        } else {
216            leaf.glyphs.middle_item
217        },
218        leaf.glyphs.item_indent,
219    );
220
221    if leaf.multiline {
222        let rest_prefix = (
223            leaf.glyphs.middle_skip,
224            leaf.glyphs.skip_indent,
225        );
226        debug_assert_eq!(prefix.0.chars().count(), rest_prefix.0.chars().count());
227        debug_assert_eq!(prefix.1.chars().count(), rest_prefix.1.chars().count());
228
229        let root = if ask_fmt.alternate() {
230            format!("{:#}", leaf.root)
231        } else {
232            format!("{:}", leaf.root)
233        };
234        for line in root.lines() {
235            // print single line
236            for s in spaces.as_slice() {
237                if *s {
238                    write!(&mut result, "{}", format!("{}", self_glyphs.last_skip))?;
239                    write!(&mut result, "{}", format!("{}", self_glyphs.skip_indent))?;
240                } else {
241                    write!(&mut result, "{}", format!("{}", self_glyphs.middle_skip))?;
242                    write!(&mut result, "{}", format!("{}", self_glyphs.skip_indent))?;
243                }
244            }
245            write!(&mut result, "{}", format!("{}", prefix.0))?;
246            write!(&mut result, "{}", format!("{}", prefix.1))?;
247            write!(&mut result, "{}", format!("{}", line))?;
248            writeln!(&mut result)?;
249            prefix = rest_prefix;
250        }
251    } else {
252        // print single line
253        for s in spaces.as_slice() {
254            if *s {
255                write!(&mut result, "{}", format!("{}", self_glyphs.last_skip))?;
256                write!(&mut result, "{}", format!("{}", self_glyphs.skip_indent))?;
257            } else {
258                write!(&mut result, "{}", format!("{}", self_glyphs.middle_skip))?;
259                write!(&mut result, "{}", format!("{}", self_glyphs.skip_indent))?;
260            }
261        }
262
263        write!(&mut result, "{}", format!("{}", prefix.0))?;
264        write!(&mut result, "{}", format!("{}", prefix.1))?;
265        write!(&mut result, "{}", format!("{}", leaf.root))?;
266        writeln!(&mut result)?;
267
268    }
269    Ok(result)
270}
271
272impl<D: Display> Display for Tree<D, Up> {
273    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
274        let mut queue = DisplauQueue::new();
275        let no_space = Rc::new(Vec::new());
276
277        let mut deque = VecDeque::new();
278        let first = format!("{}\n", self.root);
279        deque.push_back(first);
280
281        enqueue_leaves(&mut queue, self.clone(), no_space);
282
283        while let Some((last, leaf, spaces)) = queue.pop_front() {
284            let leaf_printed = print_leaf_up(leaf, last, f, &spaces, &self.glyphs)?;
285            deque.push_back(leaf_printed);
286            // recurse
287            if !leaf.leaves.is_empty() {
288
289                let s: &Vec<bool> = &spaces;
290                let mut child_spaces = s.clone();
291                child_spaces.push(last);
292                let child_spaces = Rc::new(child_spaces);
293
294                enqueue_leaves(&mut queue, leaf, child_spaces);
295            }
296        }
297
298        while let Some(write) = deque.pop_back() {
299            write!(f, "{}", write)?;
300            
301        }
302        Ok(())
303    }
304}
305
306type DisplauQueue<'t, D, V> = VecDeque<(bool, &'t Tree<D, V>, Rc<Vec<bool>>)>;
307
308fn enqueue_leaves<'t, D: Display, V: Direction>(
309    queue: &mut DisplauQueue<'t, D, V>,
310    parent: &'t Tree<D, V>,
311    spaces: Rc<Vec<bool>>,
312) {
313    for (i, leaf) in parent.leaves.iter().rev().enumerate() {
314        let last = i == 0;
315        queue.push_front((last, leaf, spaces.clone()));
316    }
317}
318
319
320
321#[derive(Copy, Clone, Debug, PartialEq, Eq)]
322pub struct GlyphPalette {
323    pub middle_item: &'static str,
324    pub last_item: &'static str,
325    pub last_item_up: &'static str,
326    pub item_indent: &'static str,
327
328    pub middle_skip: &'static str,
329    pub last_skip: &'static str,
330    pub skip_indent: &'static str,
331}
332
333impl GlyphPalette {
334    pub const fn new() -> Self {
335        Self {
336            middle_item: "├",
337            last_item: "└",
338            last_item_up: "┌",
339            item_indent: "── ",
340
341            middle_skip: "│",
342            last_skip: " ",
343            skip_indent: "   ",
344        }
345    }
346}
347
348impl Default for GlyphPalette {
349    fn default() -> Self {
350        Self::new()
351    }
352}