Something went wrong. Try again.
forked from evanpurkhiser/things3-cloud
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191pub mod anytime;pub mod area;pub mod areas;pub mod auth;pub mod completions;pub mod delete;pub mod edit;pub mod find;pub mod inbox;pub mod logbook;pub mod mark;pub mod new;pub mod project;pub mod projects;pub mod reorder;pub mod show;pub mod someday;pub mod tags;pub mod today;pub mod trash;pub mod upcoming;
use anyhow::{Result, bail};use clap::{Args, Subcommand};use serde::Serialize;
use crate::{app::Cli, cmd_ctx::CmdCtx};
pub trait Command { fn run_with_ctx( &self, cli: &Cli, out: &mut dyn std::io::Write, ctx: &mut dyn CmdCtx, ) -> Result<()>;}
pub(crate) fn detailed_json_conflict(json: bool, detailed: bool) -> Result<()> { if json && detailed { bail!("--detailed cannot be combined with --json."); } Ok(())}
pub(crate) fn write_json<T: Serialize>(out: &mut dyn std::io::Write, value: &T) -> Result<()> { serde_json::to_writer_pretty(&mut *out, value)?; writeln!(out)?; Ok(())}
#[derive(Default, Args)]pub struct DetailedArgs { /// Show notes and checklists beneath each task #[arg(long, short = 'd')] pub detailed: bool,}
#[derive(Args)]pub struct TagDeltaArgs { #[arg( long = "add-tags", short = 'a', help = "Comma-separated tags to add (titles or ID prefixes)" )] pub add_tags: Option<String>, #[arg( long = "remove-tags", short = 'r', help = "Comma-separated tags to remove (titles or ID prefixes)" )] pub remove_tags: Option<String>,}
#[derive(Subcommand)]pub enum Commands { Inbox(inbox::InboxArgs), Today(today::TodayArgs), Upcoming(upcoming::UpcomingArgs), Anytime(anytime::AnytimeArgs), Someday(someday::SomedayArgs), Logbook(logbook::LogbookArgs), Trash(trash::TrashArgs), Projects(projects::ProjectsArgs), Project(project::ProjectArgs), Areas(areas::AreasArgs), Area(area::AreaArgs), Tags(tags::TagsArgs), New(new::NewArgs), Edit(edit::EditArgs), Mark(mark::MarkArgs), Reorder(reorder::ReorderArgs), Delete(delete::DeleteArgs), Auth(auth::AuthArgs), Find(find::FindArgs), Show(show::ShowArgs), Completions(completions::CompletionsArgs),}
impl Command for Commands { fn run_with_ctx( &self, cli: &Cli, out: &mut dyn std::io::Write, ctx: &mut dyn CmdCtx, ) -> Result<()> { match self { Commands::Inbox(args) => args.run_with_ctx(cli, out, ctx), Commands::Today(args) => args.run_with_ctx(cli, out, ctx), Commands::Upcoming(args) => args.run_with_ctx(cli, out, ctx), Commands::Anytime(args) => args.run_with_ctx(cli, out, ctx), Commands::Someday(args) => args.run_with_ctx(cli, out, ctx), Commands::Logbook(args) => args.run_with_ctx(cli, out, ctx), Commands::Trash(args) => args.run_with_ctx(cli, out, ctx), Commands::Projects(args) => args.run_with_ctx(cli, out, ctx), Commands::Project(args) => args.run_with_ctx(cli, out, ctx), Commands::Areas(args) => args.run_with_ctx(cli, out, ctx), Commands::Area(args) => args.run_with_ctx(cli, out, ctx), Commands::Tags(args) => args.run_with_ctx(cli, out, ctx), Commands::New(args) => args.run_with_ctx(cli, out, ctx), Commands::Edit(args) => args.run_with_ctx(cli, out, ctx), Commands::Mark(args) => args.run_with_ctx(cli, out, ctx), Commands::Reorder(args) => args.run_with_ctx(cli, out, ctx), Commands::Delete(args) => args.run_with_ctx(cli, out, ctx), Commands::Auth(args) => args.run_with_ctx(cli, out, ctx), Commands::Find(args) => args.run_with_ctx(cli, out, ctx), Commands::Show(args) => args.run_with_ctx(cli, out, ctx), Commands::Completions(args) => args.run_with_ctx(cli, out, ctx), } }}
#[cfg(test)]mod tests { use clap::Parser;
use crate::app::Cli;
/// the flag combinations clap refuses before any command runs #[test] fn conflicting_and_missing_arguments_are_refused_at_parse_time() { let refused = |args: &[&str]| { let mut argv = vec!["things"]; argv.extend_from_slice(args); Cli::try_parse_from(argv).is_err() }; assert!(refused(&["edit"])); assert!(refused(&[ "edit", "A", "--deadline", "2027-01-15", "--clear-deadline" ])); assert!(refused(&[ "edit", "A", "--reminder", "09:00", "--clear-reminder" ])); assert!(refused(&["edit", "A", "--times", "3"])); assert!(refused(&[ "edit", "A", "--repeat", "daily", "--times", "3", "--until", "2027-01-15" ])); assert!(refused(&["new", "x", "--before", "A", "--after", "B"])); assert!(refused(&["new", "x", "--until", "2027-01-15"])); assert!(refused(&["reorder", "A"])); assert!(refused(&[ "reorder", "A", "--before-id", "B", "--after-id", "C" ])); assert!(refused(&["mark", "--done"])); assert!(refused(&["delete"])); assert!(!refused(&[ "edit", "A", "--repeat", "daily", "--times", "3" ])); assert!(!refused(&["reorder", "A", "--after-id", "B"])); }}