implement cli help
This commit is contained in:
@@ -1,7 +1,18 @@
|
||||
use super::args::*;
|
||||
use super::{ArgList, DescriptionMod, Filter, Modification, Report};
|
||||
use crate::usage;
|
||||
use nom::{branch::alt, combinator::*, sequence::*, IResult};
|
||||
use taskchampion::Status;
|
||||
use textwrap::dedent;
|
||||
|
||||
// IMPLEMENTATION NOTE:
|
||||
//
|
||||
// For each variant of Subcommand, there is a private, empty type of the same name with a `parse`
|
||||
// method and a `get_usage` method. The parse methods may handle several subcommands, but always
|
||||
// produce the variant of the same name as the type.
|
||||
//
|
||||
// This organization helps to gather the parsing and usage information into
|
||||
// comprehensible chunks of code, to ensure that everything is documented.
|
||||
|
||||
/// A subcommand is the specific operation that the CLI should execute.
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -45,19 +56,33 @@ pub(crate) enum Subcommand {
|
||||
impl Subcommand {
|
||||
pub(super) fn parse(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
alt((
|
||||
Self::version,
|
||||
Self::help,
|
||||
Self::add,
|
||||
Self::modify_prepend_append,
|
||||
Self::start_stop_done,
|
||||
Self::list,
|
||||
Self::info,
|
||||
Self::gc,
|
||||
Self::sync,
|
||||
Version::parse,
|
||||
Help::parse,
|
||||
Add::parse,
|
||||
Modify::parse,
|
||||
List::parse,
|
||||
Info::parse,
|
||||
Gc::parse,
|
||||
Sync::parse,
|
||||
))(input)
|
||||
}
|
||||
|
||||
fn version(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
pub(super) fn get_usage(u: &mut usage::Usage) {
|
||||
Version::get_usage(u);
|
||||
Help::get_usage(u);
|
||||
Add::get_usage(u);
|
||||
Modify::get_usage(u);
|
||||
List::get_usage(u);
|
||||
Info::get_usage(u);
|
||||
Gc::get_usage(u);
|
||||
Sync::get_usage(u);
|
||||
}
|
||||
}
|
||||
|
||||
struct Version;
|
||||
|
||||
impl Version {
|
||||
fn parse(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn to_subcommand(_: &str) -> Result<Subcommand, ()> {
|
||||
Ok(Subcommand::Version)
|
||||
}
|
||||
@@ -70,7 +95,20 @@ impl Subcommand {
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn help(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn get_usage(u: &mut usage::Usage) {
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "version".to_owned(),
|
||||
syntax: "version".to_owned(),
|
||||
summary: "Show the TaskChampion version".to_owned(),
|
||||
description: "Show the version of the TaskChampion binary".to_owned(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct Help;
|
||||
|
||||
impl Help {
|
||||
fn parse(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn to_subcommand(input: &str) -> Result<Subcommand, ()> {
|
||||
Ok(Subcommand::Help {
|
||||
summary: input == "-h",
|
||||
@@ -86,7 +124,13 @@ impl Subcommand {
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn add(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn get_usage(_u: &mut usage::Usage) {}
|
||||
}
|
||||
|
||||
struct Add;
|
||||
|
||||
impl Add {
|
||||
fn parse(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn to_subcommand(input: (&str, Modification)) -> Result<Subcommand, ()> {
|
||||
Ok(Subcommand::Add {
|
||||
modification: input.1,
|
||||
@@ -98,7 +142,24 @@ impl Subcommand {
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn modify_prepend_append(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn get_usage(u: &mut usage::Usage) {
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "add".to_owned(),
|
||||
syntax: "add [modification]".to_owned(),
|
||||
summary: "Add a new task".to_owned(),
|
||||
description: dedent(
|
||||
"
|
||||
Add a new, pending task to the list of tasks. The modification must include a
|
||||
description.",
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct Modify;
|
||||
|
||||
impl Modify {
|
||||
fn parse(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn to_subcommand(input: (Filter, &str, Modification)) -> Result<Subcommand, ()> {
|
||||
let filter = input.0;
|
||||
let mut modification = input.2;
|
||||
@@ -114,6 +175,9 @@ impl Subcommand {
|
||||
modification.description = DescriptionMod::Append(s)
|
||||
}
|
||||
}
|
||||
"start" => modification.active = Some(true),
|
||||
"stop" => modification.active = Some(false),
|
||||
"done" => modification.status = Some(Status::Completed),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -129,33 +193,6 @@ impl Subcommand {
|
||||
arg_matching(literal("modify")),
|
||||
arg_matching(literal("prepend")),
|
||||
arg_matching(literal("append")),
|
||||
)),
|
||||
Modification::parse,
|
||||
)),
|
||||
to_subcommand,
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn start_stop_done(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
// start, stop, and done are special cases of modify
|
||||
fn to_subcommand(input: (Filter, &str, Modification)) -> Result<Subcommand, ()> {
|
||||
let filter = input.0;
|
||||
let mut modification = input.2;
|
||||
match input.1 {
|
||||
"start" => modification.active = Some(true),
|
||||
"stop" => modification.active = Some(false),
|
||||
"done" => modification.status = Some(Status::Completed),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
Ok(Subcommand::Modify {
|
||||
filter,
|
||||
modification,
|
||||
})
|
||||
}
|
||||
map_res(
|
||||
tuple((
|
||||
Filter::parse,
|
||||
alt((
|
||||
arg_matching(literal("start")),
|
||||
arg_matching(literal("stop")),
|
||||
arg_matching(literal("done")),
|
||||
@@ -166,7 +203,70 @@ impl Subcommand {
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn list(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn get_usage(u: &mut usage::Usage) {
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "modify".to_owned(),
|
||||
syntax: "[filter] modify [modification]".to_owned(),
|
||||
summary: "Modify tasks".to_owned(),
|
||||
description: dedent(
|
||||
"
|
||||
Modify all tasks matching the filter.",
|
||||
),
|
||||
});
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "prepend".to_owned(),
|
||||
syntax: "[filter] prepend [modification]".to_owned(),
|
||||
summary: "Prepend task description".to_owned(),
|
||||
description: dedent(
|
||||
"
|
||||
Modify all tasks matching the filter by inserting the given description before each
|
||||
task's description.",
|
||||
),
|
||||
});
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "append".to_owned(),
|
||||
syntax: "[filter] append [modification]".to_owned(),
|
||||
summary: "Append task description".to_owned(),
|
||||
description: dedent(
|
||||
"
|
||||
Modify all tasks matching the filter by adding the given description to the end
|
||||
of each task's description.",
|
||||
),
|
||||
});
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "start".to_owned(),
|
||||
syntax: "[filter] start [modification]".to_owned(),
|
||||
summary: "Start tasks".to_owned(),
|
||||
description: dedent(
|
||||
"
|
||||
Start all tasks matching the filter, additionally applying any given modifications."),
|
||||
});
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "stop".to_owned(),
|
||||
syntax: "[filter] start [modification]".to_owned(),
|
||||
summary: "Stop tasks".to_owned(),
|
||||
description: dedent(
|
||||
"
|
||||
Stop all tasks matching the filter, additionally applying any given modifications.",
|
||||
),
|
||||
});
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "done".to_owned(),
|
||||
syntax: "[filter] start [modification]".to_owned(),
|
||||
summary: "Mark tasks as completed".to_owned(),
|
||||
description: dedent(
|
||||
"
|
||||
Mark all tasks matching the filter as completed, additionally applying any given
|
||||
modifications.",
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct List;
|
||||
|
||||
impl List {
|
||||
fn parse(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn to_subcommand(input: (Report, &str)) -> Result<Subcommand, ()> {
|
||||
Ok(Subcommand::List { report: input.0 })
|
||||
}
|
||||
@@ -176,7 +276,23 @@ impl Subcommand {
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn info(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn get_usage(u: &mut usage::Usage) {
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "list".to_owned(),
|
||||
syntax: "[filter] list".to_owned(),
|
||||
summary: "List tasks".to_owned(),
|
||||
description: dedent(
|
||||
"
|
||||
Show a list of the tasks matching the filter",
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct Info;
|
||||
|
||||
impl Info {
|
||||
fn parse(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn to_subcommand(input: (Filter, &str)) -> Result<Subcommand, ()> {
|
||||
let debug = input.1 == "debug";
|
||||
Ok(Subcommand::Info {
|
||||
@@ -196,19 +312,76 @@ impl Subcommand {
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn gc(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn get_usage(u: &mut usage::Usage) {
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "info".to_owned(),
|
||||
syntax: "[filter] info".to_owned(),
|
||||
summary: "Show tasks".to_owned(),
|
||||
description: dedent(
|
||||
"
|
||||
Show information about all tasks matching the fiter.",
|
||||
),
|
||||
});
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "debug".to_owned(),
|
||||
syntax: "[filter] debug".to_owned(),
|
||||
summary: "Show task debug details".to_owned(),
|
||||
description: dedent(
|
||||
"
|
||||
Show all key/value properties of the tasks matching the fiter.",
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct Gc;
|
||||
|
||||
impl Gc {
|
||||
fn parse(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn to_subcommand(_: &str) -> Result<Subcommand, ()> {
|
||||
Ok(Subcommand::Gc)
|
||||
}
|
||||
map_res(arg_matching(literal("gc")), to_subcommand)(input)
|
||||
}
|
||||
|
||||
fn sync(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn get_usage(u: &mut usage::Usage) {
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "gc".to_owned(),
|
||||
syntax: "gc".to_owned(),
|
||||
summary: "Perform 'garbage collection'".to_owned(),
|
||||
description: dedent(
|
||||
"
|
||||
Perform 'garbage collection'. This refreshes the list of pending tasks
|
||||
and their short id's.",
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct Sync;
|
||||
|
||||
impl Sync {
|
||||
fn parse(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||
fn to_subcommand(_: &str) -> Result<Subcommand, ()> {
|
||||
Ok(Subcommand::Sync)
|
||||
}
|
||||
map_res(arg_matching(literal("sync")), to_subcommand)(input)
|
||||
}
|
||||
|
||||
fn get_usage(u: &mut usage::Usage) {
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "sync".to_owned(),
|
||||
syntax: "sync".to_owned(),
|
||||
summary: "Synchronize this replica".to_owned(),
|
||||
description: dedent(
|
||||
"
|
||||
Synchronize this replica locally or against a remote server, as configured.
|
||||
|
||||
Synchronization is a critical part of maintaining the task database, and should
|
||||
be done regularly, even if only locally. It is typically run in a crontask.",
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user