mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
implement cli help
This commit is contained in:
parent
2c579b9f01
commit
6b550e7516
7 changed files with 323 additions and 53 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -569,7 +569,7 @@ dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"strsim",
|
"strsim",
|
||||||
"textwrap",
|
"textwrap 0.11.0",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"vec_map",
|
"vec_map",
|
||||||
]
|
]
|
||||||
|
@ -2274,6 +2274,7 @@ dependencies = [
|
||||||
"prettytable-rs",
|
"prettytable-rs",
|
||||||
"taskchampion",
|
"taskchampion",
|
||||||
"tempdir",
|
"tempdir",
|
||||||
|
"textwrap 0.12.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2346,6 +2347,15 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.22"
|
version = "1.0.22"
|
||||||
|
|
|
@ -11,6 +11,7 @@ failure = "^0.1.8"
|
||||||
log = "^0.4.11"
|
log = "^0.4.11"
|
||||||
nom = "*"
|
nom = "*"
|
||||||
prettytable-rs = "^0.8.0"
|
prettytable-rs = "^0.8.0"
|
||||||
|
textwrap = "0.12.1"
|
||||||
|
|
||||||
[dependencies.config]
|
[dependencies.config]
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
|
@ -24,4 +24,10 @@ pub(crate) use modification::{DescriptionMod, Modification};
|
||||||
pub(crate) use report::Report;
|
pub(crate) use report::Report;
|
||||||
pub(crate) use subcommand::Subcommand;
|
pub(crate) use subcommand::Subcommand;
|
||||||
|
|
||||||
|
use crate::usage::Usage;
|
||||||
|
|
||||||
type ArgList<'a> = &'a [&'a str];
|
type ArgList<'a> = &'a [&'a str];
|
||||||
|
|
||||||
|
pub(crate) fn get_usage(usage: &mut Usage) {
|
||||||
|
Subcommand::get_usage(usage);
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
use super::args::*;
|
use super::args::*;
|
||||||
use super::{ArgList, DescriptionMod, Filter, Modification, Report};
|
use super::{ArgList, DescriptionMod, Filter, Modification, Report};
|
||||||
|
use crate::usage;
|
||||||
use nom::{branch::alt, combinator::*, sequence::*, IResult};
|
use nom::{branch::alt, combinator::*, sequence::*, IResult};
|
||||||
use taskchampion::Status;
|
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.
|
/// A subcommand is the specific operation that the CLI should execute.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -45,19 +56,33 @@ pub(crate) enum Subcommand {
|
||||||
impl Subcommand {
|
impl Subcommand {
|
||||||
pub(super) fn parse(input: ArgList) -> IResult<ArgList, Subcommand> {
|
pub(super) fn parse(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||||
alt((
|
alt((
|
||||||
Self::version,
|
Version::parse,
|
||||||
Self::help,
|
Help::parse,
|
||||||
Self::add,
|
Add::parse,
|
||||||
Self::modify_prepend_append,
|
Modify::parse,
|
||||||
Self::start_stop_done,
|
List::parse,
|
||||||
Self::list,
|
Info::parse,
|
||||||
Self::info,
|
Gc::parse,
|
||||||
Self::gc,
|
Sync::parse,
|
||||||
Self::sync,
|
|
||||||
))(input)
|
))(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, ()> {
|
fn to_subcommand(_: &str) -> Result<Subcommand, ()> {
|
||||||
Ok(Subcommand::Version)
|
Ok(Subcommand::Version)
|
||||||
}
|
}
|
||||||
|
@ -70,7 +95,20 @@ impl Subcommand {
|
||||||
)(input)
|
)(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, ()> {
|
fn to_subcommand(input: &str) -> Result<Subcommand, ()> {
|
||||||
Ok(Subcommand::Help {
|
Ok(Subcommand::Help {
|
||||||
summary: input == "-h",
|
summary: input == "-h",
|
||||||
|
@ -86,7 +124,13 @@ impl Subcommand {
|
||||||
)(input)
|
)(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, ()> {
|
fn to_subcommand(input: (&str, Modification)) -> Result<Subcommand, ()> {
|
||||||
Ok(Subcommand::Add {
|
Ok(Subcommand::Add {
|
||||||
modification: input.1,
|
modification: input.1,
|
||||||
|
@ -98,7 +142,24 @@ impl Subcommand {
|
||||||
)(input)
|
)(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, ()> {
|
fn to_subcommand(input: (Filter, &str, Modification)) -> Result<Subcommand, ()> {
|
||||||
let filter = input.0;
|
let filter = input.0;
|
||||||
let mut modification = input.2;
|
let mut modification = input.2;
|
||||||
|
@ -114,6 +175,9 @@ impl Subcommand {
|
||||||
modification.description = DescriptionMod::Append(s)
|
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("modify")),
|
||||||
arg_matching(literal("prepend")),
|
arg_matching(literal("prepend")),
|
||||||
arg_matching(literal("append")),
|
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("start")),
|
||||||
arg_matching(literal("stop")),
|
arg_matching(literal("stop")),
|
||||||
arg_matching(literal("done")),
|
arg_matching(literal("done")),
|
||||||
|
@ -166,7 +203,70 @@ impl Subcommand {
|
||||||
)(input)
|
)(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, ()> {
|
fn to_subcommand(input: (Report, &str)) -> Result<Subcommand, ()> {
|
||||||
Ok(Subcommand::List { report: input.0 })
|
Ok(Subcommand::List { report: input.0 })
|
||||||
}
|
}
|
||||||
|
@ -176,7 +276,23 @@ impl Subcommand {
|
||||||
)(input)
|
)(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, ()> {
|
fn to_subcommand(input: (Filter, &str)) -> Result<Subcommand, ()> {
|
||||||
let debug = input.1 == "debug";
|
let debug = input.1 == "debug";
|
||||||
Ok(Subcommand::Info {
|
Ok(Subcommand::Info {
|
||||||
|
@ -196,19 +312,76 @@ impl Subcommand {
|
||||||
)(input)
|
)(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, ()> {
|
fn to_subcommand(_: &str) -> Result<Subcommand, ()> {
|
||||||
Ok(Subcommand::Gc)
|
Ok(Subcommand::Gc)
|
||||||
}
|
}
|
||||||
map_res(arg_matching(literal("gc")), to_subcommand)(input)
|
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, ()> {
|
fn to_subcommand(_: &str) -> Result<Subcommand, ()> {
|
||||||
Ok(Subcommand::Sync)
|
Ok(Subcommand::Sync)
|
||||||
}
|
}
|
||||||
map_res(arg_matching(literal("sync")), to_subcommand)(input)
|
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)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
|
use crate::usage::Usage;
|
||||||
use failure::Fallible;
|
use failure::Fallible;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
pub(crate) fn execute(command_name: String, summary: bool) -> Fallible<()> {
|
pub(crate) fn execute(command_name: String, summary: bool) -> Fallible<()> {
|
||||||
println!(
|
let usage = Usage::new();
|
||||||
"TaskChampion {}: Personal task-tracking",
|
usage.write_help(io::stdout(), command_name, summary)?;
|
||||||
env!("CARGO_PKG_VERSION")
|
|
||||||
);
|
|
||||||
if !summary {
|
|
||||||
println!();
|
|
||||||
println!("USAGE: {} [args]\n(help output TODO)", command_name); // TODO
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ mod argparse;
|
||||||
mod invocation;
|
mod invocation;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod table;
|
mod table;
|
||||||
|
mod usage;
|
||||||
|
|
||||||
/// The main entry point for the command-line interface. This builds an Invocation
|
/// The main entry point for the command-line interface. This builds an Invocation
|
||||||
/// from the particulars of the operating-system interface, and then executes it.
|
/// from the particulars of the operating-system interface, and then executes it.
|
||||||
|
|
83
cli/src/usage.rs
Normal file
83
cli/src/usage.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
//! This module handles creation of CLI usage documents (--help, manpages, etc.) in
|
||||||
|
//! a way that puts the source of that documentation near its implementation.
|
||||||
|
|
||||||
|
use crate::argparse;
|
||||||
|
use std::io::{Result, Write};
|
||||||
|
use textwrap::indent;
|
||||||
|
|
||||||
|
/// A top-level structure containing usage/help information for the entire CLI.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub(crate) struct Usage {
|
||||||
|
pub(crate) subcommands: Vec<Subcommand>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Usage {
|
||||||
|
/// Get a new, completely-filled-out usage object
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
let mut rv = Self {
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
argparse::get_usage(&mut rv);
|
||||||
|
|
||||||
|
// TODO: sort subcommands
|
||||||
|
|
||||||
|
rv
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write this usage to the given output as a help string, writing a short version if `summary`
|
||||||
|
/// is true.
|
||||||
|
pub(crate) fn write_help<W: Write>(
|
||||||
|
&self,
|
||||||
|
mut w: W,
|
||||||
|
command_name: String,
|
||||||
|
summary: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
"TaskChampion {}: Personal task-tracking\n\n",
|
||||||
|
env!("CARGO_PKG_VERSION")
|
||||||
|
)?;
|
||||||
|
write!(w, "USAGE:\n {} [args]\n\n", command_name)?;
|
||||||
|
write!(w, "TaskChampion subcommands:\n")?;
|
||||||
|
for subcommand in self.subcommands.iter() {
|
||||||
|
subcommand.write_help(&mut w, summary)?;
|
||||||
|
}
|
||||||
|
if !summary {
|
||||||
|
write!(w, "\nSee `task help` for more detail\n")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub(crate) struct Subcommand {
|
||||||
|
/// Name of the subcommand
|
||||||
|
pub(crate) name: String,
|
||||||
|
|
||||||
|
/// Syntax summary, without command_name
|
||||||
|
pub(crate) syntax: String,
|
||||||
|
|
||||||
|
/// One-line description of the subcommand. Use an initial capital and no trailing period.
|
||||||
|
pub(crate) summary: String,
|
||||||
|
|
||||||
|
/// Multi-line description of the subcommand. It's OK for this to duplicate summary, as the
|
||||||
|
/// two are not displayed together.
|
||||||
|
pub(crate) description: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Subcommand {
|
||||||
|
fn write_help<W: Write>(&self, mut w: W, summary: bool) -> Result<()> {
|
||||||
|
if summary {
|
||||||
|
write!(w, " task {} - {}\n", self.name, self.summary)?;
|
||||||
|
} else {
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
" task {}\n{}\n",
|
||||||
|
self.syntax,
|
||||||
|
indent(self.description.trim(), " ")
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue