diff --git a/Cargo.lock b/Cargo.lock index 66dff57..559b0eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -720,7 +720,7 @@ checksum = "062c875482ccb676fd40c804a40e3824d4464c18c364547456d1c8e8e951ae47" dependencies = [ "miette", "nom", - "thiserror", + "thiserror 1.0.68", ] [[package]] @@ -787,7 +787,7 @@ dependencies = [ "supports-unicode", "terminal_size", "textwrap", - "thiserror", + "thiserror 1.0.68", "unicode-width", ] @@ -907,7 +907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", - "thiserror", + "thiserror 1.0.68", "ucd-trie", ] @@ -1371,7 +1371,16 @@ version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.68", +] + +[[package]] +name = "thiserror" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037e29b009aa709f293b974da5cd33b15783c049e07f8435778ce8c4871525d8" +dependencies = [ + "thiserror-impl 2.0.2", ] [[package]] @@ -1385,6 +1394,17 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "thiserror-impl" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea4778c7e8ff768bdb32a58a2349903859fe719a320300d7d4ce8636f19a1e69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "typenum" version = "1.17.0" @@ -1489,7 +1509,7 @@ dependencies = [ "serde_json", "strum", "tera", - "thiserror", + "thiserror 2.0.2", "usage-lib", "xx", ] @@ -1514,7 +1534,8 @@ dependencies = [ "shell-words", "strum", "tera", - "thiserror", + "thiserror 2.0.2", + "versions", "xx", ] @@ -1530,6 +1551,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "versions" +version = "6.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25d498b63d1fdb376b4250f39ab3a5ee8d103957346abacd911e2d8b612c139" +dependencies = [ + "itertools", + "nom", +] + [[package]] name = "wait-timeout" version = "0.2.0" @@ -1803,7 +1834,7 @@ dependencies = [ "log", "miette", "regex", - "thiserror", + "thiserror 1.0.68", ] [[package]] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 2ac2d68..10c9168 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -42,7 +42,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1.0" strum = { version = "0.26", features = ["derive"] } tera = "1" -thiserror = "1" +thiserror = "2" usage-lib = { workspace = true, features = ["clap", "docs"] } xx = "1" diff --git a/docs/spec/reference/index.md b/docs/spec/reference/index.md index fa2b579..73f4e7b 100644 --- a/docs/spec/reference/index.md +++ b/docs/spec/reference/index.md @@ -6,6 +6,7 @@ bin "mycli" # the name of the binary version "1.0.0" # the version of the CLI author "nobody" # the author of the CLI license "MIT" # SPDX license the CLI is released under +min_usage_version "1.0.0" # the minimum version of usage this CLI supports # help for -h before_help "before about" diff --git a/examples/example.sh b/examples/example.sh index f465ba8..b518c17 100755 --- a/examples/example.sh +++ b/examples/example.sh @@ -5,7 +5,8 @@ #USAGE flag "--bar " help="Option value" #USAGE flag "--defaulted " default="mydefault" help="Defaulted value" #USAGE arg "baz" help="Positional values" -set -euo pipefail +#USAGE min_usage_version "1" +set -eo pipefail echo foo: $usage_foo echo bar: $usage_bar diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 3257032..bd6beec 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -29,12 +29,13 @@ kdl = "4" log = "0.4" miette = "5" once_cell = "1" +regex = "1" serde = { version = "1", features = ["derive"] } strum = { version = "0.26", features = ["derive"] } tera = { version = "1", optional = true } -thiserror = "1" +thiserror = "2" +versions = "6" xx = "1" -regex = "1" [features] default = ["docs"] diff --git a/lib/src/spec/mod.rs b/lib/src/spec/mod.rs index 724c65a..f29a223 100644 --- a/lib/src/spec/mod.rs +++ b/lib/src/spec/mod.rs @@ -11,7 +11,7 @@ pub mod mount; use indexmap::IndexMap; use kdl::{KdlDocument, KdlEntry, KdlNode, KdlValue}; -use log::info; +use log::{info, warn}; use serde::Serialize; use std::fmt::{Display, Formatter}; use std::iter::once; @@ -42,6 +42,7 @@ pub struct Spec { pub about_long: Option, pub about_md: Option, pub disable_help: Option, + pub min_usage_version: Option, } impl Spec { @@ -121,6 +122,11 @@ impl Spec { schema.complete.insert(complete.name.clone(), complete); } "disable_help" => schema.disable_help = Some(node.arg(0)?.ensure_bool()?), + "min_usage_version" => { + let v = node.arg(0)?.ensure_string()?; + check_usage_version(&v); + schema.min_usage_version = Some(v); + } "include" => { let file = node .props() @@ -186,10 +192,29 @@ impl Spec { if other.disable_help.is_some() { self.disable_help = other.disable_help; } + if other.min_usage_version.is_some() { + self.min_usage_version = other.min_usage_version; + } self.cmd.merge(other.cmd); } } +fn check_usage_version(version: &str) { + let cur = versions::Versioning::new(env!("CARGO_PKG_VERSION")).unwrap(); + match versions::Versioning::new(version) { + Some(v) => { + if cur < v { + warn!( + "This usage spec requires at least version {}, but you are using version {} of usage", + version, + cur + ); + } + } + _ => warn!("Invalid version: {}", version), + } +} + fn split_script(file: &Path) -> Result<(String, String), UsageErr> { let full = file::read_to_string(file)?; if full.starts_with("#!") @@ -293,6 +318,11 @@ impl Display for Spec { node.push(KdlEntry::new(disable_help)); nodes.push(node); } + if let Some(min_usage_version) = &self.min_usage_version { + let mut node = KdlNode::new("min_usage_version"); + node.push(KdlEntry::new(min_usage_version.clone())); + nodes.push(node); + } if !self.usage.is_empty() { let mut node = KdlNode::new("usage"); node.push(KdlEntry::new(self.usage.clone()));