cli: `rad patch --help` opens man page

This is the same behavior as `git`. We also update the man page to have
the full list of commands and flags.
This commit is contained in:
cloudhead 2023-08-31 13:31:58 +02:00
parent b1213931de
commit 8d5299fe85
No known key found for this signature in database
5 changed files with 99 additions and 18 deletions

View File

@ -12,7 +12,16 @@ rad-patch - Manage radicle patches.
== Synopsis == Synopsis
*rad patch* * *rad patch* [<option>...] +
*rad patch* _list_ [--all|--merged|--open|--archived|--draft] [<option>...] +
*rad patch* _show_ <patch-id> [<option>...] +
*rad patch* _archive_ <patch-id> [<option>...] +
*rad patch* _update_ <patch-id> [<option>...] +
*rad patch* _checkout_ <patch-id> [<option>...] +
*rad patch* _delete_ <patch-id> [<option>...] +
*rad patch* _redact_ <revision-id> [<option>...] +
*rad patch* _ready_ <patch-id> [--undo] [<option>...] +
*rad patch* _edit_ <patch-id> [<option>...] +
== Description == Description
@ -20,13 +29,64 @@ The Radicle *patch* command is used for managing changesets inside of Radicle
repositories. repositories.
Though many actions can be performed using *rad patch*, certain patch-related Though many actions can be performed using *rad patch*, certain patch-related
actions require using *git* directly. For example, opening a patch is typically actions use *git* directly. For example, opening a patch is typically
done using *git push*, while merging a patch is done with a combination of done using *git push*, while merging a patch is done with a combination of
*git merge* and *git push*. *git merge* and *git push*.
To make this possible, Radicle ships with a helper program, *git-remote-rad* To make this possible, Radicle ships with a helper program, *git-remote-rad*
which is invoked by *git* on push and fetch to and from Radicle remotes. which is invoked by *git* on push and fetch to and from Radicle remotes.
== Commands
With no arguments, *rad patch* defaults to the _list_ command, showing the list of
open patches for the current repository.
=== show
Shows information on the given patch.
*<patch-id>*:: The patch to show
*--patch*, *-p*:: Show the patch changes in git patch format
*--verbose*, *-v*:: Show additional information about the patch
=== edit
Edits a patch revision comment. To edit the patch title or description, pass
in the *<patch-id>*. To edit a revision comment, pass that revision's
*<revision-id>*.
*<revision-id>*::
The revision to edit.
*--message*, *-m [<string>]*::
Comment message to the patch or revision. If omitted, Radicle will prompt for
a comment string via *$EDITOR*.
=== list
List patches in the current repository. The default is *--open*.
*--all*:: List all patches, including merged and archived patches
*--archived*:: List only archived patches
*--merged*:: List only merged patches
*--open*:: List only open patches
*--draft*:: List only draft patches
=== ready
Mark a patch as ready to review. This changes the state of a patch from *draft*
to *open*.
*--undo*:: Change a patch back to *draft*
=== update
Updates a patch to the current repository *HEAD*. This is a low-level command
that should only be used when using *git push rad* is not possible.
*--message*, *-m [<string>]*:: Provide a comment message to the revision
*--no-message*:: Leave the revision comment message blank
== Opening a patch == Opening a patch
To open a patch, we start by making changes to our working copy, typically on To open a patch, we start by making changes to our working copy, typically on
@ -148,7 +208,3 @@ our master branch. Then we pushed to our *rad* remote.
To list patches, run *rad patch*. By default, this will only show open patches. To list patches, run *rad patch*. By default, this will only show open patches.
To list all patches, including ones that have been merged or archived, add the To list all patches, including ones that have been merged or archived, add the
*--all* option. *--all* option.
== Other patch commands
For a full list of patch sub-commands, run *rad patch --help*.

View File

@ -242,7 +242,10 @@ impl Args for Options {
Long("verbose") | Short('v') => { Long("verbose") | Short('v') => {
verbose = true; verbose = true;
} }
Long("help") | Short('h') => { Long("help") => {
return Err(Error::HelpManual.into());
}
Short('h') => {
return Err(Error::Help.into()); return Err(Error::Help.into());
} }

View File

@ -76,28 +76,30 @@ where
opts opts
} }
Err(err) => { Err(err) => {
match err.downcast_ref::<Error>() { let hint = match err.downcast_ref::<Error>() {
Some(Error::Help) => { Some(Error::Help) => {
term::help(help.name, help.version, help.description, help.usage); term::help(help.name, help.version, help.description, help.usage);
process::exit(0); process::exit(0);
} }
Some(Error::HelpManual) => {
let Ok(status) = term::manual(help.name) else {
perror(help.name, "failed to load manual page");
process::exit(1);
};
process::exit(status.code().unwrap_or(0));
}
Some(Error::Usage) => { Some(Error::Usage) => {
term::usage(help.name, help.usage); term::usage(help.name, help.usage);
process::exit(1); process::exit(1);
} }
_ => {} Some(Error::WithHint { hint, .. }) => Some(hint),
None => None,
}; };
eprintln!( perror(help.name, &err);
"{} {} rad {}: {err}",
Paint::red(ERROR_PREFIX),
Paint::red("Error:"),
help.name,
);
if let Some(Error::WithHint { hint, .. }) = err.downcast_ref::<Error>() { if let Some(hint) = hint {
eprintln!("{}", Paint::yellow(hint)); eprintln!("{}", Paint::yellow(hint));
} }
process::exit(1); process::exit(1);
} }
}; };
@ -123,6 +125,15 @@ pub fn profile() -> Result<Profile, anyhow::Error> {
} }
} }
pub fn perror(name: &str, err: impl std::fmt::Display) {
eprintln!(
"{} {} rad {}: {err}",
Paint::red(ERROR_PREFIX),
Paint::red("Error:"),
name,
);
}
pub fn fail(header: &str, error: &anyhow::Error) { pub fn fail(header: &str, error: &anyhow::Error) {
let err = error.to_string(); let err = error.to_string();
let err = err.trim_end(); let err = err.trim_end();

View File

@ -15,6 +15,9 @@ pub enum Error {
/// If this error is returned from argument parsing, help is displayed. /// If this error is returned from argument parsing, help is displayed.
#[error("help invoked")] #[error("help invoked")]
Help, Help,
/// If this error is returned from argument parsing, the manual page is displayed.
#[error("help manual invoked")]
HelpManual,
/// If this error is returned from argument parsing, usage is displayed. /// If this error is returned from argument parsing, usage is displayed.
#[error("usage invoked")] #[error("usage invoked")]
Usage, Usage,

View File

@ -1,5 +1,5 @@
use std::ffi::OsStr; use std::ffi::OsStr;
use std::{env, fmt}; use std::{env, fmt, io, process};
use inquire::ui::{ErrorMessageRenderConfig, StyleSheet, Styled}; use inquire::ui::{ErrorMessageRenderConfig, StyleSheet, Styled};
use inquire::InquireError; use inquire::InquireError;
@ -123,6 +123,14 @@ pub fn help(name: &str, version: &str, description: &str, usage: &str) {
println!("rad-{name} {version}\n{description}\n{usage}"); println!("rad-{name} {version}\n{description}\n{usage}");
} }
pub fn manual(name: &str) -> io::Result<process::ExitStatus> {
let mut child = process::Command::new("man")
.arg(format!("rad-{name}"))
.spawn()?;
child.wait()
}
pub fn usage(name: &str, usage: &str) { pub fn usage(name: &str, usage: &str) {
println!( println!(
"{} {}\n{}", "{} {}\n{}",