sysutils: add tree utility
This commit is contained in:
@@ -81,6 +81,10 @@ path = "src/echo.rs"
|
||||
name = "ls"
|
||||
path = "src/ls.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "tree"
|
||||
path = "src/tree.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "mv"
|
||||
path = "src/mv.rs"
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
use std::{
|
||||
fs, io,
|
||||
path::{Path, PathBuf},
|
||||
process::ExitCode,
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
fn list<P: AsRef<Path>>(path: P, depth: usize, last_mask: u64) -> io::Result<()> {
|
||||
fn indent(depth: usize, mask: u64) {
|
||||
for i in 0..depth + 1 {
|
||||
if i < depth {
|
||||
if (1 << i) & mask == 0 {
|
||||
print!("│ ");
|
||||
} else {
|
||||
print!(" ");
|
||||
}
|
||||
} else if (1 << i) & mask == 0 {
|
||||
print!("├─");
|
||||
} else {
|
||||
print!("└─");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut entries = vec![];
|
||||
let dir = fs::read_dir(path)?;
|
||||
for entry in dir {
|
||||
let Ok((ty, entry)) = entry.and_then(|e| Ok((e.file_type()?, e))) else {
|
||||
entries.push(None);
|
||||
continue;
|
||||
};
|
||||
|
||||
let name = entry.file_name().into_string().unwrap();
|
||||
|
||||
if name == "." || name == ".." {
|
||||
continue;
|
||||
}
|
||||
|
||||
entries.push(Some((name, entry.path(), ty.is_dir())));
|
||||
}
|
||||
|
||||
entries.sort_by(|a, b| {
|
||||
let (Some((a, _, _)), Some((b, _, _))) = (a.as_ref(), b.as_ref()) else {
|
||||
return Ord::cmp(&a.is_none(), &b.is_none());
|
||||
};
|
||||
|
||||
Ord::cmp(a, b)
|
||||
});
|
||||
|
||||
let len = entries.len();
|
||||
for (i, entry) in entries.into_iter().enumerate() {
|
||||
let last_bit = ((i == len - 1) as u64) << depth;
|
||||
indent(depth, last_mask | last_bit);
|
||||
|
||||
let Some((name, path, dir)) = entry else {
|
||||
println!("<error>");
|
||||
continue;
|
||||
};
|
||||
|
||||
println!("{name}");
|
||||
|
||||
if dir {
|
||||
list(path, depth + 1, last_mask | last_bit).ok();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
fn run<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||
list(path, 0, 0)
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args = Args::parse();
|
||||
let path = args.path.unwrap_or_else(|| PathBuf::from("."));
|
||||
match run(&path) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(error) => {
|
||||
eprintln!("{}: {}", path.display(), error);
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,7 @@ const PROGRAMS: &[(&str, &str)] = &[
|
||||
("sysmon", "bin/sysmon"),
|
||||
("top", "bin/top"),
|
||||
("touch", "bin/touch"),
|
||||
("tree", "bin/tree"),
|
||||
("tst", "bin/tst"),
|
||||
("view", "bin/view"),
|
||||
// netutils
|
||||
|
||||
Reference in New Issue
Block a user