use clap::Parser;
mod checkbox;
mod cli;
mod commands;
mod config;
mod editor;
mod error;
mod filter;
mod library;
mod model;
mod recur;
mod storage;
fn main() {
let cli = cli::Cli::parse();
if let Err(err) = dispatch(cli) {
eprintln!("error: {err}");
std::process::exit(1);
}
}
pub(crate) fn dispatch(cli: cli::Cli) -> error::Result<()> {
if !matches!(cli.command, cli::Command::Completions { .. } | cli::Command::Recur { .. }) {
commands::recur::autorun();
}
match cli.command {
cli::Command::Repl => commands::repl::run()?,
cli::Command::Info => {
let config = config::Config::load()?;
println!("base dir: {}", config::base_dir()?.display());
println!("editor: {}", config.editor.as_deref().unwrap_or("(auto)"));
match commands::repl::detected_width() {
Some(cols) => println!("terminal: {cols} cols"),
None => println!("terminal: (not a tty)"),
}
}
cli::Command::Lib { command } => commands::lib::run(command)?,
cli::Command::New { title, description, priority, tags, due, dir, template } => {
commands::task::new(title, description, priority, tags, due, dir, template)?
}
cli::Command::Template { command } => match command {
cli::TemplateCommand::List => commands::template::list()?,
cli::TemplateCommand::Show { name } => commands::template::show(&name)?,
cli::TemplateCommand::New { name, lib, editor } => {
commands::template::new(&name, lib, editor)?
}
cli::TemplateCommand::Edit { name, editor } => {
commands::template::edit(&name, editor)?
}
cli::TemplateCommand::Remove { name } => commands::template::remove(&name)?,
},
cli::Command::Recur { command } => commands::recur::run(command)?,
cli::Command::List { filter, quiet } => commands::task::list(filter, quiet)?,
cli::Command::Batch { new_status, filter, dry_run, force } => {
commands::task::batch(&new_status, filter, dry_run, force)?
}
cli::Command::Archive { filter, dry_run, force } => {
commands::task::archive(filter, dry_run, force)?
}
cli::Command::Adopt { paths, all, dry_run, rename } => {
commands::adopt::run(commands::adopt::AdoptArgs { paths, all, dry_run, rename })?
}
cli::Command::Search { query, status, tag } => {
commands::task::search(query, status, tag)?
}
cli::Command::Tag { command } => match command {
cli::TagCommand::Add { id, tags } => commands::task::tag_add(&id, tags)?,
cli::TagCommand::Remove { id, tags } => commands::task::tag_remove(&id, tags)?,
cli::TagCommand::List => commands::task::tag_list()?,
},
cli::Command::Show { id } => commands::task::show(&id)?,
cli::Command::Delete { id, force } => commands::task::delete(&id, force)?,
cli::Command::Start { id } => commands::task::set_status(&id, model::Status::InProgress)?,
cli::Command::Done { id } => commands::task::set_status(&id, model::Status::Done)?,
cli::Command::Cancel { id } => commands::task::set_status(&id, model::Status::Cancelled)?,
cli::Command::Status { id, status } => {
let status = status
.parse::<model::Status>()
.map_err(error::Error::InvalidTaskFile)?;
commands::task::set_status(&id, status)?
}
cli::Command::Set { id, title, description, priority, due, tag_add, tag_remove } => {
commands::task::set_fields(&id, title, description, priority, due, tag_add, tag_remove)?
}
cli::Command::Step { command } => commands::step::run(command)?,
cli::Command::Log { id, text, after, stdin, quiet } => {
commands::body::log(&id, text, after.as_deref(), stdin, quiet)?
}
cli::Command::Rewrite { id, old, new, stdin, quiet } => {
commands::body::rewrite(&id, &old, new, stdin, quiet)?
}
cli::Command::Strike { id, prefix, quiet } => {
commands::body::strike(&id, &prefix, quiet)?
}
cli::Command::Board { dir, tag, columns } => {
let root = commands::task::active_library_root()?;
let scope = match &dir {
Some(d) => root.join(d),
None => root,
};
let outcome = storage::load_library(&scope)?;
let tasks: Vec<_> = outcome
.tasks
.into_iter()
.filter(|t| tag.as_ref().is_none_or(|tg| t.task.meta.tags.contains(tg)))
.collect();
print!("{}", commands::board::render(&tasks, columns)?);
}
cli::Command::Remind { within, overdue, summary } => {
commands::report::remind(within, overdue, summary)?
}
cli::Command::Overdue => commands::report::overdue()?,
cli::Command::Stats => commands::report::stats()?,
cli::Command::Completions { shell } => {
use clap::CommandFactory;
let shell: clap_complete::Shell = shell
.parse()
.map_err(|_| error::Error::InvalidTaskFile(format!("unknown shell '{shell}'")))?;
clap_complete::generate(shell, &mut cli::Cli::command(), "tasks", &mut std::io::stdout());
}
cli::Command::FixNames { dry_run } => commands::task::fix_names(dry_run)?,
cli::Command::Edit { id, editor, lib } => commands::task::edit(id, editor, lib)?,
}
Ok(())
}