From 3c41527ba3c3db570dca5ba19e8c141dc389cd75 Mon Sep 17 00:00:00 2001 From: sigoden Date: Sat, 6 Apr 2024 12:11:05 +0000 Subject: [PATCH] feat: add `@meta version` and `@meta author` --- README.md | 4 +- docs/specification.md | 69 ++++++---- src/command/mod.rs | 33 +++-- src/utils.rs | 2 + ...ration__validate__help_version_legacy.snap | 123 ++++++++++++++++++ ...tegration__validate__version_missing.snap} | 2 - tests/validate.rs | 31 ++++- 7 files changed, 221 insertions(+), 43 deletions(-) create mode 100644 tests/snapshots/integration__validate__help_version_legacy.snap rename tests/snapshots/{integration__validate__version.snap => integration__validate__version_missing.snap} (99%) diff --git a/README.md b/README.md index 521a5dc3..ea417b9b 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ It's how the argc parser identifies configuration. | Tag | Description | | :---------------------------------------------- | ------------------------------------ | +| [`@describe`](./docs/specification.md#describe) | Set the description for the command. | | [`@cmd`](./docs/specification.md#cmd) | Define a subcommand. | | [`@alias`](./docs/specification.md#alias) | Set aliases for the subcommand. | | [`@arg`](./docs/specification.md#arg) | Define a positional argument. | @@ -108,9 +109,6 @@ It's how the argc parser identifies configuration. | [`@flag`](./docs/specification.md#flag) | Define a flag argument. | | [`@env`](./docs/specification.md#env) | Define an environment variable. | | [`@meta`](./docs/specification.md#meta) | Add a metadata. | -| [`@describe`](./docs/specification.md#describe) | Set the description for the command. | -| [`@version`](./docs/specification.md#version) | Set the version for the command. | -| [`@author`](./docs/specification.md#author) | Set the author for the command. | See [specification](https://github.com/sigoden/argc/blob/main/docs/specification.md) for the grammar and usage of all the comment tags. diff --git a/docs/specification.md b/docs/specification.md index 4b336b04..34ad8f9b 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -1,5 +1,24 @@ # Specification +### `@describe` + +Set the description for the command. + +> **Syntax**\ +> `@describe` string + +```sh +# @describe A demo CLI +``` + +```sh +# @describe Multi-line auto-wrapped help text +# +# Extra lines after the @cmd or @describe, which don't start with an @, are +# treated as the long description. A line which is not a comment ends +# the block. +``` + ## `@cmd` Define a subcommand. @@ -150,49 +169,49 @@ Add a metadata. | syntax | scope | description | | :--------------------------- | ------ | :------------------------------------------------------------------- | -| `@meta dotenv []` | root | Load a `.env` file from a custom path, if persent. | +| `@meta version ` | any | Set the version for the command. | +| `@meta author ` | any | Set the author for the command. | +| `@meta dotenv []` | root | Load a dotenv file from a custom path, if persent. | +| `@meta symbol ` | any | Define a symbolic parameter, e.g. `+toolchain`, `@argument-file`. | +| `@meta man-section <1-8>` | root | Override the section for the man page, defaulting to 1. | | `@meta default-subcommand` | subcmd | Set the current subcommand as the default. | | `@meta inherit-flag-options` | root | Subcommands will inherit the flags/options from their parent. | -| `@meta no-inherit-env` | root | Subcommands don't inherit the env vars from their parent. | -| `@meta symbol ` | anycmd | Define a symbolic parameter, e.g. `+toolchain`, `@argument-file`. | +| `@meta no-inherit-env` | root | Subcommands will not inherit the env vars from their parent. | | `@meta combine-shorts` | root | Short flags/options can be combined, e.g. `prog -xf => prog -x -f `. | -| `@meta man-section <1-8>` | root | Override the default section the man page. | - -### `@describe` - -Set the description for the command. - -> **Syntax**\ -> `@describe` string ```sh -# @describe A demo cli +# @meta version 1.0.0 +# @meta author nobody +# @meta dotenv .env.local # Load .env.local +# @meta dotenv # Load .env +# @meta symbol +toolchain[`_choice_fn`] +# @meta man-section 8 # Generate to man section 8 ``` -## `@version` +## Deprecated Tags -Set the version for the command. +Deprecated tags can still be used, but are not recommended and may be completely abandoned in subsequent versions. -> **Syntax**\ -> `@version` string +### `@version` -```sh -# @version 2.17.1 +Set the version for the command. + +```diff +-- # @version 2.17.1 +++ # @meta version 2.17.1 ``` -## `@author` +### `@author` Set the author for the command. -```sh -# @author alice +```diff +-- # @author nobody +++ # @meta author nobody ``` -> **Syntax**\ -> `@author` string - -## Component +## Internal components ### short diff --git a/src/command/mod.rs b/src/command/mod.rs index aad54ce4..9af6eb87 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -12,9 +12,9 @@ use crate::param::{ }; use crate::parser::{parse, parse_symbol, Event, EventData, EventScope, Position}; use crate::utils::{ - AFTER_HOOK, BEFORE_HOOK, INTERNAL_SYMBOL, MAIN_NAME, META_COMBINE_SHORTS, + AFTER_HOOK, BEFORE_HOOK, INTERNAL_SYMBOL, MAIN_NAME, META_AUTHOR, META_COMBINE_SHORTS, META_DEFAULT_SUBCOMMAND, META_DOTENV, META_INHERIT_FLAG_OPTIONS, META_NO_INHERIT_ENV, - META_SYMBOL, ROOT_NAME, + META_SYMBOL, META_VERSION, ROOT_NAME, }; use crate::Result; @@ -150,12 +150,29 @@ impl Command { } EventData::Meta(key, value) => { let cmd = Self::get_cmd(&mut root_cmd, "@meta", position)?; - if key == META_SYMBOL { - let (ch, name, choice_fn) = parse_symbol(&value).ok_or_else(|| { - anyhow!("@meta(line {}) invalid symbol value", position) - })?; - cmd.symbols - .insert(ch, (name.to_string(), choice_fn.map(|v| v.to_string()))); + match key.as_str() { + META_SYMBOL => { + let (ch, name, choice_fn) = parse_symbol(&value).ok_or_else(|| { + anyhow!("@meta(line {}) invalid symbol value", position) + })?; + cmd.symbols + .insert(ch, (name.to_string(), choice_fn.map(|v| v.to_string()))); + } + META_VERSION => { + if value.is_empty() { + bail!("@meta(line {}) invalid version value", position) + } else { + cmd.version = Some(value.clone()); + } + } + META_AUTHOR => { + if value.is_empty() { + bail!("@meta(line {}) invalid version value", position) + } else { + cmd.author = Some(value.clone()); + } + } + _ => {} } cmd.metadata.push((key, value, position)); } diff --git a/src/utils.rs b/src/utils.rs index 627e12eb..34612ad1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -14,6 +14,8 @@ pub const AFTER_HOOK: &str = "_argc_after"; pub const ROOT_NAME: &str = "prog"; pub const MAIN_NAME: &str = "main"; +pub(crate) const META_VERSION: &str = "version"; +pub(crate) const META_AUTHOR: &str = "author"; pub(crate) const META_DOTENV: &str = "dotenv"; pub(crate) const META_DEFAULT_SUBCOMMAND: &str = "default-subcommand"; pub(crate) const META_INHERIT_FLAG_OPTIONS: &str = "inherit-flag-options"; diff --git a/tests/snapshots/integration__validate__help_version_legacy.snap b/tests/snapshots/integration__validate__help_version_legacy.snap new file mode 100644 index 00000000..ce642f3f --- /dev/null +++ b/tests/snapshots/integration__validate__help_version_legacy.snap @@ -0,0 +1,123 @@ +--- +source: tests/validate.rs +expression: data +--- +************ RUN ************ +prog help + +# OUTPUT +command cat >&2 <<-'EOF' +prog 1.0.0 +nobody +Test argc + +USAGE: prog + +EOF +exit 0 + +# RUN_OUTPUT +prog 1.0.0 +nobody +Test argc + +USAGE: prog + +************ RUN ************ +prog --help + +# OUTPUT +command cat >&2 <<-'EOF' +prog 1.0.0 +nobody +Test argc + +USAGE: prog + +EOF +exit 0 + +# RUN_OUTPUT +prog 1.0.0 +nobody +Test argc + +USAGE: prog + +************ RUN ************ +prog -help + +# OUTPUT +command cat >&2 <<-'EOF' +prog 1.0.0 +nobody +Test argc + +USAGE: prog + +EOF +exit 0 + +# RUN_OUTPUT +prog 1.0.0 +nobody +Test argc + +USAGE: prog + +************ RUN ************ +prog -h + +# OUTPUT +command cat >&2 <<-'EOF' +prog 1.0.0 +nobody +Test argc + +USAGE: prog + +EOF +exit 0 + +# RUN_OUTPUT +prog 1.0.0 +nobody +Test argc + +USAGE: prog + +************ RUN ************ +prog --version + +# OUTPUT +command cat >&2 <<-'EOF' +prog 1.0.0 +EOF +exit 0 + +# RUN_OUTPUT +prog 1.0.0 + +************ RUN ************ +prog -version + +# OUTPUT +command cat >&2 <<-'EOF' +prog 1.0.0 +EOF +exit 0 + +# RUN_OUTPUT +prog 1.0.0 + +************ RUN ************ +prog -V + +# OUTPUT +command cat >&2 <<-'EOF' +prog 1.0.0 +EOF +exit 0 + +# RUN_OUTPUT +prog 1.0.0 diff --git a/tests/snapshots/integration__validate__version.snap b/tests/snapshots/integration__validate__version_missing.snap similarity index 99% rename from tests/snapshots/integration__validate__version.snap rename to tests/snapshots/integration__validate__version_missing.snap index f1c2bf4b..8e6b486b 100644 --- a/tests/snapshots/integration__validate__version.snap +++ b/tests/snapshots/integration__validate__version_missing.snap @@ -28,5 +28,3 @@ argc__args=([0]="prog" [1]="cmd" [2]="--version") argc__fn=cmd argc__positionals=([0]="--version") cmd --version - - diff --git a/tests/validate.rs b/tests/validate.rs index 4019f15d..c146d677 100644 --- a/tests/validate.rs +++ b/tests/validate.rs @@ -4,8 +4,29 @@ use crate::*; fn help_version() { let script = r###" # @describe Test argc -# @version 1.0.0 -# @author nobody +# @meta version 1.0.0 +# @meta author nobody +"###; + snapshot_multi!( + script, + [ + vec!["prog", "help"], + vec!["prog", "--help"], + vec!["prog", "-help"], + vec!["prog", "-h"], + vec!["prog", "--version"], + vec!["prog", "-version"], + vec!["prog", "-V"], + ] + ); +} + +#[test] +fn help_version_legacy() { + let script = r###" +# @describe Test argc +# @version 1.0.0 +# @author nobody "###; snapshot_multi!( script, @@ -25,7 +46,7 @@ fn help_version() { fn help_version_shadow() { let script = r###" # @describe Test argc -# @version 1.0.0 +# @meta version 1.0.0 # @flag -h --host # @flag -V --verify @@ -37,7 +58,7 @@ fn help_version_shadow() { fn help_version_exist() { let script = r###" # @describe Test argc -# @version 1.0.0 +# @meta version 1.0.0 # @flag -h --help # @flag -V --version @@ -56,7 +77,7 @@ fn help_notations() { } #[test] -fn version() { +fn version_missing() { let script = r###" # @cmd cmd() { :; }