use crate::tree_config::{tree_config, TreeConfig}; use std::cmp::max; use std::sync::{Arc, Mutex}; /// Tree that holds `text` for the current leaf and a list of `children` that are the branches. #[derive(Debug)] pub struct Tree { pub text: Option, pub children: Vec, } /// Position of the element relative to its siblings #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Position { Inside, First, Last, Only, } impl Tree { /// Create a new tree with some optional text. pub fn new(text: Option<&str>) -> Tree { Tree { text: text.map(|x| x.to_string()), children: Vec::new(), } } /// Navigate to the branch at the given `path` relative to this tree. /// If a valid branch is found by following the path, it is returned. pub fn at_mut(&mut self, path: &[usize]) -> Option<&mut Tree> { match path.first() { Some(&i) => match self.children.get_mut(i) { Some(x) => x.at_mut(&path[1..]), _ => None, }, _ => Some(self), } } /// "Render" this tree as a list of `String`s. /// Each string represents a line in the tree. /// `does_continue` is a bool for each column indicating whether the tree continues. pub fn lines( &self, does_continue: &Vec, index: usize, pool_size: usize, config: &TreeConfig, ) -> Vec { let does_continue = if config.show_first_level && does_continue.is_empty() { vec![true] } else { does_continue.clone() }; let position = match index { _ if pool_size == 1 => Position::Only, _ if (index + 1) == pool_size => Position::Last, 0 => Position::First, _ => Position::Inside, }; let mut next_continue = does_continue.clone(); next_continue.push(match position { Position::Inside | Position::First => true, Position::Last | Position::Only => false, }); let mut txt = String::new(); let pad: String; if does_continue.len() > 1 { for &i in &does_continue[2..] { txt.push_str(&format!( "{}{:indent$}", if i { config.symbols.continued } else { " " }, "", indent = max(config.indent, 1) - 1 )); } pad = txt.clone(); let branch_size = max(config.indent, 2usize) - 2; let branch = match config.symbols.branch.len() { 0 => "-".repeat(branch_size), 1 => config.symbols.branch.repeat(branch_size), _n => config .symbols .branch .repeat(branch_size) .chars() .take(branch_size) .collect::(), }; let is_multiline = self .text .as_ref() .map(|x| x.contains("\n")) .unwrap_or(false); let first_leaf = match (is_multiline, config.symbols.multiline_first) { (true, Some(x)) => x, _ => config.symbols.leaf, }; txt.push_str(&format!( "{}{}{}", match position { Position::Only => config.symbols.join_only, Position::First => config.symbols.join_first, Position::Last => config.symbols.join_last, Position::Inside => config.symbols.join_inner, }, branch, first_leaf, )); let s = match &self.text { Some(x) => match is_multiline { true => format!( "{}", x.replace( "\n", &format!( "\n{}{}{}{}", &pad, match position { Position::Only | Position::Last => " ".repeat(config.symbols.continued.chars().count()), _ => config.symbols.continued.to_string(), }, " ".repeat(branch_size), match &config.symbols.multiline_continued { Some(multi) => multi.to_string(), _ => " ".repeat(first_leaf.chars().count()), } ), ) ), false => x.clone(), }, _ => String::new(), }; txt.push_str(&s); } else { if let Some(x) = &self.text { txt.push_str(&x); } } let mut ret = vec![txt]; for (index, x) in self.children.iter().enumerate() { for line in x.lines(&next_continue, index, self.children.len(), config) { ret.push(line); } } ret } } /// Holds the current state of the tree, including the path to the branch. /// Multiple trees may point to the same data. #[derive(Debug, Clone)] pub(crate) struct TreeBuilderBase { data: Arc>, path: Vec, dive_count: usize, config: Option, is_enabled: bool, } impl TreeBuilderBase { /// Create a new state pub fn new() -> TreeBuilderBase { TreeBuilderBase { data: Arc::new(Mutex::new(Tree::new(None))), path: vec![], dive_count: 1, config: None, is_enabled: true, } } pub fn set_enabled(&mut self, enabled: bool) { self.is_enabled = enabled; } pub fn is_enabled(&self) -> bool { self.is_enabled } pub fn add_leaf(&mut self, text: &str) { let &dive_count = &self.dive_count; if dive_count > 0 { for i in 0..dive_count { let mut n = 0; if let Some(x) = self.data.lock().unwrap().at_mut(&self.path) { x.children.push(Tree::new(if i == max(1, dive_count) - 1 { Some(&text) } else { None })); n = x.children.len() - 1; } self.path.push(n); } self.dive_count = 0; } else { if let Some(x) = self .data .lock() .unwrap() .at_mut(&self.path[..max(1, self.path.len()) - 1]) { x.children.push(Tree::new(Some(&text))); let n = match self.path.last() { Some(&x) => x + 1, _ => 0, }; self.path.last_mut().map(|x| *x = n); } } } pub fn set_config_override(&mut self, config: Option) { self.config = config; } pub fn config_override(&self) -> &Option { &self.config } pub fn config_override_mut(&mut self) -> &mut Option { &mut self.config } pub fn enter(&mut self) { self.dive_count += 1; } /// Try stepping up to the parent tree branch. /// Returns false if already at the top branch. pub fn exit(&mut self) -> bool { if self.dive_count > 0 { self.dive_count -= 1; true } else { if self.path.len() > 1 { self.path.pop(); true } else { false } } } pub fn depth(&self) -> usize { max(1, self.path.len() + self.dive_count) - 1 } pub fn peek_print(&self) { println!("{}", self.peek_string()); } pub fn print(&mut self) { self.peek_print(); self.clear(); } pub fn clear(&mut self) { *self = Self::new(); } pub fn string(&mut self) -> String { let s = self.peek_string(); self.clear(); s } pub fn peek_string(&self) -> String { let config = self .config_override() .clone() .unwrap_or_else(|| tree_config().clone()); (&self.data.lock().unwrap().lines(&vec![], 0, 1, &config)[1..]).join("\n") } }