Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 80 additions & 62 deletions cli/assets/fig.ts

Large diffs are not rendered by default.

276 changes: 178 additions & 98 deletions cli/assets/usage.1

Large diffs are not rendered by default.

14 changes: 8 additions & 6 deletions cli/src/cli/complete_word.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,26 +55,28 @@ fn render_completer_run(run: &str, ctx: &tera::Context) -> tera::TeraResult<Stri

/// Generate shell completion candidates for a partial command line
///
/// This is used internally by shell completion scripts to provide
/// intelligent completions for commands, flags, and arguments.
/// What the scripts from `usage generate completion` run on every Tab: they pass the words
/// typed so far and read back one candidate per line. Useful by hand, too, for checking what
/// a spec offers at a given point without installing anything.
#[derive(Debug, Args)]
#[usage(alias = "cw", effect = "read")]
pub struct CompleteWord {
/// User's input from the command line
/// The words typed so far, starting with the program name
words: Vec<String>,

/// Usage spec file or script with usage shebang, use "-" to read from stdin
/// A usage spec file, or a script with a usage shebang; "-" reads stdin
#[usage(short, long)]
file: Option<PathBuf>,

/// Raw string spec input
/// The spec itself, as a string, instead of a file
#[usage(short, long, required_unless = "--file", overrides = "--file")]
spec: Option<String>,

/// Current word index
/// Index of the word being completed; defaults to the last word
#[usage(long)]
cword: Option<usize>,

/// The shell the candidates are for, which decides how they are quoted
#[usage(
long,
default = "bash",
Expand Down
21 changes: 15 additions & 6 deletions cli/src/cli/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,33 @@ use usage::Spec;

use crate::env;

/// Execute a script, parsing args and exposing them as environment variables
/// Run a script through any interpreter, with its parsed arguments as environment variables
///
/// For scripts in a language `usage` has no dedicated command for. A shebang of
/// `#!/usr/bin/env -S usage exec node` parses the arguments against the `USAGE` comments at the
/// top of the file, then runs `node <script> <args>` with each flag and argument exported as
/// `usage_<name>`. When a file named `.<script>.usage.kdl` sits beside the script, the spec is
/// read from it instead of from the comments.
///
/// `-h` and `--help` belong to the script once one is named, so they print its help page
/// rather than this one. Asked with no script to describe, they print this page.
#[derive(Debug, Args)]
// The words after the script are the script's, so a flag `usage` does not know is a value to
// forward rather than a mistake to report — the root's `error` stops here.
#[usage(alias = "x", unknown_flags = "value")]
pub struct Exec {
/// command to execute after parsing usage spec
/// The interpreter to run the script with, such as `node` or `python3`
command: String,
/// path to script to execute
/// The script to run
bin: PathBuf,
/// arguments to pass to script
/// Arguments to pass to the script
args: Vec<String>,

/// Show help
/// Print the script's help page instead of running it
#[usage(short)]
h: bool,

/// Show help
/// Print the script's help page instead of running it
#[usage(long)]
help: bool,
}
Expand Down
4 changes: 2 additions & 2 deletions cli/src/cli/explain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ use crate::cli::{empty_mount_answers, OutputFormat};
// nothing.
#[usage(effect = "read", verbatim_doc_comment)]
pub struct Explain {
/// A usage spec file or script with a usage shebang, use "-" to read from stdin
/// A usage spec file, or a script with a usage shebang; "-" reads stdin
#[usage(short, long, value_hint = usage_rs::ValueHint::FilePath)]
file: Option<PathBuf>,

/// Raw string spec input
/// The spec itself, as a string, instead of a file
#[usage(short, long, required_unless = "--file", overrides = "--file")]
spec: Option<String>,

Expand Down
26 changes: 15 additions & 11 deletions cli/src/cli/generate/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ use usage_rs::Args;

use super::parse_file_or_stdin;

/// Generate shell completion scripts for bash, fish, nu, powershell, or zsh
/// Generate a shell completion script for bash, fish, nu, powershell, or zsh
///
/// The script is a shim: on each Tab it hands the words typed so far to `usage complete-word`,
/// so `usage` must be installed wherever the script is. The spec comes from `--file`, or from
/// running `--usage-cmd` at completion time, which keeps a CLI that prints its own spec from
/// ever going stale.
#[derive(Args)]
#[usage(alias = "c", alias_hidden("complete", "completions"), effect = "read")]
pub struct Completion {
/// Shell to generate completions for
/// The shell to generate the script for
#[usage(choices("bash", "fish", "nu", "powershell", "zsh"))]
shell: String,

/// The CLI which we're generating completions for
/// The name of the CLI being completed, as it is typed at the prompt
bin: String,

/// Install the script where this shell looks for it, instead of printing it
Expand All @@ -28,23 +33,22 @@ pub struct Completion {
#[usage(long, requires = "--install", effect = "write")]
force: bool,

/// A .usage.kdl spec file to use for generating completions, use "-" to read from stdin
/// A usage spec file, or a script with a usage shebang; "-" reads stdin
#[usage(short, long)]
file: Option<PathBuf>,

/// A cache key to use for storing the results of calling the CLI with --usage-cmd
/// Cache what --usage-cmd prints under this key, so it runs once per key rather than on every Tab; the CLI's version is a good key
#[usage(long, requires = "--usage-cmd")]
cache_key: Option<String>,

/// Override the bin used for calling back to usage-cli
///
/// You may need to set this if you have a different bin named "usage"
/// The `usage` executable the script calls back to, when it is not `usage` on PATH
#[usage(long, default = "usage", env = "JDX_USAGE_BIN")]
usage_bin: String,

/// A command which generates a usage spec
/// e.g.: `mycli --usage` or `mycli completion usage`
/// Defaults to "$bin --usage"
/// A command that prints the CLI's spec, run in place of reading --file
///
/// For a CLI that answers with its own spec, such as `mycli __usage_spec__`, so the script
/// always completes the version that is installed. Required unless --file is given.
#[usage(long, required_unless = "--file")]
usage_cmd: Option<String>,
}
Expand Down
16 changes: 8 additions & 8 deletions cli/src/cli/generate/completion_init.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
use usage::complete::complete_init;
use usage_rs::Args;

/// Generate a shell init script that auto-completes any usage shebang script on $PATH
/// Generate a shell init script that completes every usage-shebang script on PATH
///
/// Source the output once from your shell rc (e.g. ~/.bashrc) to enable
/// tab-completion for any executable whose first line is a `usage` shebang —
/// no per-script `usage g completion` step required.
/// Source it once from the shell's rc file and Tab works on any executable whose first line
/// is a `usage` shebang, with no per-script `usage generate completion` step. bash and zsh
/// register a fallback completer that asks `usage complete-word` when the command has such a
/// shebang; fish has no fallback, so it scans PATH once at startup and registers each script
/// it finds.
#[derive(Args)]
#[usage(
alias = "ci",
alias_hidden("init", "completions-init"),
effect = "read"
)]
pub struct CompletionInit {
/// Shell to generate the init script for
/// The shell to generate the script for
#[usage(choices("bash", "fish", "zsh"))]
shell: String,

/// Override the bin used for calling back to usage-cli
///
/// You may need to set this if you have a different bin named "usage"
/// The `usage` executable the script calls back to, when it is not `usage` on PATH
#[usage(long, default = "usage", env = "JDX_USAGE_BIN")]
usage_bin: String,
}
Expand Down
8 changes: 4 additions & 4 deletions cli/src/cli/generate/fig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,23 @@ mod description_format {
}
}

/// Generate Fig completion spec for Amazon Q / Fig
/// Generate a Fig completion spec, for Amazon Q and Fig
#[derive(Args)]
#[usage(effect = "read")]
pub struct Fig {
/// A usage spec taken in as a file, use "-" to read from stdin
/// A usage spec file, or a script with a usage shebang; "-" reads stdin
#[usage(short, long)]
file: Option<PathBuf>,

/// File path where the generated Fig spec will be saved, or "-" for stdout
/// Where to write the spec, or "-" for stdout (default)
#[usage(
long,
value_hint = usage_rs::ValueHint::FilePath,
effect = "write"
)]
out_file: Option<PathBuf>,

/// Raw string spec input
/// The spec itself, as a string, instead of a file
#[usage(long, required_unless = "--file", overrides = "--file")]
spec: Option<String>,
}
Expand Down
6 changes: 3 additions & 3 deletions cli/src/cli/generate/go.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ use crate::cli::generate;
#[derive(Args)]
#[usage(effect = "read")]
pub struct Go {
/// A usage spec taken in as a file, use "-" to read from stdin
/// A usage spec file, or a script with a usage shebang; "-" reads stdin
#[usage(short, long)]
file: Option<PathBuf>,

/// File path where the generated Go source will be saved, or "-" for stdout
/// Where to write the Go source, or "-" for stdout (default)
#[usage(
short,
long,
Expand All @@ -33,7 +33,7 @@ pub struct Go {
#[usage(short, long)]
package: Option<String>,

/// Raw string spec input
/// The spec itself, as a string, instead of a file
#[usage(long, required_unless = "--file", overrides = "--file")]
spec: Option<String>,
}
Expand Down
9 changes: 6 additions & 3 deletions cli/src/cli/generate/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ use std::path::PathBuf;
use usage::miette::IntoDiagnostic;
use usage::miette::Result;

/// Outputs a usage spec in json format
/// Print a usage spec as JSON
///
/// The same document the KDL describes, with included files merged and defaults filled in,
/// for a tool that would rather not parse KDL itself.
#[derive(usage_rs::Args)]
#[usage(effect = "read")]
pub struct Json {
/// A usage spec taken in as a file, use "-" to read from stdin
/// A usage spec file, or a script with a usage shebang; "-" reads stdin
#[usage(short, long)]
file: Option<PathBuf>,

/// raw string spec input
/// The spec itself, as a string, instead of a file
#[usage(long, required_unless = "--file", overrides = "--file")]
spec: Option<String>,

Expand Down
8 changes: 6 additions & 2 deletions cli/src/cli/generate/json_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ use crate::schema::{config_schema, SchemaOptions};
use usage::miette::Result;

/// Generate a JSON Schema for a CLI's config file from its usage spec
///
/// Built from the spec's `config` block: each `prop` becomes a property with its type,
/// default, and help, so an editor can complete and validate the file the CLI reads. A spec
/// whose settings cannot live in a file is an error rather than an empty schema.
#[derive(usage_rs::Args)]
#[usage(effect = "read")]
pub struct JsonSchema {
/// A usage spec taken in as a file, use "-" to read from stdin
/// A usage spec file, or a script with a usage shebang; "-" reads stdin
#[usage(short, long)]
file: Option<PathBuf>,

Expand All @@ -22,7 +26,7 @@ pub struct JsonSchema {
)]
out_file: Option<PathBuf>,

/// raw string spec input
/// The spec itself, as a string, instead of a file
#[usage(long, required_unless = "--file", overrides = "--file")]
spec: Option<String>,

Expand Down
2 changes: 1 addition & 1 deletion cli/src/cli/generate/manpage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use usage_rs::Args;
#[derive(Args)]
#[usage(alias = "man", effect = "read")]
pub struct Manpage {
/// A usage spec taken in as a file, use "-" to read from stdin
/// A usage spec file, or a script with a usage shebang; "-" reads stdin
#[usage(short, long)]
file: PathBuf,

Expand Down
13 changes: 8 additions & 5 deletions cli/src/cli/generate/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ use usage::docs::markdown::{MarkdownRenderer, MarkdownTemplate};
use usage::error::UsageErr;
use usage_rs::Args;

/// Generate markdown documentation from usage specs
/// Generate Markdown documentation from a usage spec
///
/// One page by default, or a page per command with --multi. Every part of the output comes
/// from a Tera template that --template can replace.
#[derive(Args)]
#[usage(alias = "md", effect = "read")]
pub struct Markdown {
/// A usage spec taken in as a file, use "-" to read from stdin
/// A usage spec file, or a script with a usage shebang; "-" reads stdin
#[usage(short, long)]
file: PathBuf,

Expand All @@ -27,7 +30,7 @@ pub struct Markdown {
#[usage(long)]
html_encode: bool,

/// Output markdown files to this directory (required when using --multi)
/// Directory for the per-command pages (required with --multi)
#[usage(
long,
value_hint = usage_rs::ValueHint::DirPath,
Expand All @@ -49,11 +52,11 @@ pub struct Markdown {
#[usage(long)]
replace_pre_with_code_fences: bool,

/// Prefix to add to all URLs
/// Prefix for the links between pages, such as /cli/reference
#[usage(long)]
url_prefix: Option<String>,

/// Override a Tera template with NAME=PATH
/// Replace a built-in Tera template, as NAME=PATH; the names are spec, index, command, argument, flag, and config
#[usage(long)]
template: Vec<String>,
}
Expand Down
2 changes: 1 addition & 1 deletion cli/src/cli/generate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ mod manpage;
mod markdown;
mod sdk;

/// Generate completions, documentation, and other artifacts from usage specs
/// Generate completions, docs, manpages, SDKs, and parse tables from a usage spec
// Cannot run alone, and every child starts at `read`, so the parent is `read` too.
#[derive(usage_rs::Args)]
#[usage(alias = "g", effect = "read", run)]
Expand Down
9 changes: 6 additions & 3 deletions cli/src/cli/generate/sdk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,32 @@ use crate::cli::generate;
use usage::sdk::{SdkLanguage, SdkOptions};

/// Generate a type-safe SDK from a usage spec
///
/// The SDK is a subprocess wrapper: typed arguments, flags, and choices for every command,
/// and a client that builds the argument list and runs the binary.
// The only generator whose output flag is required: it cannot print an SDK to stdout, so
// every invocation writes a directory, and the effect belongs on the command rather than on
// a flag that raises it.
#[derive(Args)]
#[usage(effect = "write")]
pub struct Sdk {
/// A usage spec taken in as a file
/// A usage spec file, or a script with a usage shebang; "-" reads stdin
#[usage(short, long)]
file: Option<PathBuf>,

/// Target language for the SDK
#[usage(short, long, choices("typescript", "python"))]
language: String,

/// Output directory for generated SDK files
/// Directory to write the SDK into
#[usage(short, long)]
output: PathBuf,

/// Override the package/module name (defaults to spec bin name)
#[usage(short, long)]
package_name: Option<String>,

/// Raw string spec input
/// The spec itself, as a string, instead of a file
#[usage(long, required_unless = "--file", overrides = "--file")]
spec: Option<String>,
}
Expand Down
11 changes: 9 additions & 2 deletions cli/src/cli/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@ use usage::{Parser, Spec, SpecArg, SpecCommand, SpecFlag, SpecFlagAction};
use crate::cli::generate::parse_file_or_stdin;
use crate::cli::{empty_mount_answers, OutputFormat};

/// Lint a usage spec file for common issues
/// Lint a usage spec for mistakes that still parse
///
/// A spec can be valid KDL and still be wrong: a flag or subcommand declared twice, a required
/// argument after an optional one, a variadic argument that is not last, a command or flag
/// with no help, an example its own spec cannot parse. Each finding carries a code such as
/// `duplicate-flag`, and `--format json` prints the findings as a list for a script to act on.
///
/// Exits 1 when there is an error, or a warning under `--warnings-as-errors`.
#[derive(usage_rs::Args)]
#[usage(effect = "read")]
pub struct Lint {
/// A usage spec file to lint, use "-" to read from stdin
/// A usage spec file, or a script with a usage shebang; "-" reads stdin
file: PathBuf,

/// Output format
Expand Down
Loading