From a9b0912b2bd314a6e50982b75e9f93b265ad1890 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 15 May 2024 00:28:50 -0700 Subject: [PATCH] Add --man subcommand (#2041) --- Cargo.lock | 17 ++ Cargo.toml | 10 +- README.md | 8 + completions/just.bash | 2 +- completions/just.elvish | 1 + completions/just.fish | 1 + completions/just.powershell | 1 + completions/just.zsh | 1 + {bin => crates}/generate-book/Cargo.toml | 0 {bin => crates}/generate-book/src/main.rs | 0 {bin => crates}/ref-type/Cargo.toml | 0 {bin => crates}/ref-type/src/main.rs | 0 {bin => crates}/ref-type/tests/integration.rs | 0 .../update-contributors/Cargo.toml | 0 .../update-contributors/src/main.rs | 0 justfile | 12 +- man/just.1 | 217 +++++++++--------- src/config.rs | 40 ++-- src/error.rs | 6 + src/subcommand.rs | 25 +- tests/lib.rs | 1 + tests/man.rs | 9 + 22 files changed, 206 insertions(+), 145 deletions(-) rename {bin => crates}/generate-book/Cargo.toml (100%) rename {bin => crates}/generate-book/src/main.rs (100%) rename {bin => crates}/ref-type/Cargo.toml (100%) rename {bin => crates}/ref-type/src/main.rs (100%) rename {bin => crates}/ref-type/tests/integration.rs (100%) rename {bin => crates}/update-contributors/Cargo.toml (100%) rename {bin => crates}/update-contributors/src/main.rs (100%) create mode 100644 tests/man.rs diff --git a/Cargo.lock b/Cargo.lock index 4b90853a95..a34c6a5d0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,6 +215,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +[[package]] +name = "clap_mangen" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1dd95b5ebb5c1c54581dd6346f3ed6a79a3eef95dd372fc2ac13d535535300e" +dependencies = [ + "clap 4.5.4", + "roff", +] + [[package]] name = "colorchoice" version = "1.0.1" @@ -512,6 +522,7 @@ dependencies = [ "camino", "clap 4.5.4", "clap_complete", + "clap_mangen", "cradle", "ctrlc", "derivative", @@ -774,6 +785,12 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +[[package]] +name = "roff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316" + [[package]] name = "rustix" version = "0.38.34" diff --git a/Cargo.toml b/Cargo.toml index cbbb1e1ed2..5ac4ca8a30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ repository = "https://github.com/casey/just" rust-version = "1.63" [workspace] -members = [".", "bin/ref-type", "bin/generate-book", "bin/update-contributors"] +members = [".", "crates/*"] [dependencies] ansi_term = "0.12.0" @@ -24,6 +24,7 @@ blake3 = { version = "1.5.0", features = ["rayon", "mmap"] } camino = "1.0.4" clap = { version = "4.0.0", features = ["env", "wrap_help"] } clap_complete = "4.0.0" +clap_mangen = "0.2.20" ctrlc = { version = "3.1.1", features = ["termination"] } derivative = "2.0.0" dirs = "5.0.1" @@ -64,13 +65,6 @@ path = "src/main.rs" name = "just" test = false -[features] -# No features are active by default. -default = [] -# The `help4help2man` feature modifies the message produced by `--help` -# so that `help2man` produces a reasonable man page. -help4help2man = [] - # The public documentation is minimal and doesn't change between # platforms, so we only build them for linux on docs.rs to save # their build machines some cycles. diff --git a/README.md b/README.md index a6d8c7d423..8937fbefc6 100644 --- a/README.md +++ b/README.md @@ -3158,6 +3158,14 @@ fpath=($HOMEBREW_PREFIX/share/zsh/site-functions $fpath) # compinit ``` +### Man Page + +`just` can print its own man page with `just --man`. Man pages are written in +[`roff`](https://en.wikipedia.org/wiki/Roff_%28software%29), a venerable markup +language and one of the first practical applications of Unix. If you have +[`groff`](https://www.gnu.org/software/groff/) installed you can view the man +page with `just --man | groff -mandoc -Tascii | less`. + ### Grammar A non-normative grammar of `justfile`s can be found in diff --git a/completions/just.bash b/completions/just.bash index 0cd0880c61..7006dba8c7 100644 --- a/completions/just.bash +++ b/completions/just.bash @@ -30,7 +30,7 @@ _just() { case "${cmd}" in "$1") - opts="-n -f -q -u -v -d -c -e -l -s -E -h -V --check --chooser --color --command-color --yes --dry-run --dump-format --highlight --list-heading --list-prefix --no-aliases --no-deps --no-dotenv --no-highlight --justfile --quiet --set --shell --shell-arg --shell-command --clear-shell-args --unsorted --unstable --verbose --working-directory --changelog --choose --command --completions --dump --edit --evaluate --fmt --init --list --show --summary --variables --dotenv-filename --dotenv-path --help --version [ARGUMENTS]..." + opts="-n -f -q -u -v -d -c -e -l -s -E -h -V --check --chooser --color --command-color --yes --dry-run --dump-format --highlight --list-heading --list-prefix --no-aliases --no-deps --no-dotenv --no-highlight --justfile --quiet --set --shell --shell-arg --shell-command --clear-shell-args --unsorted --unstable --verbose --working-directory --changelog --choose --command --completions --dump --edit --evaluate --fmt --init --list --man --show --summary --variables --dotenv-filename --dotenv-path --help --version [ARGUMENTS]..." if [[ ${cur} == -* ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/completions/just.elvish b/completions/just.elvish index 86882d50ec..461b75ae65 100644 --- a/completions/just.elvish +++ b/completions/just.elvish @@ -66,6 +66,7 @@ set edit:completion:arg-completer[just] = {|@words| cand --init 'Initialize new justfile in project root' cand -l 'List available recipes and their arguments' cand --list 'List available recipes and their arguments' + cand --man 'Print man page' cand --summary 'List names of available recipes' cand --variables 'List names of variables' cand -h 'Print help' diff --git a/completions/just.fish b/completions/just.fish index 23da10459b..073820af74 100644 --- a/completions/just.fish +++ b/completions/just.fish @@ -73,6 +73,7 @@ complete -c just -l evaluate -d 'Evaluate and print all variables. If a variable complete -c just -l fmt -d 'Format and overwrite justfile' complete -c just -l init -d 'Initialize new justfile in project root' complete -c just -s l -l list -d 'List available recipes and their arguments' +complete -c just -l man -d 'Print man page' complete -c just -l summary -d 'List names of available recipes' complete -c just -l variables -d 'List names of variables' complete -c just -s h -l help -d 'Print help' diff --git a/completions/just.powershell b/completions/just.powershell index f46cea1a2e..2b02718f1c 100644 --- a/completions/just.powershell +++ b/completions/just.powershell @@ -69,6 +69,7 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock { [CompletionResult]::new('--init', 'init', [CompletionResultType]::ParameterName, 'Initialize new justfile in project root') [CompletionResult]::new('-l', 'l', [CompletionResultType]::ParameterName, 'List available recipes and their arguments') [CompletionResult]::new('--list', 'list', [CompletionResultType]::ParameterName, 'List available recipes and their arguments') + [CompletionResult]::new('--man', 'man', [CompletionResultType]::ParameterName, 'Print man page') [CompletionResult]::new('--summary', 'summary', [CompletionResultType]::ParameterName, 'List names of available recipes') [CompletionResult]::new('--variables', 'variables', [CompletionResultType]::ParameterName, 'List names of variables') [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help') diff --git a/completions/just.zsh b/completions/just.zsh index 4550daa7e9..0b79adbacb 100644 --- a/completions/just.zsh +++ b/completions/just.zsh @@ -64,6 +64,7 @@ _just() { '--init[Initialize new justfile in project root]' \ '-l[List available recipes and their arguments]' \ '--list[List available recipes and their arguments]' \ +'--man[Print man page]' \ '--summary[List names of available recipes]' \ '--variables[List names of variables]' \ '-h[Print help]' \ diff --git a/bin/generate-book/Cargo.toml b/crates/generate-book/Cargo.toml similarity index 100% rename from bin/generate-book/Cargo.toml rename to crates/generate-book/Cargo.toml diff --git a/bin/generate-book/src/main.rs b/crates/generate-book/src/main.rs similarity index 100% rename from bin/generate-book/src/main.rs rename to crates/generate-book/src/main.rs diff --git a/bin/ref-type/Cargo.toml b/crates/ref-type/Cargo.toml similarity index 100% rename from bin/ref-type/Cargo.toml rename to crates/ref-type/Cargo.toml diff --git a/bin/ref-type/src/main.rs b/crates/ref-type/src/main.rs similarity index 100% rename from bin/ref-type/src/main.rs rename to crates/ref-type/src/main.rs diff --git a/bin/ref-type/tests/integration.rs b/crates/ref-type/tests/integration.rs similarity index 100% rename from bin/ref-type/tests/integration.rs rename to crates/ref-type/tests/integration.rs diff --git a/bin/update-contributors/Cargo.toml b/crates/update-contributors/Cargo.toml similarity index 100% rename from bin/update-contributors/Cargo.toml rename to crates/update-contributors/Cargo.toml diff --git a/bin/update-contributors/src/main.rs b/crates/update-contributors/src/main.rs similarity index 100% rename from bin/update-contributors/src/main.rs rename to crates/update-contributors/src/main.rs diff --git a/justfile b/justfile index 660e3daecd..98fc4226e3 100755 --- a/justfile +++ b/justfile @@ -43,13 +43,7 @@ shellcheck: shellcheck www/install.sh man: - cargo build --features help4help2man - help2man \ - --name 'save and run commands' \ - --manual 'Just Manual' \ - --no-info \ - target/debug/just \ - > man/just.1 + cargo run -- --man > man/just.1 view-man: man man man/just.1 @@ -115,10 +109,6 @@ install-dev-deps: cargo install cargo-watch cargo install mdbook mdbook-linkcheck -# install system development dependencies with homebrew -install-dev-deps-homebrew: - brew install help2man - # everyone's favorite animate paper clip clippy: cargo clippy --all --all-targets --all-features diff --git a/man/just.1 b/man/just.1 index 5021ce574c..dca1230440 100644 --- a/man/just.1 +++ b/man/just.1 @@ -1,159 +1,168 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH JUST "1" "May 2024" "just 1.26.0" "Just Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.TH just 1 "just 1.26.0" .SH NAME -just \- save and run commands +just \- 🤖 Just a command runner \- https://github.com/casey/just +.SH SYNOPSIS +\fBjust\fR [\fB\-\-check\fR] [\fB\-\-chooser\fR] [\fB\-\-color\fR] [\fB\-\-command\-color\fR] [\fB\-\-yes\fR] [\fB\-n\fR|\fB\-\-dry\-run\fR] [\fB\-\-dump\-format\fR] [\fB\-\-highlight\fR] [\fB\-\-list\-heading\fR] [\fB\-\-list\-prefix\fR] [\fB\-\-no\-aliases\fR] [\fB\-\-no\-deps\fR] [\fB\-\-no\-dotenv\fR] [\fB\-\-no\-highlight\fR] [\fB\-f\fR|\fB\-\-justfile\fR] [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-\-set\fR] [\fB\-\-shell\fR] [\fB\-\-shell\-arg\fR] [\fB\-\-shell\-command\fR] [\fB\-\-clear\-shell\-args\fR] [\fB\-u\fR|\fB\-\-unsorted\fR] [\fB\-\-unstable\fR] [\fB\-v\fR|\fB\-\-verbose\fR]... [\fB\-d\fR|\fB\-\-working\-directory\fR] [\fB\-\-changelog\fR] [\fB\-\-choose\fR] [\fB\-c\fR|\fB\-\-command\fR] [\fB\-\-completions\fR] [\fB\-\-dump\fR] [\fB\-e\fR|\fB\-\-edit\fR] [\fB\-\-evaluate\fR] [\fB\-\-fmt\fR] [\fB\-\-init\fR] [\fB\-l\fR|\fB\-\-list\fR] [\fB\-\-man\fR] [\fB\-s\fR|\fB\-\-show\fR] [\fB\-\-summary\fR] [\fB\-\-variables\fR] [\fB\-\-dotenv\-filename\fR] [\fB\-E\fR|\fB\-\-dotenv\-path\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIARGUMENTS\fR] .SH DESCRIPTION -just 1.26.0 -\- Please see https://github.com/casey/just for more information. -.SS "USAGE:" -.IP -just [FLAGS] [OPTIONS] [\-\-] [ARGUMENTS]... -.SS "FLAGS:" -.TP -\fB\-\-changelog\fR -Print changelog +🤖 Just a command runner \- https://github.com/casey/just +.SH OPTIONS .TP \fB\-\-check\fR -Run `\-\-fmt` in 'check' mode. Exits with 0 if justfile is formatted -correctly. Exits with 1 and prints a diff if formatting is required. +Run `\-\-fmt` in \*(Aqcheck\*(Aq mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required. .TP -\fB\-\-choose\fR -Select one or more recipes to run using a binary chooser. If `\-\-chooser` is -not passed the chooser defaults to the value of $JUST_CHOOSER, falling back -to `fzf` +\fB\-\-chooser\fR +Override binary invoked by `\-\-choose` +.RS +May also be specified with the \fBJUST_CHOOSER\fR environment variable. +.RE +.TP +\fB\-\-color\fR [default: auto] +Print colorful output +.br + +.br +[\fIpossible values: \fRauto, always, never] +.TP +\fB\-\-command\-color\fR +Echo recipe lines in +.br + +.br +[\fIpossible values: \fRblack, blue, cyan, green, purple, red, yellow] .TP -\fB\-\-clear\-shell\-args\fR -Clear shell arguments +\fB\-\-yes\fR +Automatically confirm all recipes. .TP \fB\-n\fR, \fB\-\-dry\-run\fR Print what just would do without doing it .TP -\fB\-\-dump\fR -Print justfile -.TP -\fB\-e\fR, \fB\-\-edit\fR -Edit justfile with editor given by $VISUAL or $EDITOR, falling back to -`vim` -.TP -\fB\-\-evaluate\fR -Evaluate and print all variables. If a variable name is given as an -argument, only print that variable's value. -.TP -\fB\-\-fmt\fR -Format and overwrite justfile +\fB\-\-dump\-format\fR=\fIFORMAT\fR [default: just] +Dump justfile as +.br + +.br +[\fIpossible values: \fRjust, json] .TP \fB\-\-highlight\fR Highlight echoed recipe lines in bold .TP -\fB\-\-init\fR -Initialize new justfile in project root +\fB\-\-list\-heading\fR=\fITEXT\fR +Print before list .TP -\fB\-l\fR, \fB\-\-list\fR -List available recipes and their arguments +\fB\-\-list\-prefix\fR=\fITEXT\fR +Print before each list item .TP \fB\-\-no\-aliases\fR -Don't show aliases in list +Don\*(Aqt show aliases in list .TP \fB\-\-no\-deps\fR -Don't run recipe dependencies +Don\*(Aqt run recipe dependencies .TP \fB\-\-no\-dotenv\fR -Don't load `.env` file +Don\*(Aqt load `.env` file .TP \fB\-\-no\-highlight\fR -Don't highlight echoed recipe lines in bold +Don\*(Aqt highlight echoed recipe lines in bold +.TP +\fB\-f\fR, \fB\-\-justfile\fR +Use as justfile .TP \fB\-q\fR, \fB\-\-quiet\fR Suppress all output .TP +\fB\-\-set\fR=\fIVARIABLE VALUE\fR +Override with +.TP +\fB\-\-shell\fR +Invoke to run recipes +.TP +\fB\-\-shell\-arg\fR +Invoke shell with as an argument +.TP \fB\-\-shell\-command\fR Invoke with the shell used to run recipe lines and backticks .TP -\fB\-\-summary\fR -List names of available recipes +\fB\-\-clear\-shell\-args\fR +Clear shell arguments .TP \fB\-u\fR, \fB\-\-unsorted\fR Return list and summary entries in source order .TP \fB\-\-unstable\fR Enable unstable features -.TP -\fB\-\-variables\fR -List names of variables +.RS +May also be specified with the \fBJUST_UNSTABLE\fR environment variable. +.RE .TP \fB\-v\fR, \fB\-\-verbose\fR Use verbose output .TP -\fB\-\-yes\fR -Automatically confirm all recipes. +\fB\-d\fR, \fB\-\-working\-directory\fR +Use as working directory. \-\-justfile must also be set .TP -\fB\-h\fR, \fB\-\-help\fR -Print help information +\fB\-\-changelog\fR +Print changelog .TP -\fB\-V\fR, \fB\-\-version\fR -Print version information -.SS "OPTIONS:" +\fB\-\-choose\fR +Select one or more recipes to run using a binary chooser. If `\-\-chooser` is not passed the chooser defaults to the value of $JUST_CHOOSER, falling back to `fzf` .TP -\fB\-\-chooser\fR -Override binary invoked by `\-\-choose` -.HP -\fB\-\-color\fR -.TP -Print colorful output [default: auto] -[possible values: auto, always, never] -.HP -\fB\-c\fR, \fB\-\-command\fR -.IP +\fB\-c\fR, \fB\-\-command\fR Run an arbitrary command with the working directory, `.env`, overrides, and exports set -.HP -\fB\-\-command\-color\fR -.IP -Echo recipe lines in [possible values: black, blue, cyan, green, purple, red, -yellow] -.HP -\fB\-\-completions\fR -.IP -Print shell completion script for [possible values: zsh, bash, fish, powershell, -elvish] -.HP -\fB\-\-dotenv\-filename\fR -.IP -Search for environment file named instead of `.env` -.HP -\fB\-E\fR, \fB\-\-dotenv\-path\fR -.IP -Load as environment file instead of searching for one -.HP -\fB\-\-dump\-format\fR .TP -Dump justfile as [default: just] -[possible values: just, json] +\fB\-\-completions\fR=\fISHELL\fR +Print shell completion script for +.br + +.br +[\fIpossible values: \fRbash, elvish, fish, powershell, zsh] .TP -\fB\-f\fR, \fB\-\-justfile\fR -Use as justfile +\fB\-\-dump\fR +Print justfile .TP -\fB\-\-list\-heading\fR -Print before list +\fB\-e\fR, \fB\-\-edit\fR +Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim` .TP -\fB\-\-list\-prefix\fR -Print before each list item +\fB\-\-evaluate\fR +Evaluate and print all variables. If a variable name is given as an argument, only print that variable\*(Aqs value. .TP -\fB\-\-set\fR -Override with +\fB\-\-fmt\fR +Format and overwrite justfile .TP -\fB\-\-shell\fR -Invoke to run recipes +\fB\-\-init\fR +Initialize new justfile in project root .TP -\fB\-\-shell\-arg\fR ... -Invoke shell with as an argument +\fB\-l\fR, \fB\-\-list\fR +List available recipes and their arguments .TP -\fB\-s\fR, \fB\-\-show\fR +\fB\-\-man\fR +Print man page +.TP +\fB\-s\fR, \fB\-\-show\fR=\fIRECIPE\fR Show information about -.HP -\fB\-d\fR, \fB\-\-working\-directory\fR -.IP -Use as working directory. \fB\-\-justfile\fR must also be set -.SS "ARGS:" .TP -... +\fB\-\-summary\fR +List names of available recipes +.TP +\fB\-\-variables\fR +List names of variables +.TP +\fB\-\-dotenv\-filename\fR +Search for environment file named instead of `.env` +.TP +\fB\-E\fR, \fB\-\-dotenv\-path\fR +Load as environment file instead of searching for one +.TP +\fB\-h\fR, \fB\-\-help\fR +Print help +.TP +\fB\-V\fR, \fB\-\-version\fR +Print version +.TP +[\fIARGUMENTS\fR] Overrides and recipe(s) to run, defaulting to the first recipe in the justfile +.SH VERSION +v1.26.0 +.SH AUTHORS +Casey Rodarmor diff --git a/src/config.rs b/src/config.rs index 10a15caa8e..e47fe7f94d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -56,6 +56,7 @@ mod cmd { pub(crate) const FORMAT: &str = "FORMAT"; pub(crate) const INIT: &str = "INIT"; pub(crate) const LIST: &str = "LIST"; + pub(crate) const MAN: &str = "MAN"; pub(crate) const SHOW: &str = "SHOW"; pub(crate) const SUMMARY: &str = "SUMMARY"; pub(crate) const VARIABLES: &str = "VARIABLES"; @@ -71,13 +72,14 @@ mod cmd { FORMAT, INIT, LIST, + MAN, SHOW, SUMMARY, VARIABLES, ]; pub(crate) const ARGLESS: &[&str] = &[ - CHANGELOG, DUMP, EDIT, FORMAT, INIT, LIST, SUMMARY, VARIABLES, + CHANGELOG, DUMP, EDIT, FORMAT, INIT, LIST, MAN, SUMMARY, VARIABLES, ]; } @@ -140,8 +142,15 @@ mod arg { impl Config { pub(crate) fn app() -> Command { - let app = Command::new(env!("CARGO_PKG_NAME")) + Command::new(env!("CARGO_PKG_NAME")) .bin_name(env!("CARGO_PKG_NAME")) + .version(env!("CARGO_PKG_VERSION")) + .author(env!("CARGO_PKG_AUTHORS")) + .about(concat!( + env!("CARGO_PKG_DESCRIPTION"), + " - ", + env!("CARGO_PKG_HOMEPAGE") + )) .trailing_var_arg(true) .styles( Styles::styled() @@ -400,6 +409,12 @@ impl Config { .action(ArgAction::SetTrue) .help("List available recipes and their arguments"), ) + .arg( + Arg::new(cmd::MAN) + .long("man") + .action(ArgAction::SetTrue) + .help("Print man page"), + ) .arg( Arg::new(cmd::SHOW) .short('s') @@ -442,24 +457,7 @@ impl Config { .num_args(1..) .action(ArgAction::Append) .help("Overrides and recipe(s) to run, defaulting to the first recipe in the justfile"), - ); - - if cfg!(feature = "help4help2man") { - app.version(env!("CARGO_PKG_VERSION")).about(concat!( - "- Please see ", - env!("CARGO_PKG_HOMEPAGE"), - " for more information." - )) - } else { - app - .version(env!("CARGO_PKG_VERSION")) - .author(env!("CARGO_PKG_AUTHORS")) - .about(concat!( - env!("CARGO_PKG_DESCRIPTION"), - " - ", - env!("CARGO_PKG_HOMEPAGE") - )) - } + ) } fn color_from_matches(matches: &ArgMatches) -> ConfigResult { @@ -629,6 +627,8 @@ impl Config { Subcommand::Init } else if matches.get_flag(cmd::LIST) { Subcommand::List + } else if matches.get_flag(cmd::MAN) { + Subcommand::Man } else if let Some(name) = matches.get_one::(cmd::SHOW).map(Into::into) { Subcommand::Show { name } } else if matches.get_flag(cmd::EVALUATE) { diff --git a/src/error.rs b/src/error.rs index 9bcf2719df..e18d7e9c5a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -142,6 +142,9 @@ pub(crate) enum Error<'src> { line_number: Option, signal: i32, }, + StdoutIo { + io_error: io::Error, + }, TempdirIo { recipe: &'src str, io_error: io::Error, @@ -406,6 +409,9 @@ impl<'src> ColorDisplay for Error<'src> { write!(f, "Recipe `{recipe}` was terminated by signal {signal}")?; } } + StdoutIo { io_error } => { + write!(f, "I/O error writing to stdout: {io_error}?")?; + } TempdirIo { recipe, io_error } => { write!(f, "Recipe `{recipe}` could not be run because of an IO error while trying to create a temporary \ directory or write a file to that directory: {io_error}")?; diff --git a/src/subcommand.rs b/src/subcommand.rs index 30417e0229..3d0346b92b 100644 --- a/src/subcommand.rs +++ b/src/subcommand.rs @@ -1,5 +1,6 @@ use { super::*, + clap_mangen::Man, std::io::{Read, Seek}, tempfile::tempfile, }; @@ -30,6 +31,7 @@ pub(crate) enum Subcommand { Format, Init, List, + Man, Run { arguments: Vec, overrides: BTreeMap, @@ -56,6 +58,7 @@ impl Subcommand { } Completions { shell } => return Self::completions(*shell), Init => return Self::init(config), + Man => return Self::man(), Run { arguments, overrides, @@ -87,7 +90,7 @@ impl Subcommand { Show { ref name } => Self::show(config, name, justfile)?, Summary => Self::summary(config, justfile), Variables => Self::variables(justfile), - Changelog | Completions { .. } | Edit | Init | Run { .. } => unreachable!(), + Changelog | Completions { .. } | Edit | Init | Man | Run { .. } => unreachable!(), } Ok(()) @@ -440,6 +443,26 @@ impl Subcommand { } } + fn man() -> Result<(), Error<'static>> { + let mut buffer = Vec::::new(); + + Man::new(Config::app()) + .render(&mut buffer) + .expect("writing to buffer cannot fail"); + + let mut stdout = io::stdout().lock(); + + stdout + .write_all(&buffer) + .map_err(|io_error| Error::StdoutIo { io_error })?; + + stdout + .flush() + .map_err(|io_error| Error::StdoutIo { io_error })?; + + Ok(()) + } + fn list(config: &Config, level: usize, justfile: &Justfile) { const MAX_WIDTH: usize = 50; diff --git a/tests/lib.rs b/tests/lib.rs index abb3c46c76..26c9d12581 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -66,6 +66,7 @@ mod interrupts; mod invocation_directory; mod json; mod line_prefixes; +mod man; mod misc; mod modules; mod multibyte_char; diff --git a/tests/man.rs b/tests/man.rs new file mode 100644 index 0000000000..e957414c44 --- /dev/null +++ b/tests/man.rs @@ -0,0 +1,9 @@ +use super::*; + +#[test] +fn output() { + Test::new() + .arg("--man") + .stdout_regex("(?s).*.TH just 1.*") + .run(); +}