termtree-0.5.1/.cargo_vcs_info.json0000644000000001360000000000100126660ustar { "git": { "sha1": "976ddd79b0af191bdd1325adfa0c4baa9c773f63" }, "path_in_vcs": "" }termtree-0.5.1/Cargo.lock0000644000000121350000000000100106430ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anstream" version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys", ] [[package]] name = "colorchoice" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "is_terminal_polyfill" version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" [[package]] name = "normalize-line-endings" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "similar" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" [[package]] name = "snapbox" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40e14d10e4c2b4331ac24c33baa5a03e1fbca81c045b285b53b2a612d28569fb" dependencies = [ "anstream", "anstyle", "normalize-line-endings", "similar", "snapbox-macros", ] [[package]] name = "snapbox-macros" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1f4c14672714436c09254801c934b203196a51182a5107fb76591c7cc56424d" dependencies = [ "anstream", ] [[package]] name = "termtree" version = "0.5.1" dependencies = [ "snapbox", ] [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" termtree-0.5.1/Cargo.toml0000644000000101650000000000100106670ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.74" name = "termtree" version = "0.5.1" build = false include = [ "build.rs", "src/**/*", "Cargo.toml", "Cargo.lock", "LICENSE*", "README.md", "benches/**/*", "examples/**/*", ] autobins = false autoexamples = false autotests = false autobenches = false description = "Visualize tree-like data on the command-line" homepage = "https://github.com/rust-cli/termtree" documentation = "https://docs.rs/termtree" readme = "README.md" keywords = [ "cli", "tree", "dag", ] categories = [ "command-line-interface", "visualization", ] license = "MIT" repository = "https://github.com/rust-cli/termtree" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" min = 1 replace = "{{version}}" search = "Unreleased" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = "...{{tag_name}}" search = '\.\.\.HEAD' [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" min = 1 replace = "{{date}}" search = "ReleaseDate" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = """ ## [Unreleased] - ReleaseDate """ search = "" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = """ [Unreleased]: https://github.com/rust-cli/termtree/compare/{{tag_name}}...HEAD""" search = "" [lib] name = "termtree" path = "src/lib.rs" [[example]] name = "pretty-test" path = "examples/pretty-test.rs" [[example]] name = "tree" path = "examples/tree.rs" [dependencies] [dev-dependencies.snapbox] version = "0.6.10" [lints.clippy] bool_assert_comparison = "allow" branches_sharing_code = "allow" checked_conversions = "warn" collapsible_else_if = "allow" create_dir = "warn" dbg_macro = "warn" debug_assert_with_mut_call = "warn" doc_markdown = "warn" empty_enum = "warn" enum_glob_use = "warn" expl_impl_clone_on_copy = "warn" explicit_deref_methods = "warn" explicit_into_iter_loop = "warn" fallible_impl_from = "warn" filter_map_next = "warn" flat_map_option = "warn" float_cmp_const = "warn" fn_params_excessive_bools = "warn" from_iter_instead_of_collect = "warn" if_same_then_else = "allow" implicit_clone = "warn" imprecise_flops = "warn" inconsistent_struct_constructor = "warn" inefficient_to_string = "warn" infinite_loop = "warn" invalid_upcast_comparisons = "warn" items_after_statements = "warn" large_digit_groups = "warn" large_stack_arrays = "warn" large_types_passed_by_value = "warn" let_and_return = "allow" linkedlist = "warn" lossy_float_literal = "warn" macro_use_imports = "warn" match_wildcard_for_single_variants = "warn" mem_forget = "warn" mutex_integer = "warn" needless_continue = "warn" needless_for_each = "warn" negative_feature_names = "warn" path_buf_push_overwrite = "warn" ptr_as_ptr = "warn" rc_mutex = "warn" redundant_feature_names = "warn" ref_option_ref = "warn" rest_pat_in_fully_bound_structs = "warn" same_functions_in_if_condition = "warn" self_named_module_files = "warn" semicolon_if_nothing_returned = "warn" single_match_else = "warn" str_to_string = "warn" string_add = "warn" string_add_assign = "warn" string_lit_as_bytes = "warn" string_to_string = "warn" todo = "warn" trait_duplication_in_bounds = "warn" verbose_file_reads = "warn" wildcard_imports = "warn" zero_sized_map_values = "warn" [lints.rust] rust_2018_idioms = "warn" unreachable_pub = "warn" unsafe_op_in_unsafe_fn = "warn" unused_lifetimes = "warn" unused_macro_rules = "warn" unused_qualifications = "warn" termtree-0.5.1/Cargo.toml.orig000064400000000000000000000065651046102023000143610ustar 00000000000000[workspace] resolver = "2" [workspace.package] license = "MIT" edition = "2021" rust-version = "1.74" # MSRV include = [ "build.rs", "src/**/*", "Cargo.toml", "Cargo.lock", "LICENSE*", "README.md", "benches/**/*", "examples/**/*" ] [workspace.lints.rust] rust_2018_idioms = "warn" unreachable_pub = "warn" unsafe_op_in_unsafe_fn = "warn" unused_lifetimes = "warn" unused_macro_rules = "warn" unused_qualifications = "warn" [workspace.lints.clippy] bool_assert_comparison = "allow" branches_sharing_code = "allow" checked_conversions = "warn" collapsible_else_if = "allow" create_dir = "warn" dbg_macro = "warn" debug_assert_with_mut_call = "warn" doc_markdown = "warn" empty_enum = "warn" enum_glob_use = "warn" expl_impl_clone_on_copy = "warn" explicit_deref_methods = "warn" explicit_into_iter_loop = "warn" fallible_impl_from = "warn" filter_map_next = "warn" flat_map_option = "warn" float_cmp_const = "warn" fn_params_excessive_bools = "warn" from_iter_instead_of_collect = "warn" if_same_then_else = "allow" implicit_clone = "warn" imprecise_flops = "warn" inconsistent_struct_constructor = "warn" inefficient_to_string = "warn" infinite_loop = "warn" invalid_upcast_comparisons = "warn" items_after_statements = "warn" large_digit_groups = "warn" large_stack_arrays = "warn" large_types_passed_by_value = "warn" let_and_return = "allow" # sometimes good to name what you are returning linkedlist = "warn" lossy_float_literal = "warn" macro_use_imports = "warn" match_wildcard_for_single_variants = "warn" mem_forget = "warn" mutex_integer = "warn" needless_continue = "warn" needless_for_each = "warn" negative_feature_names = "warn" path_buf_push_overwrite = "warn" ptr_as_ptr = "warn" rc_mutex = "warn" redundant_feature_names = "warn" ref_option_ref = "warn" rest_pat_in_fully_bound_structs = "warn" same_functions_in_if_condition = "warn" self_named_module_files = "warn" semicolon_if_nothing_returned = "warn" single_match_else = "warn" str_to_string = "warn" string_add = "warn" string_add_assign = "warn" string_lit_as_bytes = "warn" string_to_string = "warn" todo = "warn" trait_duplication_in_bounds = "warn" verbose_file_reads = "warn" wildcard_imports = "warn" zero_sized_map_values = "warn" [package] name = "termtree" version = "0.5.1" description = "Visualize tree-like data on the command-line" documentation = "https://docs.rs/termtree" homepage = "https://github.com/rust-cli/termtree" repository = "https://github.com/rust-cli/termtree" categories = ["command-line-interface", "visualization"] keywords = ["cli", "tree", "dag"] license.workspace = true edition.workspace = true rust-version.workspace = true include.workspace = true [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] [package.metadata.release] pre-release-replacements = [ {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/termtree/compare/{{tag_name}}...HEAD", exactly=1}, ] [dependencies] [dev-dependencies] snapbox = "0.6.10" [lints] workspace = true termtree-0.5.1/LICENSE-MIT000064400000000000000000000020461046102023000131140ustar 00000000000000Copyright (c) Individual contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. termtree-0.5.1/README.md000064400000000000000000000031211046102023000127320ustar 00000000000000# termtree [![Main](https://github.com/rust-cli/termtree/actions/workflows/main.yml/badge.svg)](https://github.com/rust-cli/termtree/actions/workflows/main.yml) > Visualize tree-like data on the command-line [API documentation](https://docs.rs/termtree) ## Example An example program is provided under the "examples" directory to mimic the `tree(1)` linux program ```bash $ cargo run --example tree target Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/examples/tree target` target └── debug ├── .cargo-lock ├── .fingerprint | └── termtree-21a5bdbd42e0b6da | ├── dep-example-tree | ├── dep-lib-termtree | ├── example-tree | ├── example-tree.json | ├── lib-termtree | └── lib-termtree.json ├── build ├── deps | └── libtermtree.rlib ├── examples | ├── tree | └── tree.dSYM | └── Contents | ├── Info.plist | └── Resources | └── DWARF | └── tree ├── libtermtree.rlib └── native ``` ## Related Crates - [`treeline`](https://crates.io/crates/treeline): termtree was forked from this. - [`tree_decorator`](https://crates.io/crates/tree_decorator) - [`xtree`](https://crates.io/crates/xtree) - [`ptree`](https://crates.io/crates/ptree) ## License Licensed under MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) termtree-0.5.1/examples/pretty-test.rs000064400000000000000000000051431046102023000161510ustar 00000000000000//! Make the output of `cargo test` prettier in tree style. //! To see the complete demo coupling with rust-script, you can refer to //! use std::collections::{btree_map::Entry, BTreeMap}; use termtree::{GlyphPalette, Tree}; fn main() { let text = " test a::b::c ... FAILED test a::d ... ok test works ... ok "; assert_eq!( pretty_test(text.trim().lines()).unwrap().to_string(), "\ test ├── a │ ├── b │ │ └─ ❌ c │ └─ ✅ d └─ ✅ works\n" ); } #[derive(Debug)] enum Node<'s> { Path(BTreeMap<&'s str, Node<'s>>), Status(&'s str), } fn pretty_test<'s>(lines: impl Iterator) -> Option> { let mut path = BTreeMap::new(); for line in lines { let mut iter = line.splitn(3, ' '); let mut split = iter.nth(1)?.split("::"); let next = split.next(); let status = iter.next()?; make_node(split, status, &mut path, next); } let mut tree = Tree::new("test"); for (root, child) in path { make_tree(root, &child, &mut tree); } Some(tree) } // Add paths to Node fn make_node<'s>( mut split: impl Iterator, status: &'s str, path: &mut BTreeMap<&'s str, Node<'s>>, key: Option<&'s str>, ) { let Some(key) = key else { return }; let next = split.next(); match path.entry(key) { Entry::Vacant(empty) => { if next.is_some() { let mut btree = BTreeMap::new(); make_node(split, status, &mut btree, next); empty.insert(Node::Path(btree)); } else { empty.insert(Node::Status(status)); } } Entry::Occupied(mut btree) => { if let Node::Path(btree) = btree.get_mut() { make_node(split, status, btree, next); } } } } // Add Node to Tree fn make_tree<'s>(root: &'s str, node: &Node<'s>, parent: &mut Tree<&'s str>) { match node { Node::Path(btree) => { let mut t = Tree::new(root); for (path, child) in btree { make_tree(path, child, &mut t); } parent.push(t); } Node::Status(s) => { parent.push(Tree::new(root).with_glyphs(set_status(s))); } } } // Display with a status icon fn set_status(status: &str) -> GlyphPalette { let mut glyph = GlyphPalette::new(); glyph.item_indent = if status.ends_with("ok") { "─ ✅ " } else { "─ ❌ " }; glyph } termtree-0.5.1/examples/tree.rs000064400000000000000000000015451046102023000146060ustar 00000000000000use termtree::Tree; use std::path::Path; use std::{env, fs, io}; fn label>(p: P) -> String { p.as_ref().file_name().unwrap().to_str().unwrap().to_owned() } fn tree>(p: P) -> io::Result> { let result = fs::read_dir(&p)?.filter_map(|e| e.ok()).fold( Tree::new(label(p.as_ref().canonicalize()?)), |mut root, entry| { let dir = entry.metadata().unwrap(); if dir.is_dir() { root.push(tree(entry.path()).unwrap()); } else { root.push(Tree::new(label(entry.path()))); } root }, ); Ok(result) } fn main() { let dir = env::args().nth(1).unwrap_or_else(|| String::from(".")); match tree(dir) { Ok(tree) => println!("{}", tree), Err(err) => println!("error: {}", err), } } termtree-0.5.1/src/lib.rs000064400000000000000000000144671046102023000133750ustar 00000000000000#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![warn(clippy::print_stderr)] #![warn(clippy::print_stdout)] #[cfg(test)] mod tests; use std::collections::VecDeque; use std::fmt::{self, Display}; use std::rc::Rc; /// a simple recursive type which is able to render its /// components in a tree-like format #[derive(Debug, Clone)] pub struct Tree { pub root: D, pub leaves: Vec>, multiline: bool, glyphs: Option, } impl Tree { pub fn new(root: D) -> Self { Tree { root, leaves: Vec::new(), multiline: false, glyphs: None, } } pub fn with_leaves(mut self, leaves: impl IntoIterator>>) -> Self { self.leaves = leaves.into_iter().map(Into::into).collect(); self } /// Ensure all lines for `root` are indented pub fn with_multiline(mut self, yes: bool) -> Self { self.multiline = yes; self } /// Customize the rendering of this node pub fn with_glyphs(mut self, glyphs: GlyphPalette) -> Self { self.glyphs = Some(glyphs); self } } impl Tree { /// Ensure all lines for `root` are indented pub fn set_multiline(&mut self, yes: bool) -> &mut Self { self.multiline = yes; self } /// Customize the rendering of this node pub fn set_glyphs(&mut self, glyphs: GlyphPalette) -> &mut Self { self.glyphs = Some(glyphs); self } } impl Tree { pub fn push(&mut self, leaf: impl Into>) -> &mut Self { self.leaves.push(leaf.into()); self } } impl From for Tree { fn from(inner: D) -> Self { Self::new(inner) } } impl Extend for Tree { fn extend>(&mut self, iter: T) { self.leaves.extend(iter.into_iter().map(Into::into)); } } impl Extend> for Tree { fn extend>>(&mut self, iter: T) { self.leaves.extend(iter); } } impl Display for Tree { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.root.fmt(f)?; // Pass along `f.alternate()` writeln!(f)?; let mut queue = DisplauQueue::new(); let no_space = Rc::new(Vec::new()); let default_glyphs = GlyphPalette::new(); let glyphs = self.glyphs.as_ref().unwrap_or(&default_glyphs); enqueue_leaves(&mut queue, self, glyphs, no_space); while let Some((last, leaf, glyphs, spaces)) = queue.pop_front() { let mut prefix = ( if last { glyphs.last_item } else { glyphs.middle_item }, glyphs.item_indent, ); if leaf.multiline { let rest_prefix = ( if last { glyphs.last_skip } else { glyphs.middle_skip }, glyphs.skip_indent, ); debug_assert_eq!(prefix.0.chars().count(), rest_prefix.0.chars().count()); debug_assert_eq!(prefix.1.chars().count(), rest_prefix.1.chars().count()); let root = if f.alternate() { format!("{:#}", leaf.root) } else { format!("{:}", leaf.root) }; for line in root.lines() { // print single line for s in spaces.as_slice() { s.skip.fmt(f)?; s.indent.fmt(f)?; } prefix.0.fmt(f)?; prefix.1.fmt(f)?; line.fmt(f)?; writeln!(f)?; prefix = rest_prefix; } } else { // print single line for s in spaces.as_slice() { s.skip.fmt(f)?; s.indent.fmt(f)?; } prefix.0.fmt(f)?; prefix.1.fmt(f)?; leaf.root.fmt(f)?; // Pass along `f.alternate()` writeln!(f)?; } // recurse if !leaf.leaves.is_empty() { let s: &Vec = &spaces; let mut child_spaces = s.clone(); child_spaces.push(if last { glyphs.last_space() } else { glyphs.middle_space() }); let child_spaces = Rc::new(child_spaces); enqueue_leaves(&mut queue, leaf, glyphs, child_spaces); } } Ok(()) } } type DisplauQueue<'t, D> = VecDeque<(bool, &'t Tree, &'t GlyphPalette, Rc>)>; fn enqueue_leaves<'t, D: Display>( queue: &mut DisplauQueue<'t, D>, parent: &'t Tree, parent_glyphs: &'t GlyphPalette, spaces: Rc>, ) { for (i, leaf) in parent.leaves.iter().rev().enumerate() { let last = i == 0; let glyphs = leaf.glyphs.as_ref().unwrap_or(parent_glyphs); queue.push_front((last, leaf, glyphs, spaces.clone())); } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] struct SpacePalette { skip: &'static str, indent: &'static str, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct GlyphPalette { pub middle_item: &'static str, pub last_item: &'static str, pub item_indent: &'static str, pub middle_skip: &'static str, pub last_skip: &'static str, pub skip_indent: &'static str, } impl GlyphPalette { pub const fn new() -> Self { Self { middle_item: "├", last_item: "└", item_indent: "── ", middle_skip: "│", last_skip: " ", skip_indent: " ", } } fn middle_space(&self) -> SpacePalette { SpacePalette { skip: self.middle_skip, indent: self.skip_indent, } } fn last_space(&self) -> SpacePalette { SpacePalette { skip: self.last_skip, indent: self.skip_indent, } } } impl Default for GlyphPalette { fn default() -> Self { Self::new() } } termtree-0.5.1/src/tests.rs000064400000000000000000000055151046102023000137630ustar 00000000000000use snapbox::assert_data_eq; use snapbox::str; use super::*; #[test] fn render_tree_root() { let tree = Tree::new("foo"); assert_data_eq!( format!("{}", tree), str![[r#" foo "#]] ); } #[test] fn render_tree_with_leaves() { let tree = Tree::new("foo").with_leaves([Tree::new("bar").with_leaves(["baz"])]); assert_data_eq!( format!("{}", tree), str![[r#" foo └── bar └── baz "#]] ); } #[test] fn render_tree_with_multiple_leaves() { let tree = Tree::new("foo").with_leaves(["bar", "baz"]); assert_data_eq!( format!("{}", tree), str![[r#" foo ├── bar └── baz "#]] ); } #[test] fn render_tree_with_multiline_leaf() { let tree = Tree::new("foo").with_leaves([ Tree::new("hello\nworld").with_multiline(true), Tree::new("goodbye\nworld").with_multiline(true), ]); assert_data_eq!( format!("{}", tree), str![[r#" foo ├── hello │ world └── goodbye world "#]] ); } #[test] fn render_custom_glyphs() { let root = GlyphPalette { middle_item: "[mid ]", last_item: "[last ]", item_indent: "[indent ]", middle_skip: "[mskip]", last_skip: "[lskip]", skip_indent: "[iskip ]", }; let middle = GlyphPalette { middle_item: "(mid )", last_item: "(last )", item_indent: "(indent )", middle_skip: "(mskip)", last_skip: "(lskip)", skip_indent: "(iskip )", }; let tree = Tree::new("node 1").with_glyphs(root).with_leaves([ Tree::new("node 1.1"), Tree::new("node 1.2"), Tree::new("node 1.3").with_leaves([ Tree::new("node 1.3.1").with_glyphs(middle), Tree::new("node 1.3.2").with_glyphs(middle), Tree::new("node 1.3.3") .with_glyphs(middle) .with_leaves(["node 1.3.3.1", "node 1.3.3.2"]), ]), Tree::new("node 1.4").with_leaves([ Tree::new("node 1.4.1"), Tree::new("node 1.4.2"), Tree::new("node 1.4.3").with_leaves(["node 1.4.3.1", "node 1.4.3.2"]), ]), ]); assert_data_eq!( format!("{}", tree), str![[r#" node 1 [mid ][indent ]node 1.1 [mid ][indent ]node 1.2 [mid ][indent ]node 1.3 [mskip][iskip ](mid )(indent )node 1.3.1 [mskip][iskip ](mid )(indent )node 1.3.2 [mskip][iskip ](last )(indent )node 1.3.3 [mskip][iskip ](lskip)(iskip )(mid )(indent )node 1.3.3.1 [mskip][iskip ](lskip)(iskip )(last )(indent )node 1.3.3.2 [last ][indent ]node 1.4 [lskip][iskip ][mid ][indent ]node 1.4.1 [lskip][iskip ][mid ][indent ]node 1.4.2 [lskip][iskip ][last ][indent ]node 1.4.3 [lskip][iskip ][lskip][iskip ][mid ][indent ]node 1.4.3.1 [lskip][iskip ][lskip][iskip ][last ][indent ]node 1.4.3.2 "#]] ); }