termtree-0.4.1/.cargo_vcs_info.json0000644000000001360000000000100126650ustar { "git": { "sha1": "b333eaccb707d862feb45d5202de63ff26b77839" }, "path_in_vcs": "" }termtree-0.4.1/Cargo.lock0000644000000002300000000000100106330ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "termtree" version = "0.4.1" termtree-0.4.1/Cargo.toml0000644000000034250000000000100106670ustar # 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 = "2018" name = "termtree" version = "0.4.1" include = [ "src/**/*", "Cargo.toml", "LICENSE*", "README.md", "examples/**/*", ] 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.release.pre-release-replacements]] file = "CHANGELOG.md" search = "Unreleased" replace = "{{version}}" min = 1 [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = '\.\.\.HEAD' replace = "...{{tag_name}}" exactly = 1 [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = "ReleaseDate" replace = "{{date}}" min = 1 [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = "" replace = """ ## [Unreleased] - ReleaseDate """ exactly = 1 [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = "" replace = """ [Unreleased]: https://github.com/rust-cli/termtree/compare/{{tag_name}}...HEAD""" exactly = 1 termtree-0.4.1/Cargo.toml.orig000064400000000000000000000020541046102023000143450ustar 00000000000000[package] name = "termtree" version = "0.4.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 = "MIT" edition = "2018" include = [ "src/**/*", "Cargo.toml", "LICENSE*", "README.md", "examples/**/*" ] [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}, ] termtree-0.4.1/LICENSE000064400000000000000000000020401046102023000124560ustar 00000000000000Copyright (c) 2017 Doug Tangren 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.4.1/README.md000064400000000000000000000031211046102023000127310ustar 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.4.1/examples/tree.rs000064400000000000000000000015451046102023000146050ustar 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.4.1/src/lib.rs000064400000000000000000000136141046102023000133650ustar 00000000000000#![allow(clippy::branches_sharing_code)] #[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: GlyphPalette, } impl Tree { pub fn new(root: D) -> Self { Tree { root, leaves: Vec::new(), multiline: false, glyphs: GlyphPalette::new(), } } 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 = 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 = 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()); enqueue_leaves(&mut queue, self, no_space); while let Some((last, leaf, spaces)) = queue.pop_front() { let mut prefix = ( if last { leaf.glyphs.last_item } else { leaf.glyphs.middle_item }, leaf.glyphs.item_indent, ); if leaf.multiline { let rest_prefix = ( if last { leaf.glyphs.last_skip } else { leaf.glyphs.middle_skip }, leaf.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() { if *s { self.glyphs.last_skip.fmt(f)?; self.glyphs.skip_indent.fmt(f)?; } else { self.glyphs.middle_skip.fmt(f)?; self.glyphs.skip_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() { if *s { self.glyphs.last_skip.fmt(f)?; self.glyphs.skip_indent.fmt(f)?; } else { self.glyphs.middle_skip.fmt(f)?; self.glyphs.skip_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(last); let child_spaces = Rc::new(child_spaces); enqueue_leaves(&mut queue, leaf, child_spaces); } } Ok(()) } } type DisplauQueue<'t, D> = VecDeque<(bool, &'t Tree, Rc>)>; fn enqueue_leaves<'t, D: Display>( queue: &mut DisplauQueue<'t, D>, parent: &'t Tree, spaces: Rc>, ) { for (i, leaf) in parent.leaves.iter().rev().enumerate() { let last = i == 0; queue.push_front((last, leaf, spaces.clone())); } } #[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: " ", } } } impl Default for GlyphPalette { fn default() -> Self { Self::new() } } termtree-0.4.1/src/tests.rs000064400000000000000000000016141046102023000137560ustar 00000000000000use super::*; #[test] fn render_tree_root() { let tree = Tree::new("foo"); assert_eq!(format!("{}", tree), "foo\n") } #[test] fn render_tree_with_leaves() { let tree = Tree::new("foo").with_leaves([Tree::new("bar").with_leaves(["baz"])]); assert_eq!( format!("{}", tree), r#"foo └── bar └── baz "# ) } #[test] fn render_tree_with_multiple_leaves() { let tree = Tree::new("foo").with_leaves(["bar", "baz"]); assert_eq!( format!("{}", tree), 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_eq!( format!("{}", tree), r#"foo ├── hello │ world └── goodbye world "# ) }