mirror of
https://github.com/ducaale/xh.git
synced 2025-05-05 15:32:50 +00:00
Switch to generating completions at runtime (#393)
* Allow for completion and man page generation at runtime and support for Elvish and Nshull * Refine runtime generation based on feedback provided
This commit is contained in:
parent
35dfe49761
commit
196ca1f162
@ -1,3 +1,8 @@
|
|||||||
|
## Unreleased
|
||||||
|
### Features
|
||||||
|
- Add `--generate` option to generate the man page and shell completions at runtime
|
||||||
|
- Add support for Elvish and Nushell shell completions
|
||||||
|
|
||||||
## [0.23.1] - 2025-01-02
|
## [0.23.1] - 2025-01-02
|
||||||
### Security fixes
|
### Security fixes
|
||||||
- Upgrade to ruzstd v0.7.3 to fix RUSTSEC-2024-0400, see #396 (@zuisong)
|
- Upgrade to ruzstd v0.7.3 to fix RUSTSEC-2024-0400, see #396 (@zuisong)
|
||||||
|
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -289,6 +289,16 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_complete_nushell"
|
||||||
|
version = "4.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "315902e790cc6e5ddd20cbd313c1d0d49db77f191e149f96397230fb82a17677"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"clap_complete",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.18"
|
version = "4.5.18"
|
||||||
@ -2798,6 +2808,7 @@ dependencies = [
|
|||||||
"chardetng",
|
"chardetng",
|
||||||
"clap",
|
"clap",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
|
"clap_complete_nushell",
|
||||||
"cookie_store 0.20.0",
|
"cookie_store 0.20.0",
|
||||||
"digest_auth",
|
"digest_auth",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
@ -19,7 +19,8 @@ anyhow = "1.0.38"
|
|||||||
brotli = { version = "3.3.0", default-features = false, features = ["std"] }
|
brotli = { version = "3.3.0", default-features = false, features = ["std"] }
|
||||||
chardetng = "0.1.15"
|
chardetng = "0.1.15"
|
||||||
clap = { version = "4.4", features = ["derive", "wrap_help", "string"] }
|
clap = { version = "4.4", features = ["derive", "wrap_help", "string"] }
|
||||||
clap_complete = { version = "4.4", optional = true }
|
clap_complete = "4.4"
|
||||||
|
clap_complete_nushell = "4.4"
|
||||||
cookie_store = { version = "0.20.0", features = ["preserve_order"] }
|
cookie_store = { version = "0.20.0", features = ["preserve_order"] }
|
||||||
digest_auth = "0.3.0"
|
digest_auth = "0.3.0"
|
||||||
dirs = "5.0"
|
dirs = "5.0"
|
||||||
@ -38,7 +39,7 @@ once_cell = "1.8.0"
|
|||||||
os_display = "0.1.3"
|
os_display = "0.1.3"
|
||||||
pem = "3.0"
|
pem = "3.0"
|
||||||
regex-lite = "0.1.5"
|
regex-lite = "0.1.5"
|
||||||
roff = { version = "0.2.1", optional = true }
|
roff = "0.2.1"
|
||||||
rpassword = "7.2.0"
|
rpassword = "7.2.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde-transcode = "1.1.1"
|
serde-transcode = "1.1.1"
|
||||||
@ -101,7 +102,6 @@ network-interface = ["dep:network-interface"]
|
|||||||
|
|
||||||
online-tests = []
|
online-tests = []
|
||||||
ipv6-tests = []
|
ipv6-tests = []
|
||||||
man-completion-gen = ["clap_complete", "roff"]
|
|
||||||
|
|
||||||
[package.metadata.cross.build.env]
|
[package.metadata.cross.build.env]
|
||||||
passthrough = ["CARGO_PROFILE_RELEASE_LTO"]
|
passthrough = ["CARGO_PROFILE_RELEASE_LTO"]
|
||||||
|
@ -4,9 +4,15 @@
|
|||||||
- Update `CHANGELOG.md` (rename unreleased header to the current date, add any missing changes).
|
- Update `CHANGELOG.md` (rename unreleased header to the current date, add any missing changes).
|
||||||
- Run `cargo update` to update dependencies.
|
- Run `cargo update` to update dependencies.
|
||||||
- Bump up the version in `Cargo.toml` and run `cargo check` to update `Cargo.lock`.
|
- Bump up the version in `Cargo.toml` and run `cargo check` to update `Cargo.lock`.
|
||||||
- Run the following to update man pages and shell-completion files.
|
- Run the following to update shell-completion files and man pages.
|
||||||
```sh
|
```sh
|
||||||
cargo run --all-features -- generate-completions completions && cargo run --all-features -- generate-manpages doc
|
cargo run --all-features -- --generate complete-bash > completions/xh.bash
|
||||||
|
cargo run --all-features -- --generate complete-elvish > completions/xh.elv
|
||||||
|
cargo run --all-features -- --generate complete-fish > completions/xh.fish
|
||||||
|
cargo run --all-features -- --generate complete-nushell > completions/xh.nu
|
||||||
|
cargo run --all-features -- --generate complete-powershell > completions/_xh.ps1
|
||||||
|
cargo run --all-features -- --generate complete-zsh > completions/_xh
|
||||||
|
cargo run --all-features -- --generate man > doc/xh.1
|
||||||
```
|
```
|
||||||
- Commit changes and push them to remote.
|
- Commit changes and push them to remote.
|
||||||
- Add git tag e.g `git tag v0.9.0`.
|
- Add git tag e.g `git tag v0.9.0`.
|
||||||
|
@ -49,6 +49,7 @@ none\:"Disable both coloring and formatting"))' \
|
|||||||
'--http-version=[HTTP version to use]:VERSION:(1.0 1.1 2 2-prior-knowledge)' \
|
'--http-version=[HTTP version to use]:VERSION:(1.0 1.1 2 2-prior-knowledge)' \
|
||||||
'*--resolve=[Override DNS resolution for specific domain to a custom IP]:HOST:ADDRESS:_default' \
|
'*--resolve=[Override DNS resolution for specific domain to a custom IP]:HOST:ADDRESS:_default' \
|
||||||
'--interface=[Bind to a network interface or local IP address]:NAME:_default' \
|
'--interface=[Bind to a network interface or local IP address]:NAME:_default' \
|
||||||
|
'--generate=[Generate shell completions or man pages]:KIND:(complete-bash complete-elvish complete-fish complete-nushell complete-powershell complete-zsh man)' \
|
||||||
'-j[(default) Serialize data items from the command line as a JSON object]' \
|
'-j[(default) Serialize data items from the command line as a JSON object]' \
|
||||||
'--json[(default) Serialize data items from the command line as a JSON object]' \
|
'--json[(default) Serialize data items from the command line as a JSON object]' \
|
||||||
'-f[Serialize data items from the command line as form fields]' \
|
'-f[Serialize data items from the command line as form fields]' \
|
||||||
@ -137,10 +138,11 @@ none\:"Disable both coloring and formatting"))' \
|
|||||||
'--no-ignore-stdin[]' \
|
'--no-ignore-stdin[]' \
|
||||||
'--no-curl[]' \
|
'--no-curl[]' \
|
||||||
'--no-curl-long[]' \
|
'--no-curl-long[]' \
|
||||||
|
'--no-generate[]' \
|
||||||
'--no-help[]' \
|
'--no-help[]' \
|
||||||
'-V[Print version]' \
|
'-V[Print version]' \
|
||||||
'--version[Print version]' \
|
'--version[Print version]' \
|
||||||
':raw_method_or_url -- The request URL, preceded by an optional HTTP method:_default' \
|
'::raw_method_or_url -- The request URL, preceded by an optional HTTP method:_default' \
|
||||||
'*::raw_rest_args -- Optional key-value pairs to be included in the request.:_default' \
|
'*::raw_rest_args -- Optional key-value pairs to be included in the request.:_default' \
|
||||||
&& ret=0
|
&& ret=0
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ Register-ArgumentCompleter -Native -CommandName 'xh' -ScriptBlock {
|
|||||||
[CompletionResult]::new('--http-version', '--http-version', [CompletionResultType]::ParameterName, 'HTTP version to use')
|
[CompletionResult]::new('--http-version', '--http-version', [CompletionResultType]::ParameterName, 'HTTP version to use')
|
||||||
[CompletionResult]::new('--resolve', '--resolve', [CompletionResultType]::ParameterName, 'Override DNS resolution for specific domain to a custom IP')
|
[CompletionResult]::new('--resolve', '--resolve', [CompletionResultType]::ParameterName, 'Override DNS resolution for specific domain to a custom IP')
|
||||||
[CompletionResult]::new('--interface', '--interface', [CompletionResultType]::ParameterName, 'Bind to a network interface or local IP address')
|
[CompletionResult]::new('--interface', '--interface', [CompletionResultType]::ParameterName, 'Bind to a network interface or local IP address')
|
||||||
|
[CompletionResult]::new('--generate', '--generate', [CompletionResultType]::ParameterName, 'Generate shell completions or man pages')
|
||||||
[CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, '(default) Serialize data items from the command line as a JSON object')
|
[CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, '(default) Serialize data items from the command line as a JSON object')
|
||||||
[CompletionResult]::new('--json', '--json', [CompletionResultType]::ParameterName, '(default) Serialize data items from the command line as a JSON object')
|
[CompletionResult]::new('--json', '--json', [CompletionResultType]::ParameterName, '(default) Serialize data items from the command line as a JSON object')
|
||||||
[CompletionResult]::new('-f', '-f', [CompletionResultType]::ParameterName, 'Serialize data items from the command line as form fields')
|
[CompletionResult]::new('-f', '-f', [CompletionResultType]::ParameterName, 'Serialize data items from the command line as form fields')
|
||||||
@ -140,6 +141,7 @@ Register-ArgumentCompleter -Native -CommandName 'xh' -ScriptBlock {
|
|||||||
[CompletionResult]::new('--no-ignore-stdin', '--no-ignore-stdin', [CompletionResultType]::ParameterName, 'no-ignore-stdin')
|
[CompletionResult]::new('--no-ignore-stdin', '--no-ignore-stdin', [CompletionResultType]::ParameterName, 'no-ignore-stdin')
|
||||||
[CompletionResult]::new('--no-curl', '--no-curl', [CompletionResultType]::ParameterName, 'no-curl')
|
[CompletionResult]::new('--no-curl', '--no-curl', [CompletionResultType]::ParameterName, 'no-curl')
|
||||||
[CompletionResult]::new('--no-curl-long', '--no-curl-long', [CompletionResultType]::ParameterName, 'no-curl-long')
|
[CompletionResult]::new('--no-curl-long', '--no-curl-long', [CompletionResultType]::ParameterName, 'no-curl-long')
|
||||||
|
[CompletionResult]::new('--no-generate', '--no-generate', [CompletionResultType]::ParameterName, 'no-generate')
|
||||||
[CompletionResult]::new('--no-help', '--no-help', [CompletionResultType]::ParameterName, 'no-help')
|
[CompletionResult]::new('--no-help', '--no-help', [CompletionResultType]::ParameterName, 'no-help')
|
||||||
[CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')
|
[CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||||
[CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')
|
[CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')
|
||||||
|
@ -19,7 +19,7 @@ _xh() {
|
|||||||
|
|
||||||
case "${cmd}" in
|
case "${cmd}" in
|
||||||
xh)
|
xh)
|
||||||
opts="-j -f -s -p -h -b -m -v -P -q -S -o -d -c -A -a -F -4 -6 -I -V --json --form --multipart --raw --pretty --format-options --style --response-charset --response-mime --print --headers --body --meta --verbose --debug --all --history-print --quiet --stream --output --download --continue --session --session-read-only --auth-type --auth --bearer --ignore-netrc --offline --check-status --follow --max-redirects --timeout --proxy --verify --cert --cert-key --ssl --native-tls --default-scheme --https --http-version --resolve --interface --ipv4 --ipv6 --ignore-stdin --curl --curl-long --help --no-json --no-form --no-multipart --no-raw --no-pretty --no-format-options --no-style --no-response-charset --no-response-mime --no-print --no-headers --no-body --no-meta --no-verbose --no-debug --no-all --no-history-print --no-quiet --no-stream --no-output --no-download --no-continue --no-session --no-session-read-only --no-auth-type --no-auth --no-bearer --no-ignore-netrc --no-offline --no-check-status --no-follow --no-max-redirects --no-timeout --no-proxy --no-verify --no-cert --no-cert-key --no-ssl --no-native-tls --no-default-scheme --no-https --no-http-version --no-resolve --no-interface --no-ipv4 --no-ipv6 --no-ignore-stdin --no-curl --no-curl-long --no-help --version <[METHOD] URL> [REQUEST_ITEM]..."
|
opts="-j -f -s -p -h -b -m -v -P -q -S -o -d -c -A -a -F -4 -6 -I -V --json --form --multipart --raw --pretty --format-options --style --response-charset --response-mime --print --headers --body --meta --verbose --debug --all --history-print --quiet --stream --output --download --continue --session --session-read-only --auth-type --auth --bearer --ignore-netrc --offline --check-status --follow --max-redirects --timeout --proxy --verify --cert --cert-key --ssl --native-tls --default-scheme --https --http-version --resolve --interface --ipv4 --ipv6 --ignore-stdin --curl --curl-long --generate --help --no-json --no-form --no-multipart --no-raw --no-pretty --no-format-options --no-style --no-response-charset --no-response-mime --no-print --no-headers --no-body --no-meta --no-verbose --no-debug --no-all --no-history-print --no-quiet --no-stream --no-output --no-download --no-continue --no-session --no-session-read-only --no-auth-type --no-auth --no-bearer --no-ignore-netrc --no-offline --no-check-status --no-follow --no-max-redirects --no-timeout --no-proxy --no-verify --no-cert --no-cert-key --no-ssl --no-native-tls --no-default-scheme --no-https --no-http-version --no-resolve --no-interface --no-ipv4 --no-ipv6 --no-ignore-stdin --no-curl --no-curl-long --no-generate --no-help --version [[METHOD] URL] [REQUEST_ITEM]..."
|
||||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||||
return 0
|
return 0
|
||||||
@ -149,6 +149,10 @@ _xh() {
|
|||||||
COMPREPLY=($(compgen -f "${cur}"))
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
--generate)
|
||||||
|
COMPREPLY=($(compgen -W "complete-bash complete-elvish complete-fish complete-nushell complete-powershell complete-zsh man" -- "${cur}"))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
;;
|
;;
|
||||||
|
148
completions/xh.elv
Normal file
148
completions/xh.elv
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
|
||||||
|
use builtin;
|
||||||
|
use str;
|
||||||
|
|
||||||
|
set edit:completion:arg-completer[xh] = {|@words|
|
||||||
|
fn spaces {|n|
|
||||||
|
builtin:repeat $n ' ' | str:join ''
|
||||||
|
}
|
||||||
|
fn cand {|text desc|
|
||||||
|
edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc
|
||||||
|
}
|
||||||
|
var command = 'xh'
|
||||||
|
for word $words[1..-1] {
|
||||||
|
if (str:has-prefix $word '-') {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
set command = $command';'$word
|
||||||
|
}
|
||||||
|
var completions = [
|
||||||
|
&'xh'= {
|
||||||
|
cand --raw 'Pass raw request data without extra processing'
|
||||||
|
cand --pretty 'Controls output processing'
|
||||||
|
cand --format-options 'Set output formatting options'
|
||||||
|
cand -s 'Output coloring style'
|
||||||
|
cand --style 'Output coloring style'
|
||||||
|
cand --response-charset 'Override the response encoding for terminal display purposes'
|
||||||
|
cand --response-mime 'Override the response mime type for coloring and formatting for the terminal'
|
||||||
|
cand -p 'String specifying what the output should contain'
|
||||||
|
cand --print 'String specifying what the output should contain'
|
||||||
|
cand -P 'The same as --print but applies only to intermediary requests/responses'
|
||||||
|
cand --history-print 'The same as --print but applies only to intermediary requests/responses'
|
||||||
|
cand -o 'Save output to FILE instead of stdout'
|
||||||
|
cand --output 'Save output to FILE instead of stdout'
|
||||||
|
cand --session 'Create, or reuse and update a session'
|
||||||
|
cand --session-read-only 'Create or read a session without updating it form the request/response exchange'
|
||||||
|
cand -A 'Specify the auth mechanism'
|
||||||
|
cand --auth-type 'Specify the auth mechanism'
|
||||||
|
cand -a 'Authenticate as USER with PASS (-A basic|digest) or with TOKEN (-A bearer)'
|
||||||
|
cand --auth 'Authenticate as USER with PASS (-A basic|digest) or with TOKEN (-A bearer)'
|
||||||
|
cand --bearer 'Authenticate with a bearer token'
|
||||||
|
cand --max-redirects 'Number of redirects to follow. Only respected if --follow is used'
|
||||||
|
cand --timeout 'Connection timeout of the request'
|
||||||
|
cand --proxy 'Use a proxy for a protocol. For example: --proxy https:http://proxy.host:8080'
|
||||||
|
cand --verify 'If "no", skip SSL verification. If a file path, use it as a CA bundle'
|
||||||
|
cand --cert 'Use a client side certificate for SSL'
|
||||||
|
cand --cert-key 'A private key file to use with --cert'
|
||||||
|
cand --ssl 'Force a particular TLS version'
|
||||||
|
cand --default-scheme 'The default scheme to use if not specified in the URL'
|
||||||
|
cand --http-version 'HTTP version to use'
|
||||||
|
cand --resolve 'Override DNS resolution for specific domain to a custom IP'
|
||||||
|
cand --interface 'Bind to a network interface or local IP address'
|
||||||
|
cand --generate 'Generate shell completions or man pages'
|
||||||
|
cand -j '(default) Serialize data items from the command line as a JSON object'
|
||||||
|
cand --json '(default) Serialize data items from the command line as a JSON object'
|
||||||
|
cand -f 'Serialize data items from the command line as form fields'
|
||||||
|
cand --form 'Serialize data items from the command line as form fields'
|
||||||
|
cand --multipart 'Like --form, but force a multipart/form-data request even without files'
|
||||||
|
cand -h 'Print only the response headers. Shortcut for --print=h'
|
||||||
|
cand --headers 'Print only the response headers. Shortcut for --print=h'
|
||||||
|
cand -b 'Print only the response body. Shortcut for --print=b'
|
||||||
|
cand --body 'Print only the response body. Shortcut for --print=b'
|
||||||
|
cand -m 'Print only the response metadata. Shortcut for --print=m'
|
||||||
|
cand --meta 'Print only the response metadata. Shortcut for --print=m'
|
||||||
|
cand -v 'Print the whole request as well as the response'
|
||||||
|
cand --verbose 'Print the whole request as well as the response'
|
||||||
|
cand --debug 'Print full error stack traces and debug log messages'
|
||||||
|
cand --all 'Show any intermediary requests/responses while following redirects with --follow'
|
||||||
|
cand -q 'Do not print to stdout or stderr'
|
||||||
|
cand --quiet 'Do not print to stdout or stderr'
|
||||||
|
cand -S 'Always stream the response body'
|
||||||
|
cand --stream 'Always stream the response body'
|
||||||
|
cand -d 'Download the body to a file instead of printing it'
|
||||||
|
cand --download 'Download the body to a file instead of printing it'
|
||||||
|
cand -c 'Resume an interrupted download. Requires --download and --output'
|
||||||
|
cand --continue 'Resume an interrupted download. Requires --download and --output'
|
||||||
|
cand --ignore-netrc 'Do not use credentials from .netrc'
|
||||||
|
cand --offline 'Construct HTTP requests without sending them anywhere'
|
||||||
|
cand --check-status '(default) Exit with an error status code if the server replies with an error'
|
||||||
|
cand -F 'Do follow redirects'
|
||||||
|
cand --follow 'Do follow redirects'
|
||||||
|
cand --native-tls 'Use the system TLS library instead of rustls (if enabled at compile time)'
|
||||||
|
cand --https 'Make HTTPS requests if not specified in the URL'
|
||||||
|
cand -4 'Resolve hostname to ipv4 addresses only'
|
||||||
|
cand --ipv4 'Resolve hostname to ipv4 addresses only'
|
||||||
|
cand -6 'Resolve hostname to ipv6 addresses only'
|
||||||
|
cand --ipv6 'Resolve hostname to ipv6 addresses only'
|
||||||
|
cand -I 'Do not attempt to read stdin'
|
||||||
|
cand --ignore-stdin 'Do not attempt to read stdin'
|
||||||
|
cand --curl 'Print a translation to a curl command'
|
||||||
|
cand --curl-long 'Use the long versions of curl''s flags'
|
||||||
|
cand --help 'Print help'
|
||||||
|
cand --no-json 'no-json'
|
||||||
|
cand --no-form 'no-form'
|
||||||
|
cand --no-multipart 'no-multipart'
|
||||||
|
cand --no-raw 'no-raw'
|
||||||
|
cand --no-pretty 'no-pretty'
|
||||||
|
cand --no-format-options 'no-format-options'
|
||||||
|
cand --no-style 'no-style'
|
||||||
|
cand --no-response-charset 'no-response-charset'
|
||||||
|
cand --no-response-mime 'no-response-mime'
|
||||||
|
cand --no-print 'no-print'
|
||||||
|
cand --no-headers 'no-headers'
|
||||||
|
cand --no-body 'no-body'
|
||||||
|
cand --no-meta 'no-meta'
|
||||||
|
cand --no-verbose 'no-verbose'
|
||||||
|
cand --no-debug 'no-debug'
|
||||||
|
cand --no-all 'no-all'
|
||||||
|
cand --no-history-print 'no-history-print'
|
||||||
|
cand --no-quiet 'no-quiet'
|
||||||
|
cand --no-stream 'no-stream'
|
||||||
|
cand --no-output 'no-output'
|
||||||
|
cand --no-download 'no-download'
|
||||||
|
cand --no-continue 'no-continue'
|
||||||
|
cand --no-session 'no-session'
|
||||||
|
cand --no-session-read-only 'no-session-read-only'
|
||||||
|
cand --no-auth-type 'no-auth-type'
|
||||||
|
cand --no-auth 'no-auth'
|
||||||
|
cand --no-bearer 'no-bearer'
|
||||||
|
cand --no-ignore-netrc 'no-ignore-netrc'
|
||||||
|
cand --no-offline 'no-offline'
|
||||||
|
cand --no-check-status 'no-check-status'
|
||||||
|
cand --no-follow 'no-follow'
|
||||||
|
cand --no-max-redirects 'no-max-redirects'
|
||||||
|
cand --no-timeout 'no-timeout'
|
||||||
|
cand --no-proxy 'no-proxy'
|
||||||
|
cand --no-verify 'no-verify'
|
||||||
|
cand --no-cert 'no-cert'
|
||||||
|
cand --no-cert-key 'no-cert-key'
|
||||||
|
cand --no-ssl 'no-ssl'
|
||||||
|
cand --no-native-tls 'no-native-tls'
|
||||||
|
cand --no-default-scheme 'no-default-scheme'
|
||||||
|
cand --no-https 'no-https'
|
||||||
|
cand --no-http-version 'no-http-version'
|
||||||
|
cand --no-resolve 'no-resolve'
|
||||||
|
cand --no-interface 'no-interface'
|
||||||
|
cand --no-ipv4 'no-ipv4'
|
||||||
|
cand --no-ipv6 'no-ipv6'
|
||||||
|
cand --no-ignore-stdin 'no-ignore-stdin'
|
||||||
|
cand --no-curl 'no-curl'
|
||||||
|
cand --no-curl-long 'no-curl-long'
|
||||||
|
cand --no-generate 'no-generate'
|
||||||
|
cand --no-help 'no-help'
|
||||||
|
cand -V 'Print version'
|
||||||
|
cand --version 'Print version'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
$completions[$command]
|
||||||
|
}
|
@ -23,6 +23,7 @@ complete -c xh -l default-scheme -d 'The default scheme to use if not specified
|
|||||||
complete -c xh -l http-version -d 'HTTP version to use' -r -f -a "{1.0\t'',1.1\t'',2\t'',2-prior-knowledge\t''}"
|
complete -c xh -l http-version -d 'HTTP version to use' -r -f -a "{1.0\t'',1.1\t'',2\t'',2-prior-knowledge\t''}"
|
||||||
complete -c xh -l resolve -d 'Override DNS resolution for specific domain to a custom IP' -r
|
complete -c xh -l resolve -d 'Override DNS resolution for specific domain to a custom IP' -r
|
||||||
complete -c xh -l interface -d 'Bind to a network interface or local IP address' -r
|
complete -c xh -l interface -d 'Bind to a network interface or local IP address' -r
|
||||||
|
complete -c xh -l generate -d 'Generate shell completions or man pages' -r -f -a "{complete-bash\t'',complete-elvish\t'',complete-fish\t'',complete-nushell\t'',complete-powershell\t'',complete-zsh\t'',man\t''}"
|
||||||
complete -c xh -s j -l json -d '(default) Serialize data items from the command line as a JSON object'
|
complete -c xh -s j -l json -d '(default) Serialize data items from the command line as a JSON object'
|
||||||
complete -c xh -s f -l form -d 'Serialize data items from the command line as form fields'
|
complete -c xh -s f -l form -d 'Serialize data items from the command line as form fields'
|
||||||
complete -c xh -l multipart -d 'Like --form, but force a multipart/form-data request even without files'
|
complete -c xh -l multipart -d 'Like --form, but force a multipart/form-data request even without files'
|
||||||
@ -97,5 +98,6 @@ complete -c xh -l no-ipv6
|
|||||||
complete -c xh -l no-ignore-stdin
|
complete -c xh -l no-ignore-stdin
|
||||||
complete -c xh -l no-curl
|
complete -c xh -l no-curl
|
||||||
complete -c xh -l no-curl-long
|
complete -c xh -l no-curl-long
|
||||||
|
complete -c xh -l no-generate
|
||||||
complete -c xh -l no-help
|
complete -c xh -l no-help
|
||||||
complete -c xh -s V -l version -d 'Print version'
|
complete -c xh -s V -l version -d 'Print version'
|
||||||
|
138
completions/xh.nu
Normal file
138
completions/xh.nu
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
module completions {
|
||||||
|
|
||||||
|
def "nu-complete xh pretty" [] {
|
||||||
|
[ "all" "colors" "format" "none" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
def "nu-complete xh style" [] {
|
||||||
|
[ "auto" "solarized" "monokai" "fruity" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
def "nu-complete xh auth_type" [] {
|
||||||
|
[ "basic" "bearer" "digest" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
def "nu-complete xh ssl" [] {
|
||||||
|
[ "auto" "tls1" "tls1.1" "tls1.2" "tls1.3" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
def "nu-complete xh http_version" [] {
|
||||||
|
[ "1.0" "1.1" "2" "2-prior-knowledge" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
def "nu-complete xh generate" [] {
|
||||||
|
[ "complete-bash" "complete-elvish" "complete-fish" "complete-nushell" "complete-powershell" "complete-zsh" "man" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# xh is a friendly and fast tool for sending HTTP requests
|
||||||
|
export extern xh [
|
||||||
|
--json(-j) # (default) Serialize data items from the command line as a JSON object
|
||||||
|
--form(-f) # Serialize data items from the command line as form fields
|
||||||
|
--multipart # Like --form, but force a multipart/form-data request even without files
|
||||||
|
--raw: string # Pass raw request data without extra processing
|
||||||
|
--pretty: string@"nu-complete xh pretty" # Controls output processing
|
||||||
|
--format-options: string # Set output formatting options
|
||||||
|
--style(-s): string@"nu-complete xh style" # Output coloring style
|
||||||
|
--response-charset: string # Override the response encoding for terminal display purposes
|
||||||
|
--response-mime: string # Override the response mime type for coloring and formatting for the terminal
|
||||||
|
--print(-p): string # String specifying what the output should contain
|
||||||
|
--headers(-h) # Print only the response headers. Shortcut for --print=h
|
||||||
|
--body(-b) # Print only the response body. Shortcut for --print=b
|
||||||
|
--meta(-m) # Print only the response metadata. Shortcut for --print=m
|
||||||
|
--verbose(-v) # Print the whole request as well as the response
|
||||||
|
--debug # Print full error stack traces and debug log messages
|
||||||
|
--all # Show any intermediary requests/responses while following redirects with --follow
|
||||||
|
--history-print(-P): string # The same as --print but applies only to intermediary requests/responses
|
||||||
|
--quiet(-q) # Do not print to stdout or stderr
|
||||||
|
--stream(-S) # Always stream the response body
|
||||||
|
--output(-o): string # Save output to FILE instead of stdout
|
||||||
|
--download(-d) # Download the body to a file instead of printing it
|
||||||
|
--continue(-c) # Resume an interrupted download. Requires --download and --output
|
||||||
|
--session: string # Create, or reuse and update a session
|
||||||
|
--session-read-only: string # Create or read a session without updating it form the request/response exchange
|
||||||
|
--auth-type(-A): string@"nu-complete xh auth_type" # Specify the auth mechanism
|
||||||
|
--auth(-a): string # Authenticate as USER with PASS (-A basic|digest) or with TOKEN (-A bearer)
|
||||||
|
--bearer: string # Authenticate with a bearer token
|
||||||
|
--ignore-netrc # Do not use credentials from .netrc
|
||||||
|
--offline # Construct HTTP requests without sending them anywhere
|
||||||
|
--check-status # (default) Exit with an error status code if the server replies with an error
|
||||||
|
--follow(-F) # Do follow redirects
|
||||||
|
--max-redirects: string # Number of redirects to follow. Only respected if --follow is used
|
||||||
|
--timeout: string # Connection timeout of the request
|
||||||
|
--proxy: string # Use a proxy for a protocol. For example: --proxy https:http://proxy.host:8080
|
||||||
|
--verify: string # If "no", skip SSL verification. If a file path, use it as a CA bundle
|
||||||
|
--cert: string # Use a client side certificate for SSL
|
||||||
|
--cert-key: string # A private key file to use with --cert
|
||||||
|
--ssl: string@"nu-complete xh ssl" # Force a particular TLS version
|
||||||
|
--native-tls # Use the system TLS library instead of rustls (if enabled at compile time)
|
||||||
|
--default-scheme: string # The default scheme to use if not specified in the URL
|
||||||
|
--https # Make HTTPS requests if not specified in the URL
|
||||||
|
--http-version: string@"nu-complete xh http_version" # HTTP version to use
|
||||||
|
--resolve: string # Override DNS resolution for specific domain to a custom IP
|
||||||
|
--interface: string # Bind to a network interface or local IP address
|
||||||
|
--ipv4(-4) # Resolve hostname to ipv4 addresses only
|
||||||
|
--ipv6(-6) # Resolve hostname to ipv6 addresses only
|
||||||
|
--ignore-stdin(-I) # Do not attempt to read stdin
|
||||||
|
--curl # Print a translation to a curl command
|
||||||
|
--curl-long # Use the long versions of curl's flags
|
||||||
|
--generate: string@"nu-complete xh generate" # Generate shell completions or man pages
|
||||||
|
--help # Print help
|
||||||
|
raw_method_or_url?: string # The request URL, preceded by an optional HTTP method
|
||||||
|
...raw_rest_args: string # Optional key-value pairs to be included in the request.
|
||||||
|
--no-json
|
||||||
|
--no-form
|
||||||
|
--no-multipart
|
||||||
|
--no-raw
|
||||||
|
--no-pretty
|
||||||
|
--no-format-options
|
||||||
|
--no-style
|
||||||
|
--no-response-charset
|
||||||
|
--no-response-mime
|
||||||
|
--no-print
|
||||||
|
--no-headers
|
||||||
|
--no-body
|
||||||
|
--no-meta
|
||||||
|
--no-verbose
|
||||||
|
--no-debug
|
||||||
|
--no-all
|
||||||
|
--no-history-print
|
||||||
|
--no-quiet
|
||||||
|
--no-stream
|
||||||
|
--no-output
|
||||||
|
--no-download
|
||||||
|
--no-continue
|
||||||
|
--no-session
|
||||||
|
--no-session-read-only
|
||||||
|
--no-auth-type
|
||||||
|
--no-auth
|
||||||
|
--no-bearer
|
||||||
|
--no-ignore-netrc
|
||||||
|
--no-offline
|
||||||
|
--no-check-status
|
||||||
|
--no-follow
|
||||||
|
--no-max-redirects
|
||||||
|
--no-timeout
|
||||||
|
--no-proxy
|
||||||
|
--no-verify
|
||||||
|
--no-cert
|
||||||
|
--no-cert-key
|
||||||
|
--no-ssl
|
||||||
|
--no-native-tls
|
||||||
|
--no-default-scheme
|
||||||
|
--no-https
|
||||||
|
--no-http-version
|
||||||
|
--no-resolve
|
||||||
|
--no-interface
|
||||||
|
--no-ipv4
|
||||||
|
--no-ipv6
|
||||||
|
--no-ignore-stdin
|
||||||
|
--no-curl
|
||||||
|
--no-curl-long
|
||||||
|
--no-generate
|
||||||
|
--no-help
|
||||||
|
--version(-V) # Print version
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export use completions *
|
7
doc/xh.1
7
doc/xh.1
@ -1,4 +1,4 @@
|
|||||||
.TH XH 1 2025-01-02 0.23.1 "User Commands"
|
.TH XH 1 2025-01-04 0.23.1 "User Commands"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
xh \- Friendly and fast tool for sending HTTP requests
|
xh \- Friendly and fast tool for sending HTTP requests
|
||||||
@ -320,6 +320,11 @@ For translating the other way, try https://curl2httpie.online/.
|
|||||||
\fB\-\-curl\-long\fR
|
\fB\-\-curl\-long\fR
|
||||||
Use the long versions of curl's flags.
|
Use the long versions of curl's flags.
|
||||||
.TP 4
|
.TP 4
|
||||||
|
\fB\-\-generate\fR=\fIKIND\fR
|
||||||
|
Generate shell completions or man pages.
|
||||||
|
|
||||||
|
[possible values: complete\-bash, complete\-elvish, complete\-fish, complete\-nushell, complete\-powershell, complete\-zsh, man]
|
||||||
|
.TP 4
|
||||||
\fB\-\-help\fR
|
\fB\-\-help\fR
|
||||||
Print help.
|
Print help.
|
||||||
.TP 4
|
.TP 4
|
||||||
|
296
src/cli.rs
296
src/cli.rs
@ -367,6 +367,27 @@ Example: --print=Hb"
|
|||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub curl_long: bool,
|
pub curl_long: bool,
|
||||||
|
|
||||||
|
/// Generate shell completions or man pages.
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
value_name = "KIND",
|
||||||
|
hide_possible_values = true,
|
||||||
|
long_help = "\
|
||||||
|
Generate shell completions or man pages. Possible values are:
|
||||||
|
|
||||||
|
complete-bash
|
||||||
|
complete-elvish
|
||||||
|
complete-fish
|
||||||
|
complete-nushell
|
||||||
|
complete-powershell
|
||||||
|
complete-zsh
|
||||||
|
man
|
||||||
|
|
||||||
|
Example: xh --generate=complete-bash > xh.bash",
|
||||||
|
conflicts_with = "raw_method_or_url"
|
||||||
|
)]
|
||||||
|
pub generate: Option<Generate>,
|
||||||
|
|
||||||
/// Print help.
|
/// Print help.
|
||||||
#[clap(long, action = ArgAction::HelpShort)]
|
#[clap(long, action = ArgAction::HelpShort)]
|
||||||
pub help: Option<bool>,
|
pub help: Option<bool>,
|
||||||
@ -381,8 +402,8 @@ Example: --print=Hb"
|
|||||||
///
|
///
|
||||||
/// A leading colon works as shorthand for localhost. ":8000" is equivalent
|
/// A leading colon works as shorthand for localhost. ":8000" is equivalent
|
||||||
/// to "localhost:8000", and ":/path" is equivalent to "localhost/path".
|
/// to "localhost:8000", and ":/path" is equivalent to "localhost/path".
|
||||||
#[clap(value_name = "[METHOD] URL")]
|
#[clap(value_name = "[METHOD] URL", required = true)]
|
||||||
raw_method_or_url: String,
|
raw_method_or_url: Option<String>,
|
||||||
|
|
||||||
/// Optional key-value pairs to be included in the request.
|
/// Optional key-value pairs to be included in the request.
|
||||||
///
|
///
|
||||||
@ -480,21 +501,28 @@ impl Cli {
|
|||||||
let matches = app.try_get_matches_from_mut(iter)?;
|
let matches = app.try_get_matches_from_mut(iter)?;
|
||||||
let mut cli = Self::from_arg_matches(&matches)?;
|
let mut cli = Self::from_arg_matches(&matches)?;
|
||||||
|
|
||||||
match cli.raw_method_or_url.as_str() {
|
app.get_bin_name()
|
||||||
"help" => {
|
.and_then(|name| name.split('.').next())
|
||||||
// opt-out of clap's auto-generated possible values help for --pretty
|
.unwrap_or("xh")
|
||||||
// as we already list them in the long_help
|
.clone_into(&mut cli.bin_name);
|
||||||
app = app.mut_arg("pretty", |a| a.hide_possible_values(true));
|
|
||||||
|
|
||||||
app.print_long_help().unwrap();
|
if cli.generate.is_some() {
|
||||||
safe_exit();
|
return Ok(cli);
|
||||||
}
|
|
||||||
"generate-completions" => return Err(generate_completions(app, cli.raw_rest_args)),
|
|
||||||
"generate-manpages" => return Err(generate_manpages(app, cli.raw_rest_args)),
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut raw_method_or_url = cli.raw_method_or_url.clone().unwrap();
|
||||||
|
|
||||||
|
if raw_method_or_url == "help" {
|
||||||
|
// opt-out of clap's auto-generated possible values help for --pretty
|
||||||
|
// as we already list them in the long_help
|
||||||
|
app = app.mut_arg("pretty", |a| a.hide_possible_values(true));
|
||||||
|
|
||||||
|
app.print_long_help().unwrap();
|
||||||
|
safe_exit();
|
||||||
|
}
|
||||||
|
|
||||||
let mut rest_args = mem::take(&mut cli.raw_rest_args).into_iter();
|
let mut rest_args = mem::take(&mut cli.raw_rest_args).into_iter();
|
||||||
let raw_url = match parse_method(&cli.raw_method_or_url) {
|
let raw_url = match parse_method(&raw_method_or_url) {
|
||||||
Some(method) => {
|
Some(method) => {
|
||||||
cli.method = Some(method);
|
cli.method = Some(method);
|
||||||
rest_args.next().ok_or_else(|| {
|
rest_args.next().ok_or_else(|| {
|
||||||
@ -506,7 +534,7 @@ impl Cli {
|
|||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
cli.method = None;
|
cli.method = None;
|
||||||
mem::take(&mut cli.raw_method_or_url)
|
mem::take(&mut raw_method_or_url)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
for request_item in rest_args {
|
for request_item in rest_args {
|
||||||
@ -517,11 +545,6 @@ impl Cli {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get_bin_name()
|
|
||||||
.and_then(|name| name.split('.').next())
|
|
||||||
.unwrap_or("xh")
|
|
||||||
.clone_into(&mut cli.bin_name);
|
|
||||||
|
|
||||||
if matches!(cli.bin_name.as_str(), "https" | "xhs" | "xhttps") {
|
if matches!(cli.bin_name.as_str(), "https" | "xhs" | "xhttps") {
|
||||||
cli.https = true;
|
cli.https = true;
|
||||||
}
|
}
|
||||||
@ -625,9 +648,12 @@ impl Cli {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
app.args(negations)
|
let mut app = app.args(negations)
|
||||||
.after_help(format!("Each option can be reset with a --no-OPTION argument.\n\nRun \"{} help\" for more complete documentation.", env!("CARGO_PKG_NAME")))
|
.after_help(format!("Each option can be reset with a --no-OPTION argument.\n\nRun \"{} help\" for more complete documentation.", env!("CARGO_PKG_NAME")))
|
||||||
.after_long_help("Each option can be reset with a --no-OPTION argument.")
|
.after_long_help("Each option can be reset with a --no-OPTION argument.");
|
||||||
|
|
||||||
|
app.build();
|
||||||
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn logger_config(&self) -> env_logger::Builder {
|
pub fn logger_config(&self) -> env_logger::Builder {
|
||||||
@ -744,209 +770,6 @@ fn construct_url(
|
|||||||
Ok(url)
|
Ok(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "man-completion-gen")]
|
|
||||||
// This signature is a little weird: we either return an error or don't return at all
|
|
||||||
fn generate_completions(mut app: clap::Command, rest_args: Vec<String>) -> clap::error::Error {
|
|
||||||
let bin_name = app.get_bin_name().unwrap().to_string();
|
|
||||||
if rest_args.len() != 1 {
|
|
||||||
return app.error(
|
|
||||||
clap::error::ErrorKind::WrongNumberOfValues,
|
|
||||||
"Usage: xh generate-completions <DIRECTORY>",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for &shell in clap_complete::Shell::value_variants() {
|
|
||||||
// Elvish complains about multiple deprecations and these don't seem to work
|
|
||||||
if shell != clap_complete::Shell::Elvish {
|
|
||||||
clap_complete::generate_to(shell, &mut app, &bin_name, &rest_args[0]).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
safe_exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "man-completion-gen")]
|
|
||||||
fn generate_manpages(mut app: clap::Command, rest_args: Vec<String>) -> clap::error::Error {
|
|
||||||
use roff::{bold, italic, roman, Roff};
|
|
||||||
use time::OffsetDateTime as DateTime;
|
|
||||||
|
|
||||||
if rest_args.len() != 1 {
|
|
||||||
return app.error(
|
|
||||||
clap::error::ErrorKind::WrongNumberOfValues,
|
|
||||||
"Usage: xh generate-manpages <DIRECTORY>",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let items: Vec<_> = app.get_arguments().filter(|i| !i.is_hide_set()).collect();
|
|
||||||
|
|
||||||
let mut request_items_roff = Roff::new();
|
|
||||||
let request_items = items
|
|
||||||
.iter()
|
|
||||||
.find(|opt| opt.get_id() == "raw_rest_args")
|
|
||||||
.unwrap();
|
|
||||||
let request_items_help = request_items
|
|
||||||
.get_long_help()
|
|
||||||
.or_else(|| request_items.get_help())
|
|
||||||
.expect("request_items is missing help")
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
// replace the indents in request_item help with proper roff controls
|
|
||||||
// For example:
|
|
||||||
//
|
|
||||||
// ```
|
|
||||||
// normal help normal help
|
|
||||||
// normal help normal help
|
|
||||||
//
|
|
||||||
// request-item-1
|
|
||||||
// help help
|
|
||||||
//
|
|
||||||
// request-item-2
|
|
||||||
// help help
|
|
||||||
//
|
|
||||||
// normal help normal help
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// Should look like this with roff controls
|
|
||||||
//
|
|
||||||
// ```
|
|
||||||
// normal help normal help
|
|
||||||
// normal help normal help
|
|
||||||
// .RS 12
|
|
||||||
// .TP
|
|
||||||
// request-item-1
|
|
||||||
// help help
|
|
||||||
// .TP
|
|
||||||
// request-item-2
|
|
||||||
// help help
|
|
||||||
// .RE
|
|
||||||
//
|
|
||||||
// .RS
|
|
||||||
// normal help normal help
|
|
||||||
// .RE
|
|
||||||
// ```
|
|
||||||
let lines: Vec<&str> = request_items_help.lines().collect();
|
|
||||||
let mut rs = false;
|
|
||||||
for i in 0..lines.len() {
|
|
||||||
if lines[i].is_empty() {
|
|
||||||
let prev = lines[i - 1].chars().take_while(|&x| x == ' ').count();
|
|
||||||
let next = lines[i + 1].chars().take_while(|&x| x == ' ').count();
|
|
||||||
if prev != next && next > 0 {
|
|
||||||
if !rs {
|
|
||||||
request_items_roff.control("RS", ["8"]);
|
|
||||||
rs = true;
|
|
||||||
}
|
|
||||||
request_items_roff.control("TP", ["4"]);
|
|
||||||
} else if prev != next && next == 0 {
|
|
||||||
request_items_roff.control("RE", []);
|
|
||||||
request_items_roff.text(vec![roman("")]);
|
|
||||||
request_items_roff.control("RS", []);
|
|
||||||
} else {
|
|
||||||
request_items_roff.text(vec![roman(lines[i])]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
request_items_roff.text(vec![roman(lines[i].trim())]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request_items_roff.control("RE", []);
|
|
||||||
|
|
||||||
let mut options_roff = Roff::new();
|
|
||||||
let non_pos_items = items
|
|
||||||
.iter()
|
|
||||||
.filter(|a| !a.is_positional())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
for opt in non_pos_items {
|
|
||||||
let mut header = vec![];
|
|
||||||
if let Some(short) = opt.get_short() {
|
|
||||||
header.push(bold(format!("-{}", short)));
|
|
||||||
}
|
|
||||||
if let Some(long) = opt.get_long() {
|
|
||||||
if !header.is_empty() {
|
|
||||||
header.push(roman(", "));
|
|
||||||
}
|
|
||||||
header.push(bold(format!("--{}", long)));
|
|
||||||
}
|
|
||||||
if opt.get_action().takes_values() {
|
|
||||||
let value_name = &opt.get_value_names().unwrap();
|
|
||||||
if opt.get_long().is_some() {
|
|
||||||
header.push(roman("="));
|
|
||||||
} else {
|
|
||||||
header.push(roman(" "));
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.get_id() == "auth" {
|
|
||||||
header.push(italic("USER"));
|
|
||||||
header.push(roman("["));
|
|
||||||
header.push(italic(":PASS"));
|
|
||||||
header.push(roman("] | "));
|
|
||||||
header.push(italic("TOKEN"));
|
|
||||||
} else {
|
|
||||||
header.push(italic(value_name.join(" ")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut body = vec![];
|
|
||||||
|
|
||||||
let mut help = opt
|
|
||||||
.get_long_help()
|
|
||||||
.or_else(|| opt.get_help())
|
|
||||||
.expect("option is missing help")
|
|
||||||
.to_string();
|
|
||||||
if !help.ends_with('.') {
|
|
||||||
help.push('.')
|
|
||||||
}
|
|
||||||
body.push(roman(help));
|
|
||||||
|
|
||||||
let possible_values = opt.get_possible_values();
|
|
||||||
if !possible_values.is_empty()
|
|
||||||
&& !opt.is_hide_possible_values_set()
|
|
||||||
&& opt.get_id() != "pretty"
|
|
||||||
{
|
|
||||||
let possible_values_text = format!(
|
|
||||||
"\n\n[possible values: {}]",
|
|
||||||
possible_values
|
|
||||||
.iter()
|
|
||||||
.map(|v| v.get_name())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ")
|
|
||||||
);
|
|
||||||
body.push(roman(possible_values_text));
|
|
||||||
}
|
|
||||||
options_roff.control("TP", ["4"]);
|
|
||||||
options_roff.text(header);
|
|
||||||
options_roff.text(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut manpage = fs::read_to_string(format!("{}/man-template.roff", rest_args[0])).unwrap();
|
|
||||||
|
|
||||||
let current_date = {
|
|
||||||
let (year, month, day) = DateTime::now_utc().date().to_calendar_date();
|
|
||||||
format!("{}-{:02}-{:02}", year, u8::from(month), day)
|
|
||||||
};
|
|
||||||
|
|
||||||
manpage = manpage.replace("{{date}}", ¤t_date);
|
|
||||||
manpage = manpage.replace("{{version}}", app.get_version().unwrap());
|
|
||||||
manpage = manpage.replace("{{request_items}}", request_items_roff.to_roff().trim());
|
|
||||||
manpage = manpage.replace("{{options}}", options_roff.to_roff().trim());
|
|
||||||
|
|
||||||
fs::write(format!("{}/xh.1", rest_args[0]), manpage).unwrap();
|
|
||||||
safe_exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "man-completion-gen"))]
|
|
||||||
fn generate_completions(mut _app: clap::Command, _rest_args: Vec<String>) -> clap::error::Error {
|
|
||||||
clap::Error::raw(
|
|
||||||
clap::error::ErrorKind::InvalidSubcommand,
|
|
||||||
"generate-completions requires enabling man-completion-gen feature\n",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "man-completion-gen"))]
|
|
||||||
fn generate_manpages(mut _app: clap::Command, _rest_args: Vec<String>) -> clap::error::Error {
|
|
||||||
clap::Error::raw(
|
|
||||||
clap::error::ErrorKind::InvalidSubcommand,
|
|
||||||
"generate-manpages requires enabling man-completion-gen feature\n",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, ValueEnum, Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Default, ValueEnum, Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum AuthType {
|
pub enum AuthType {
|
||||||
#[default]
|
#[default]
|
||||||
@ -1353,6 +1176,17 @@ pub enum HttpVersion {
|
|||||||
Http2PriorKnowledge,
|
Http2PriorKnowledge,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Generate {
|
||||||
|
CompleteBash,
|
||||||
|
CompleteElvish,
|
||||||
|
CompleteFish,
|
||||||
|
CompleteNushell,
|
||||||
|
CompletePowershell,
|
||||||
|
CompleteZsh,
|
||||||
|
Man,
|
||||||
|
}
|
||||||
|
|
||||||
/// HTTPie uses Python's str.decode(). That one's very accepting of different spellings.
|
/// HTTPie uses Python's str.decode(). That one's very accepting of different spellings.
|
||||||
/// encoding_rs is not.
|
/// encoding_rs is not.
|
||||||
///
|
///
|
||||||
@ -1870,4 +1704,16 @@ mod tests {
|
|||||||
assert!(Resolve::from_str("example.com:::1").is_ok());
|
assert!(Resolve::from_str("example.com:::1").is_ok());
|
||||||
assert!(Resolve::from_str("example.com:[::1]").is_ok());
|
assert!(Resolve::from_str("example.com:[::1]").is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generate() {
|
||||||
|
let cli = parse(["--generate", "complete-bash"]).unwrap();
|
||||||
|
assert_eq!(cli.generate, Some(Generate::CompleteBash));
|
||||||
|
assert_eq!(cli.raw_method_or_url, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generate_with_url() {
|
||||||
|
parse(["--generate", "complete-zsh", "example.org"]).unwrap_err();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
195
src/generation.rs
Normal file
195
src/generation.rs
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
use std::io;
|
||||||
|
|
||||||
|
use clap_complete::Shell;
|
||||||
|
use clap_complete_nushell::Nushell;
|
||||||
|
|
||||||
|
use crate::cli::Cli;
|
||||||
|
use crate::cli::Generate;
|
||||||
|
|
||||||
|
const MAN_TEMPLATE: &str = include_str!("../doc/man-template.roff");
|
||||||
|
|
||||||
|
pub fn generate(bin_name: &str, generate: Generate) {
|
||||||
|
let mut app = Cli::into_app();
|
||||||
|
|
||||||
|
match generate {
|
||||||
|
Generate::CompleteBash => {
|
||||||
|
clap_complete::generate(Shell::Bash, &mut app, bin_name, &mut io::stdout());
|
||||||
|
}
|
||||||
|
Generate::CompleteElvish => {
|
||||||
|
clap_complete::generate(Shell::Elvish, &mut app, bin_name, &mut io::stdout());
|
||||||
|
}
|
||||||
|
Generate::CompleteFish => {
|
||||||
|
clap_complete::generate(Shell::Fish, &mut app, bin_name, &mut io::stdout());
|
||||||
|
}
|
||||||
|
Generate::CompleteNushell => {
|
||||||
|
clap_complete::generate(Nushell, &mut app, bin_name, &mut io::stdout());
|
||||||
|
}
|
||||||
|
Generate::CompletePowershell => {
|
||||||
|
clap_complete::generate(Shell::PowerShell, &mut app, bin_name, &mut io::stdout());
|
||||||
|
}
|
||||||
|
Generate::CompleteZsh => {
|
||||||
|
clap_complete::generate(Shell::Zsh, &mut app, bin_name, &mut io::stdout());
|
||||||
|
}
|
||||||
|
Generate::Man => {
|
||||||
|
generate_manpages(&mut app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_manpages(app: &mut clap::Command) {
|
||||||
|
use roff::{bold, italic, roman, Roff};
|
||||||
|
use time::OffsetDateTime as DateTime;
|
||||||
|
|
||||||
|
let items: Vec<_> = app.get_arguments().filter(|i| !i.is_hide_set()).collect();
|
||||||
|
|
||||||
|
let mut request_items_roff = Roff::new();
|
||||||
|
let request_items = items
|
||||||
|
.iter()
|
||||||
|
.find(|opt| opt.get_id() == "raw_rest_args")
|
||||||
|
.unwrap();
|
||||||
|
let request_items_help = request_items
|
||||||
|
.get_long_help()
|
||||||
|
.or_else(|| request_items.get_help())
|
||||||
|
.expect("request_items is missing help")
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
// replace the indents in request_item help with proper roff controls
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// normal help normal help
|
||||||
|
// normal help normal help
|
||||||
|
//
|
||||||
|
// request-item-1
|
||||||
|
// help help
|
||||||
|
//
|
||||||
|
// request-item-2
|
||||||
|
// help help
|
||||||
|
//
|
||||||
|
// normal help normal help
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// Should look like this with roff controls
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// normal help normal help
|
||||||
|
// normal help normal help
|
||||||
|
// .RS 12
|
||||||
|
// .TP
|
||||||
|
// request-item-1
|
||||||
|
// help help
|
||||||
|
// .TP
|
||||||
|
// request-item-2
|
||||||
|
// help help
|
||||||
|
// .RE
|
||||||
|
//
|
||||||
|
// .RS
|
||||||
|
// normal help normal help
|
||||||
|
// .RE
|
||||||
|
// ```
|
||||||
|
let lines: Vec<&str> = request_items_help.lines().collect();
|
||||||
|
let mut rs = false;
|
||||||
|
for i in 0..lines.len() {
|
||||||
|
if lines[i].is_empty() {
|
||||||
|
let prev = lines[i - 1].chars().take_while(|&x| x == ' ').count();
|
||||||
|
let next = lines[i + 1].chars().take_while(|&x| x == ' ').count();
|
||||||
|
if prev != next && next > 0 {
|
||||||
|
if !rs {
|
||||||
|
request_items_roff.control("RS", ["8"]);
|
||||||
|
rs = true;
|
||||||
|
}
|
||||||
|
request_items_roff.control("TP", ["4"]);
|
||||||
|
} else if prev != next && next == 0 {
|
||||||
|
request_items_roff.control("RE", []);
|
||||||
|
request_items_roff.text(vec![roman("")]);
|
||||||
|
request_items_roff.control("RS", []);
|
||||||
|
} else {
|
||||||
|
request_items_roff.text(vec![roman(lines[i])]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
request_items_roff.text(vec![roman(lines[i].trim())]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
request_items_roff.control("RE", []);
|
||||||
|
|
||||||
|
let mut options_roff = Roff::new();
|
||||||
|
let non_pos_items = items
|
||||||
|
.iter()
|
||||||
|
.filter(|a| !a.is_positional())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for opt in non_pos_items {
|
||||||
|
let mut header = vec![];
|
||||||
|
if let Some(short) = opt.get_short() {
|
||||||
|
header.push(bold(format!("-{}", short)));
|
||||||
|
}
|
||||||
|
if let Some(long) = opt.get_long() {
|
||||||
|
if !header.is_empty() {
|
||||||
|
header.push(roman(", "));
|
||||||
|
}
|
||||||
|
header.push(bold(format!("--{}", long)));
|
||||||
|
}
|
||||||
|
if opt.get_action().takes_values() {
|
||||||
|
let value_name = &opt.get_value_names().unwrap();
|
||||||
|
if opt.get_long().is_some() {
|
||||||
|
header.push(roman("="));
|
||||||
|
} else {
|
||||||
|
header.push(roman(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.get_id() == "auth" {
|
||||||
|
header.push(italic("USER"));
|
||||||
|
header.push(roman("["));
|
||||||
|
header.push(italic(":PASS"));
|
||||||
|
header.push(roman("] | "));
|
||||||
|
header.push(italic("TOKEN"));
|
||||||
|
} else {
|
||||||
|
header.push(italic(value_name.join(" ")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut body = vec![];
|
||||||
|
|
||||||
|
let mut help = opt
|
||||||
|
.get_long_help()
|
||||||
|
.or_else(|| opt.get_help())
|
||||||
|
.expect("option is missing help")
|
||||||
|
.to_string();
|
||||||
|
if !help.ends_with('.') {
|
||||||
|
help.push('.')
|
||||||
|
}
|
||||||
|
body.push(roman(help));
|
||||||
|
|
||||||
|
let possible_values = opt.get_possible_values();
|
||||||
|
if !possible_values.is_empty()
|
||||||
|
&& !opt.is_hide_possible_values_set()
|
||||||
|
&& opt.get_id() != "pretty"
|
||||||
|
{
|
||||||
|
let possible_values_text = format!(
|
||||||
|
"\n\n[possible values: {}]",
|
||||||
|
possible_values
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.get_name())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
);
|
||||||
|
body.push(roman(possible_values_text));
|
||||||
|
}
|
||||||
|
options_roff.control("TP", ["4"]);
|
||||||
|
options_roff.text(header);
|
||||||
|
options_roff.text(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut manpage = MAN_TEMPLATE.to_string();
|
||||||
|
|
||||||
|
let current_date = {
|
||||||
|
let (year, month, day) = DateTime::now_utc().date().to_calendar_date();
|
||||||
|
format!("{}-{:02}-{:02}", year, u8::from(month), day)
|
||||||
|
};
|
||||||
|
|
||||||
|
manpage = manpage.replace("{{date}}", ¤t_date);
|
||||||
|
manpage = manpage.replace("{{version}}", app.get_version().unwrap());
|
||||||
|
manpage = manpage.replace("{{request_items}}", request_items_roff.to_roff().trim());
|
||||||
|
manpage = manpage.replace("{{options}}", options_roff.to_roff().trim());
|
||||||
|
|
||||||
|
print!("{manpage}");
|
||||||
|
}
|
@ -5,6 +5,7 @@ mod cli;
|
|||||||
mod decoder;
|
mod decoder;
|
||||||
mod download;
|
mod download;
|
||||||
mod formatting;
|
mod formatting;
|
||||||
|
mod generation;
|
||||||
mod middleware;
|
mod middleware;
|
||||||
mod nested_json;
|
mod nested_json;
|
||||||
mod netrc;
|
mod netrc;
|
||||||
@ -101,6 +102,11 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(args: Cli) -> Result<i32> {
|
fn run(args: Cli) -> Result<i32> {
|
||||||
|
if let Some(generate) = args.generate {
|
||||||
|
generation::generate(&args.bin_name, generate);
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
if args.curl {
|
if args.curl {
|
||||||
to_curl::print_curl_translation(args)?;
|
to_curl::print_curl_translation(args)?;
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user