diff --git a/cli/src/argparse/subcommand.rs b/cli/src/argparse/subcommand.rs index d06244bec..06cc52490 100644 --- a/cli/src/argparse/subcommand.rs +++ b/cli/src/argparse/subcommand.rs @@ -59,6 +59,7 @@ pub(crate) enum Subcommand { /// Basic operations without args Gc, Sync, + Undo, } impl Subcommand { @@ -72,6 +73,7 @@ impl Subcommand { Info::parse, Gc::parse, Sync::parse, + Undo::parse, // This must come last since it accepts arbitrary report names Report::parse, )))(input) @@ -422,6 +424,29 @@ impl Sync { } } +struct Undo; + +impl Undo { + fn parse(input: ArgList) -> IResult { + fn to_subcommand(_: &str) -> Result { + Ok(Subcommand::Undo) + } + map_res(arg_matching(literal("undo")), to_subcommand)(input) + } + + fn get_usage(u: &mut usage::Usage) { + u.subcommands.push(usage::Subcommand { + name: "undo", + syntax: "undo", + summary: "Undo the latest change made on this replica", + description: " + Undo the latest change made on this replica. + + Changes cannot be undone once they have been synchronized.", + }) + } +} + #[cfg(test)] mod test { use super::*; @@ -814,4 +839,13 @@ mod test { (&EMPTY[..], subcommand) ); } + + #[test] + fn test_undo() { + let subcommand = Subcommand::Undo; + assert_eq!( + Subcommand::parse(argv!["undo"]).unwrap(), + (&EMPTY[..], subcommand) + ); + } } diff --git a/cli/src/invocation/cmd/mod.rs b/cli/src/invocation/cmd/mod.rs index 9371f1f2c..e7606ac90 100644 --- a/cli/src/invocation/cmd/mod.rs +++ b/cli/src/invocation/cmd/mod.rs @@ -8,4 +8,5 @@ pub(crate) mod info; pub(crate) mod modify; pub(crate) mod report; pub(crate) mod sync; +pub(crate) mod undo; pub(crate) mod version; diff --git a/cli/src/invocation/cmd/undo.rs b/cli/src/invocation/cmd/undo.rs new file mode 100644 index 000000000..d3f688e7a --- /dev/null +++ b/cli/src/invocation/cmd/undo.rs @@ -0,0 +1,28 @@ +use taskchampion::Replica; +use termcolor::WriteColor; + +pub(crate) fn execute(w: &mut W, replica: &mut Replica) -> Result<(), crate::Error> { + if replica.undo()? { + writeln!(w, "Undo successful.")?; + } else { + writeln!(w, "Nothing to undo.")?; + } + Ok(()) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::invocation::test::*; + use pretty_assertions::assert_eq; + + #[test] + fn test_undo() { + let mut w = test_writer(); + let mut replica = test_replica(); + + // Note that the details of the actual undo operation are tested thoroughly in the taskchampion crate + execute(&mut w, &mut replica).unwrap(); + assert_eq!(&w.into_string(), "Nothing to undo.\n") + } +} diff --git a/cli/src/invocation/mod.rs b/cli/src/invocation/mod.rs index 2f225f86a..41a8c4ce2 100644 --- a/cli/src/invocation/mod.rs +++ b/cli/src/invocation/mod.rs @@ -90,6 +90,13 @@ pub(crate) fn invoke(command: Command, settings: Settings) -> Result<(), crate:: return cmd::sync::execute(&mut w, &mut replica, &settings, &mut server); } + Command { + subcommand: Subcommand::Undo, + .. + } => { + return cmd::undo::execute(&mut w, &mut replica); + } + // handled in the first match, but here to ensure this match is exhaustive Command { subcommand: Subcommand::Help { .. },