mod action;
mod app;
mod cli;
mod collect;
mod editor;
mod error;
mod event;
mod logging;
mod model;
mod scan;
mod tui;
mod ui;
mod version;
use std::path::{Path, PathBuf};
use std::process::ExitCode;
use app::App;
use cli::Command;
use error::AppError;
#[tokio::main]
async fn main() -> ExitCode {
if let Err(err) = color_eyre::install() {
eprintln!("{}: failed to install error handler: {err}", cli::BIN_NAME);
return ExitCode::FAILURE;
}
match cli::parse(std::env::args().skip(1)) {
Ok(Command::Version) => {
println!("{}", version::long_version());
ExitCode::SUCCESS
}
Ok(Command::Help) => {
println!("{}", cli::usage());
ExitCode::SUCCESS
}
Ok(Command::Run { dir }) => {
let _log_guard = logging::init();
match run(dir).await {
Ok(()) => ExitCode::SUCCESS,
Err(err) => {
eprintln!("{}: {err:#}", cli::BIN_NAME);
ExitCode::FAILURE
}
}
}
Err(err) => {
eprintln!("{}: {err}\n\n{}", cli::BIN_NAME, cli::usage());
ExitCode::from(2)
}
}
}
async fn run(dir: PathBuf) -> color_eyre::Result<()> {
let root = validate_dir(&dir)?;
let label = root_label(&dir, &root);
tracing::info!(target = %root.display(), "tree starting");
let mut app = App::new(root, label);
let mut terminal = tui::init()?;
let result = event::run(&mut terminal, &mut app).await;
tui::restore();
result
}
fn validate_dir(dir: &Path) -> color_eyre::Result<PathBuf> {
if !dir.exists() {
return Err(AppError::NotFound(dir.to_path_buf()).into());
}
if !dir.is_dir() {
return Err(AppError::NotADirectory(dir.to_path_buf()).into());
}
Ok(std::fs::canonicalize(dir)?)
}
fn root_label(original: &Path, canonical: &Path) -> String {
let shown = original.to_string_lossy();
if matches!(shown.as_ref(), "." | "./" | "") {
canonical
.file_name()
.map(|name| name.to_string_lossy().into_owned())
.unwrap_or_else(|| shown.into_owned())
} else {
shown.into_owned()
}
}