diff --git a/Cargo.lock b/Cargo.lock index 687e6ec6..49ad9ad1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -164,15 +164,6 @@ version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "bs58" version = "0.5.1" @@ -234,8 +225,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" dependencies = [ "cfg-if", - "cpufeatures 0.3.0", - "rand_core 0.10.1", + "cpufeatures", + "rand_core", ] [[package]] @@ -250,28 +241,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "chrono-tz" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" -dependencies = [ - "chrono", - "chrono-tz-build", - "phf", -] - -[[package]] -name = "chrono-tz-build" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" -dependencies = [ - "parse-zoneinfo", - "phf", - "phf_codegen", -] - [[package]] name = "ciborium" version = "0.2.2" @@ -380,15 +349,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - [[package]] name = "cpufeatures" version = "0.3.0" @@ -464,16 +424,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - [[package]] name = "ctor" version = "1.0.7" @@ -559,12 +509,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "deunicode" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" - [[package]] name = "diff" version = "0.1.13" @@ -577,16 +521,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - [[package]] name = "duct" version = "1.1.1" @@ -742,27 +676,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "getrandom" version = "0.4.3" @@ -773,7 +686,7 @@ dependencies = [ "js-sys", "libc", "r-efi", - "rand_core 0.10.1", + "rand_core", "wasm-bindgen", ] @@ -783,30 +696,6 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" -[[package]] -name = "globset" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "globwalk" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" -dependencies = [ - "bitflags 2.13.0", - "ignore", - "walkdir", -] - [[package]] name = "half" version = "2.7.1" @@ -854,15 +743,6 @@ dependencies = [ "windows", ] -[[package]] -name = "humansize" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" -dependencies = [ - "libm", -] - [[package]] name = "iana-time-zone" version = "0.1.65" @@ -893,22 +773,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "ignore" -version = "0.4.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b915661dd01db3f05050265b2477bcc6527b3792388e2749b41623cc592be67d" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata", - "same-file", - "walkdir", - "winapi-util", -] - [[package]] name = "indexmap" version = "1.9.3" @@ -1037,24 +901,12 @@ dependencies = [ "winnow", ] -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "libc" version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - [[package]] name = "link-section" version = "0.18.3" @@ -1219,102 +1071,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "parse-zoneinfo" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" -dependencies = [ - "regex", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pest" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" -dependencies = [ - "memchr", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pest_meta" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" -dependencies = [ - "pest", - "sha2", -] - -[[package]] -name = "phf" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_codegen" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared", - "rand 0.8.6", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", -] - [[package]] name = "pin-project-lite" version = "0.2.17" @@ -1370,15 +1126,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "predicates" version = "3.1.4" @@ -1465,17 +1212,6 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" -[[package]] -name = "rand" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" -dependencies = [ - "libc", - "rand_chacha", - "rand_core 0.6.4", -] - [[package]] name = "rand" version = "0.10.1" @@ -1483,27 +1219,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20", - "getrandom 0.4.3", - "rand_core 0.10.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", + "getrandom", + "rand_core", ] [[package]] @@ -1720,17 +1437,6 @@ dependencies = [ "syn", ] -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest", -] - [[package]] name = "shared_child" version = "1.1.1" @@ -1797,28 +1503,12 @@ version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" -[[package]] -name = "siphasher" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" - [[package]] name = "slab" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" -[[package]] -name = "slug" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" -dependencies = [ - "deunicode", - "wasm-bindgen", -] - [[package]] name = "strsim" version = "0.11.1" @@ -1885,7 +1575,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.3", + "getrandom", "once_cell", "rustix", "windows-sys 0.61.2", @@ -1893,24 +1583,11 @@ dependencies = [ [[package]] name = "tera" -version = "1.20.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8004bca281f2d32df3bacd59bc67b312cb4c70cea46cbd79dbe8ac5ed206722" +checksum = "38ea62bd58771b570262e11ffa274fa9eda9986bd886e6e3a1f7f42afef8024c" dependencies = [ - "chrono", - "chrono-tz", - "globwalk", - "humansize", - "lazy_static", - "percent-encoding", - "pest", - "pest_derive", - "rand 0.8.6", - "regex", "serde", - "serde_json", - "slug", - "unicode-segmentation", ] [[package]] @@ -2014,18 +1691,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "typenum" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" - -[[package]] -name = "ucd-trie" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" - [[package]] name = "unicode-ident" version = "1.0.24" @@ -2038,12 +1703,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" -[[package]] -name = "unicode-segmentation" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" - [[package]] name = "unicode-width" version = "0.1.14" @@ -2117,12 +1776,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "versions" version = "7.0.0" @@ -2152,12 +1805,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - [[package]] name = "wasm-bindgen" version = "0.2.126" @@ -2498,11 +2145,11 @@ checksum = "f8aff208388ac09afbb2bd20c3db8f7d253c9fbed7076e3618d598bb3deef2c5" dependencies = [ "duct", "filetime", - "getrandom 0.4.3", + "getrandom", "homedir", "log", "miette", - "rand 0.10.1", + "rand", "regex", "strsim", "thiserror", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 054355c9..aa8e8ab6 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -37,7 +37,7 @@ regex = "1" serde = { version = "1", features = ["derive"] } serde_json = "1.0" serde_with = "3" -tera = "1" +tera = "2" thiserror = "2" usage-lib = { workspace = true, features = ["clap", "docs", "unstable_choices_env"] } xx = "2" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index aaffd734..91134460 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -34,7 +34,7 @@ roff = { version = "1.0", optional = true } serde = { version = "1", features = ["derive"] } shell-words = "1" strum = { version = "0.28", features = ["derive"] } -tera = { version = "1", optional = true } +tera = { version = "2", optional = true } thiserror = "2" versions = "7" xx = "2" diff --git a/lib/src/docs/cli/mod.rs b/lib/src/docs/cli/mod.rs index 3c34b553..d6eaadd9 100644 --- a/lib/src/docs/cli/mod.rs +++ b/lib/src/docs/cli/mod.rs @@ -22,22 +22,36 @@ pub fn render_help(spec: &Spec, cmd: &SpecCommand, long: bool) -> String { static TERA: LazyLock = LazyLock::new(|| { let mut tera = Tera::default(); - #[rustfmt::skip] - tera.add_raw_templates([ - ("spec_template_short.tera", include_str!("templates/spec_template_short.tera")), - ("spec_template_long.tera", include_str!("templates/spec_template_long.tera")), - ]).unwrap(); - // Register ljust filter for left-justifying text with padding tera.register_filter( "ljust", - |value: &tera::Value, args: &std::collections::HashMap| { + |value: &tera::Value, args: tera::Kwargs, _: &tera::State| -> tera::TeraResult { let value = value.as_str().unwrap_or(""); - let width = args.get("width").and_then(|v| v.as_u64()).unwrap_or(0) as usize; - let result = format!("{:("width")?.unwrap_or(0) as usize; + Ok(format!("{: tera::TeraResult { + let default_val = kwargs.must_get::("value")?; + let boolean = kwargs.get::("boolean")?.unwrap_or_default(); + if value.is_undefined() || value.is_none() || (boolean && !value.is_truthy()) { + Ok(default_val) + } else { + Ok(value.clone()) + } + }, + ); + + #[rustfmt::skip] + tera.add_raw_templates([ + ("spec_template_short.tera", include_str!("templates/spec_template_short.tera")), + ("spec_template_long.tera", include_str!("templates/spec_template_long.tera")), + ]).unwrap(); tera }); diff --git a/lib/src/docs/cli/templates/spec_template_long.tera b/lib/src/docs/cli/templates/spec_template_long.tera index 31aa48ae..2ed09536 100644 --- a/lib/src/docs/cli/templates/spec_template_long.tera +++ b/lib/src/docs/cli/templates/spec_template_long.tera @@ -15,12 +15,12 @@ {% elif spec.about %}{{ spec.about }} {% endif -%} -Usage: {{ spec.bin ~ " " ~ cmd.usage | trim }} +Usage: {{ (spec.bin ~ " " ~ cmd.usage) | trim }} {%- if cmd.subcommands %} Commands: -{%- for name, cmd in cmd.subcommands %} +{%- for cmd in cmd.subcommands | values | sort(attribute="usage") %} {{ cmd.usage | trim }} {%- if cmd.deprecated %} [deprecated: {{ cmd.deprecated }}]{%- endif %} {%- if cmd.aliases %} [aliases: {{ cmd.aliases | join(sep=", ") }}]{% endif %} @@ -46,7 +46,7 @@ Arguments: {{ arg.usage | trim }} {%- set help = arg.help_long | default(value=arg.help | default(value='')) %} {%- if help %} - {{ help | indent(width=2) }} + {{ help | indent(width=4) }} {%- endif %} {%- endif %} {%- if arg.choices and arg.choices.choices %} @@ -78,7 +78,7 @@ Flags: {%- if flag.aliases %} [aliases: {{ flag.aliases | join(sep=", ") }}]{% endif %} {%- set help = flag.help_long | default(value=flag.help | default(value='')) %} {%- if help %} - {{ help | indent(width=2) }} + {{ help | indent(width=4) }} {%- endif %} {%- endif %} {%- if flag.arg.choices and flag.arg.choices.choices %} diff --git a/lib/src/docs/cli/templates/spec_template_short.tera b/lib/src/docs/cli/templates/spec_template_short.tera index b1e45fa3..71cfe70f 100644 --- a/lib/src/docs/cli/templates/spec_template_short.tera +++ b/lib/src/docs/cli/templates/spec_template_short.tera @@ -9,12 +9,12 @@ {%- if spec.about %}{{ spec.about }} {% endif -%} -Usage: {{ spec.bin ~ " " ~ cmd.usage | trim }} +Usage: {{ (spec.bin ~ " " ~ cmd.usage) | trim }} {%- if cmd.subcommands %} Commands: -{%- for name, cmd in cmd.subcommands %} +{%- for cmd in cmd.subcommands | values | sort(attribute="usage") %} {{ cmd.usage | trim }} {%- if cmd.deprecated %} [deprecated: {{ cmd.deprecated }}]{%- endif %} {%- if cmd.aliases %} [aliases: {{ cmd.aliases | join(sep=", ") }}]{% endif %} diff --git a/lib/src/docs/markdown/renderer.rs b/lib/src/docs/markdown/renderer.rs index 9772b3b9..c8aeecc3 100644 --- a/lib/src/docs/markdown/renderer.rs +++ b/lib/src/docs/markdown/renderer.rs @@ -3,8 +3,6 @@ use crate::docs::models::Spec; use crate::error::UsageErr; use itertools::Itertools; use serde::Serialize; -use std::collections::HashMap; -use xx::regex; #[derive(Debug, Clone)] pub struct MarkdownRenderer { @@ -60,7 +58,7 @@ impl MarkdownRenderer { } pub(crate) fn insert>(&mut self, key: S, val: &T) { - self.tera_ctx.insert(key, val); + self.tera_ctx.insert(key.into(), val); } fn tera_ctx(&self) -> tera::Context { @@ -79,7 +77,10 @@ impl MarkdownRenderer { let html_encode = self.html_encode; tera.register_filter( "escape_md", - move |value: &tera::Value, _: &HashMap| { + move |value: &tera::Value, + _: tera::Kwargs, + _: &tera::State| + -> tera::TeraResult { let value = value.as_str().unwrap(); let value = value .lines() @@ -99,41 +100,9 @@ impl MarkdownRenderer { .to_string() }) .join("\n"); - Ok(value.into()) + Ok(value) }, ); - let path_re = - regex!(r"https://(github.com/[^/]+/[^/]+|gitlab.com/[^/]+/[^/]+/-)/blob/[^/]+/"); - tera.register_function("source_code_link", |args: &HashMap| { - let spec = args.get("spec").unwrap().as_object().unwrap(); - let cmd = args.get("cmd").unwrap().as_object().unwrap(); - let full_cmd = cmd.get("full_cmd").unwrap().as_array(); - let source_code_link_template = spec - .get("source_code_link_template") - .and_then(|v| v.as_str()); - if let (Some(full_cmd), Some(source_code_link_template)) = - (full_cmd, source_code_link_template) - { - if full_cmd.is_empty() { - return Ok("".into()); - } - let mut ctx = tera::Context::new(); - let path = full_cmd.iter().map(|v| v.as_str().unwrap()).join("/"); - ctx.insert("spec", spec); - ctx.insert("cmd", cmd); - ctx.insert("path", &path); - let href = TERA.clone().render_str(source_code_link_template, &ctx)?; - let friendly = path_re.replace_all(&href, "").to_string(); - let link = if path_re.is_match(&href) { - format!("[`{friendly}`]({href})") - } else { - format!("[{friendly}]({href})") - }; - Ok(link.into()) - } else { - Ok("".into()) - } - }); Ok(tera.render(template_name, &self.tera_ctx())?) } diff --git a/lib/src/docs/markdown/templates/cmd_template.md.tera b/lib/src/docs/markdown/templates/cmd_template.md.tera index 98771239..678d1079 100644 --- a/lib/src/docs/markdown/templates/cmd_template.md.tera +++ b/lib/src/docs/markdown/templates/cmd_template.md.tera @@ -1,14 +1,14 @@ {%- set source_code_link = source_code_link(spec=spec, cmd=cmd) %} {%- set full_cmd = cmd.full_cmd | join(sep=" ") %} {%- if multi -%} -{{- "#" | repeat(count=header_level) }} `{{ spec.bin ~ " " ~ full_cmd | trim }}` +{{- "#" | repeat(count=header_level) }} `{{ (spec.bin ~ " " ~ full_cmd) | trim }}` {%- endif %} {%- if cmd.before_help_md %} {{ cmd.before_help_md | escape_md }} {%- endif %} -- **Usage**: `{{ spec.bin ~ " " ~ cmd.usage | trim }}` +- **Usage**: `{{ (spec.bin ~ " " ~ cmd.usage) | trim }}` {%- if cmd.aliases %} - **Aliases**: `{{ cmd.aliases | join(sep="`, `") }}` {%- endif %} @@ -85,13 +85,13 @@ {%- endif %} {%- if multi %} -{%- for name, cmd in cmd.subcommands %} +{%- for cmd in cmd.subcommands | values | sort(attribute="usage") %} {%- if not cmd.hide or cmd.hide == false %} {%- if loop.first %} {{ "#" | repeat(count=header_level) }}# Subcommands {% endif %} -- [`{{ spec.bin ~ " " ~ cmd.usage | trim }}`]({{ url_prefix }}/{{ cmd.full_cmd | join(sep="/") }}.md) +- [`{{ (spec.bin ~ " " ~ cmd.usage) | trim }}`]({{ url_prefix }}/{{ cmd.full_cmd | join(sep="/") }}.md) {%- endif -%} {%- endfor -%} {%- endif -%} diff --git a/lib/src/docs/markdown/templates/index_template.md.tera b/lib/src/docs/markdown/templates/index_template.md.tera index 527f3dbb..7df3f746 100644 --- a/lib/src/docs/markdown/templates/index_template.md.tera +++ b/lib/src/docs/markdown/templates/index_template.md.tera @@ -1,9 +1,9 @@ {%- set about = spec.about_long | default(value=spec.about | default(value="")) %} {%- set cmd = spec.cmd %} {%- set full_cmd = cmd.full_cmd | join(sep=" ") %} -{{- "#" | repeat(count=header_level) }} `{{ spec.bin ~ " " ~ full_cmd | trim }}` +{{- "#" | repeat(count=header_level) }} `{{ (spec.bin ~ " " ~ full_cmd) | trim }}` -**Usage**: `{{ spec.bin ~ " " ~ cmd.usage | trim }}` +**Usage**: `{{ (spec.bin ~ " " ~ cmd.usage) | trim }}` {%- if spec.version %} @@ -19,5 +19,5 @@ {{ "#" | repeat(count=header_level) }} Subcommands {% endif %} -- [`{{ spec.bin ~ " " ~ cmd.usage | trim }}`]({{ url_prefix }}/{{ cmd.full_cmd | join(sep="/") }}.md) +- [`{{ (spec.bin ~ " " ~ cmd.usage) | trim }}`]({{ url_prefix }}/{{ cmd.full_cmd | join(sep="/") }}.md) {%- endfor -%} diff --git a/lib/src/docs/markdown/templates/spec_template.md.tera b/lib/src/docs/markdown/templates/spec_template.md.tera index ce190640..428d70f3 100644 --- a/lib/src/docs/markdown/templates/spec_template.md.tera +++ b/lib/src/docs/markdown/templates/spec_template.md.tera @@ -1,7 +1,7 @@ {%- set about = spec.about_long | default(value=spec.about | default(value="")) %} {%- set cmd = spec.cmd %} {%- set full_cmd = cmd.full_cmd | join(sep=" ") %} -{{- "#" | repeat(count=header_level) }} `{{ spec.bin ~ " " ~ full_cmd | trim }}` +{{- "#" | repeat(count=header_level) }} `{{ (spec.bin ~ " " ~ full_cmd) | trim }}` {%- if spec.version %} - **version**: {{ spec.version }}{% endif %} @@ -37,7 +37,7 @@ {%- for cmd in all_commands %} {%- set full_cmd = cmd.full_cmd | join(sep=" ") %} -{{ "#" | repeat(count=header_level) }} `{{ spec.bin ~ " " ~ full_cmd | trim }}` +{{ "#" | repeat(count=header_level) }} `{{ (spec.bin ~ " " ~ full_cmd) | trim }}` {%- include "cmd_template.md.tera" %} {%- endfor -%} diff --git a/lib/src/docs/markdown/tera.rs b/lib/src/docs/markdown/tera.rs index cde41581..20a9ec9a 100644 --- a/lib/src/docs/markdown/tera.rs +++ b/lib/src/docs/markdown/tera.rs @@ -1,10 +1,152 @@ -use std::collections::HashMap; +use itertools::Itertools; use std::sync::LazyLock; use tera::Tera; +use xx::regex; pub(crate) static TERA: LazyLock = LazyLock::new(|| { let mut tera = Tera::default(); + tera.register_filter( + "repeat", + move |value: &tera::Value, + args: tera::Kwargs, + _: &tera::State| + -> tera::TeraResult { + let value = value.as_str().unwrap(); + let count = args.must_get::("count")?; + Ok(value.repeat(count as usize)) + }, + ); + tera.register_filter( + "default", + |value: &tera::Value, + kwargs: tera::Kwargs, + _: &tera::State| + -> tera::TeraResult { + let default_val = kwargs.must_get::("value")?; + let boolean = kwargs.get::("boolean")?.unwrap_or_default(); + if value.is_undefined() || value.is_none() || (boolean && !value.is_truthy()) { + Ok(default_val) + } else { + Ok(value.clone()) + } + }, + ); + tera.register_filter( + "filter", + |value: &[tera::Value], + args: tera::Kwargs, + _: &tera::State| + -> tera::TeraResult { + let attr = args.must_get::("attribute")?; + let expected = args + .get::("value")? + .unwrap_or_else(tera::Value::none); + let values = value + .iter() + .filter(|item| { + let Some(actual) = item.get_from_path(&attr) else { + return false; + }; + if expected.is_none() { + !actual.is_none() + } else { + actual == &expected + } + }) + .cloned() + .collect_vec(); + Ok(tera::Value::from(values)) + }, + ); + tera.register_filter( + "slice", + |value: &[tera::Value], + args: tera::Kwargs, + _: &tera::State| + -> tera::TeraResult { + fn index(idx: i64, len: usize) -> usize { + if idx < 0 { + len.saturating_sub(idx.unsigned_abs() as usize) + } else { + idx as usize + } + } + + let start = args + .get::("start")? + .map(|idx| index(idx, value.len())) + .unwrap_or_default(); + let end = args + .get::("end")? + .map(|idx| index(idx, value.len())) + .unwrap_or(value.len()) + .min(value.len()); + if start >= end { + return Ok(tera::Value::from(Vec::::new())); + } + Ok(tera::Value::from(value[start..end].to_vec())) + }, + ); + tera.register_filter( + "concat", + |value: &[tera::Value], + args: tera::Kwargs, + _: &tera::State| + -> tera::TeraResult { + let extra = args.must_get::("with")?; + let mut values = value.to_vec(); + if let Some(extra_values) = extra.as_array() { + values.extend(extra_values.iter().cloned()); + } else { + values.push(extra); + } + Ok(tera::Value::from(values)) + }, + ); + tera.register_filter( + "escape_md", + |value: &tera::Value, _: tera::Kwargs, _: &tera::State| -> tera::TeraResult { + Ok(value.clone()) + }, + ); + let path_re = regex!(r"https://(github.com/[^/]+/[^/]+|gitlab.com/[^/]+/[^/]+/-)/blob/[^/]+/"); + tera.register_function( + "source_code_link", + move |args: tera::Kwargs, _: &tera::State| -> tera::TeraResult { + let spec = args.must_get::<&tera::Value>("spec")?; + let cmd = args.must_get::<&tera::Value>("cmd")?; + let full_cmd = cmd.get_from_path("full_cmd").and_then(|v| v.as_array()); + let source_code_link_template = spec + .get_from_path("source_code_link_template") + .and_then(|v| v.as_str()); + if let (Some(full_cmd), Some(source_code_link_template)) = + (full_cmd, source_code_link_template) + { + if full_cmd.is_empty() { + return Ok(String::new()); + } + let mut ctx = tera::Context::new(); + let path = full_cmd.iter().map(|v| v.as_str().unwrap()).join("/"); + ctx.insert_value("spec", spec.clone()); + ctx.insert_value("cmd", cmd.clone()); + ctx.insert("path", &path); + let href = TERA + .clone() + .render_str(source_code_link_template, &ctx, false)?; + let friendly = path_re.replace_all(&href, "").to_string(); + let link = if path_re.is_match(&href) { + format!("[`{friendly}`]({href})") + } else { + format!("[{friendly}]({href})") + }; + Ok(link) + } else { + Ok(String::new()) + } + }, + ); + #[rustfmt::skip] tera.add_raw_templates([ ("arg_template.md.tera", include_str!("templates/arg_template.md.tera")), @@ -14,14 +156,5 @@ pub(crate) static TERA: LazyLock = LazyLock::new(|| { ("index_template.md.tera", include_str!("templates/index_template.md.tera")), ]).unwrap(); - tera.register_filter( - "repeat", - move |value: &tera::Value, args: &HashMap| { - let value = value.as_str().unwrap(); - let count = args.get("count").unwrap().as_u64().unwrap(); - Ok(value.repeat(count as usize).into()) - }, - ); - tera });