diff --git a/Cargo.lock b/Cargo.lock index 00314a4..6375bbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.7" @@ -74,6 +89,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "backtrace" version = "0.3.69" @@ -98,12 +119,43 @@ dependencies = [ "backtrace", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[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 = "bstr" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + [[package]] name = "cc" version = "1.0.83" @@ -119,6 +171,40 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets 0.48.5", +] + +[[package]] +name = "chrono-tz" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d7b79e99bfaa0d47da0687c43aa3b7381938a62ad3a6498599039321f660b7" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + [[package]] name = "clap" version = "4.4.16" @@ -188,6 +274,72 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deunicode" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae2a35373c5c74340b79ae6780b498b2b183915ec5dacf263aac5a099bf485a" + +[[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 = "either" version = "1.9.0" @@ -260,12 +412,57 @@ dependencies = [ "libc", ] +[[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.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -284,12 +481,60 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "iana-time-zone" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "2.1.0" @@ -298,6 +543,7 @@ checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown", + "serde", ] [[package]] @@ -339,6 +585,21 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "kdl" version = "4.6.0" @@ -362,6 +623,12 @@ version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -443,6 +710,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.32.2" @@ -464,6 +740,110 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "parse-zoneinfo" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +dependencies = [ + "regex", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "pest_meta" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.76" @@ -482,6 +862,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[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", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "regex" version = "1.10.2" @@ -523,7 +933,7 @@ version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ - "bitflags", + "bitflags 2.4.1", "errno 0.3.8", "libc", "linux-raw-sys", @@ -536,6 +946,63 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "serde_json" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shell-escape" version = "0.1.5" @@ -548,6 +1015,22 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slug" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4" +dependencies = [ + "deunicode", + "wasm-bindgen", +] + [[package]] name = "smawk" version = "0.3.2" @@ -632,6 +1115,28 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tera" +version = "1.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970dff17c11e884a4a09bc76e3a17ef71e01bb13447a11e85226e254fe6d10b8" +dependencies = [ + "chrono", + "chrono-tz", + "globwalk", + "humansize", + "lazy_static", + "percent-encoding", + "pest", + "pest_derive", + "rand", + "regex", + "serde", + "serde_json", + "slug", + "unic-segment", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -682,6 +1187,68 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" +dependencies = [ + "unic-ucd-segment", +] + +[[package]] +name = "unic-ucd-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -715,6 +1282,7 @@ dependencies = [ "once_cell", "regex", "strum", + "tera", "thiserror", "usage-lib", "xx", @@ -732,6 +1300,7 @@ dependencies = [ "log", "miette", "once_cell", + "serde", "shell-escape", "strum", "thiserror", @@ -744,6 +1313,82 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" + [[package]] name = "winapi" version = "0.3.9" @@ -775,13 +1420,37 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -790,51 +1459,93 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index dd26b07..2564538 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,17 +17,18 @@ members = [ name = "usage" [dependencies] +clap = { version = "4", features = ["derive", "string"], optional = true } +indexmap = { version = "2", features = ["serde"] } +itertools = "0.12" kdl = "4" +log = "0.4" miette = "5" -thiserror = "1" -clap = { version = "4", features = ["derive", "string"], optional = true } -indexmap = "2" +once_cell = "1" +serde = { version = "1", features = ["derive"] } shell-escape = "0.1" -log = "0.4" -itertools = "0.12.0" -once_cell = "1.19.0" +strum = { version = "0.25", features = ["derive"] } +thiserror = "1" xx = "0.2" -strum = { version = "0.25.0", features = ["derive"] } [features] default = ["clap"] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 728b58f..94ccdb6 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -25,6 +25,7 @@ miette = { version = "5", features = ["fancy"] } once_cell = "1" regex = "1" strum = { version = "0.25.0", features = ["derive"] } +tera = "1.19.1" thiserror = "1" usage-lib = { path = "..", features=["clap"] } xx = "0.2" diff --git a/cli/src/cli/generate/markdown.rs b/cli/src/cli/generate/markdown.rs index a7ec944..a3b38f6 100644 --- a/cli/src/cli/generate/markdown.rs +++ b/cli/src/cli/generate/markdown.rs @@ -1,6 +1,5 @@ use std::fmt::{Display, Formatter}; use std::fs; -use std::iter::once; use std::path::{Path, PathBuf}; use std::sync::Mutex; @@ -9,6 +8,7 @@ use contracts::requires; use kdl::{KdlDocument, KdlNode}; use miette::{IntoDiagnostic, NamedSource, SourceOffset, SourceSpan}; use strum::EnumIs; +use tera::Tera; use thiserror::Error; use xx::{context, file}; @@ -147,6 +147,64 @@ impl Markdown { // } } +const USAGE_TITLE_TEMPLATE: &str = r#" +# {spec.name} +"#; + +const USAGE_OVERVIEW_TEMPLATE: &str = r#" +## Usage + +```bash +{{spec.bin}} [flags] [args] +``` +"#; + +static CONFIG_TEMPLATE: &str = r#" +### `!KEY!` + +!ENV! +!DEFAULT! + +!HELP! +!LONG_HELP! +"#; + +const COMMANDS_INDEX_TEMPLATE: &str = r#" +## Commands Index + +{% for cmd in commands -%} +* [`{{ cmd.full_cmd | join(sep=" ") }}`](#{{ cmd.full_cmd | join(sep=" ") | slugify }}) +{% endfor %} +"#; + +const COMMANDS_TEMPLATE: &str = r#" +### `{{ cmd.full_cmd | join(sep=" ") }}` + +{% if cmd.args -%} +#### Args + +{% for arg in cmd.args -%} +* `{{ arg.usage }}` – {{ arg.long_help | default(value=arg.help) }} +{% endfor -%} +{% endif -%} + +{% if cmd.flags -%} +#### Flags + +{% for flag in cmd.flags -%} +* `{{ flag.usage }}` – {{ flag.long_help | default(value=flag.help) }} +{% endfor -%} +{% endif -%} + +{% if cmd.help -%} +{{ cmd.help }} +{% endif -%} + +{% if cmd.long_help -%} +{{ cmd.long_help }} +{% endif -%} +"#; + #[derive(Debug, EnumIs)] #[strum(serialize_all = "snake_case")] enum UsageMdDirective { @@ -185,6 +243,7 @@ struct UsageMdContext { plain: bool, spec: Option, out: Mutex>, + tera: tera::Context, } impl UsageMdContext { @@ -193,6 +252,7 @@ impl UsageMdContext { plain: true, spec: None, out: Mutex::new(vec![]), + tera: tera::Context::new(), } } @@ -209,25 +269,27 @@ impl UsageMdDirective { match self { UsageMdDirective::Load { file } => { let file = context::prepend_load_root(file); - ctx.spec = Some(Spec::parse_file(&file)?.0); + let spec: Spec = Spec::parse_file(&file)?.0; + ctx.tera.insert("spec", &spec.clone()); + let commands: Vec<_> = gather_subcommands(&[&spec.cmd]) + .into_iter() + .filter(|c| !c.hide) + .collect(); + ctx.tera.insert("commands", &commands); + ctx.spec = Some(spec); ctx.push(self.to_string()); } UsageMdDirective::Title => { ensure!(ctx.spec.is_some(), "spec must be loaded before title"); ctx.plain = false; - let spec = ctx.spec.as_ref().unwrap(); ctx.push(self.to_string()); - ctx.push(format!("# {name}", name = spec.name)); + ctx.push(render_template(USAGE_TITLE_TEMPLATE, &ctx.tera)?); ctx.push("".to_string()); } UsageMdDirective::UsageOverview => { ctx.plain = false; - let spec = ctx.spec.as_ref().unwrap(); - ctx.push(self.to_string()); - ctx.push("```".to_string()); - ctx.push(format!("{bin} [flags] [args]", bin = spec.bin)); - ctx.push("```".to_string()); + ctx.push(render_template(USAGE_OVERVIEW_TEMPLATE, &ctx.tera)?); ctx.push("".to_string()); } UsageMdDirective::GlobalArgs => { @@ -279,16 +341,20 @@ impl UsageMdDirective { } UsageMdDirective::CommandIndex => { ctx.plain = false; - let spec = ctx.spec.as_ref().unwrap(); ctx.push(self.to_string()); - print_commands_index(&ctx, &[&spec.cmd])?; + ctx.push(render_template(COMMANDS_INDEX_TEMPLATE, &ctx.tera)?); ctx.push("".to_string()); } UsageMdDirective::Commands => { ctx.plain = false; let spec = ctx.spec.as_ref().unwrap(); ctx.push(self.to_string()); - print_commands(&ctx, &[&spec.cmd])?; + let commands = gather_subcommands(&[&spec.cmd]); + for cmd in &commands { + let mut tctx = ctx.tera.clone(); + tctx.insert("cmd", &cmd); + ctx.push(render_template(COMMANDS_TEMPLATE, &tctx)?); + } ctx.push("".to_string()); } UsageMdDirective::Config => { @@ -318,59 +384,26 @@ impl UsageMdDirective { } } -fn print_commands_index(ctx: &UsageMdContext, cmds: &[&SchemaCmd]) -> miette::Result<()> { - let subcommands = cmds[cmds.len() - 1] - .subcommands - .values() - .filter(|c| !c.hide) - .collect::>(); - for cmd in subcommands { - let cmds = cmds.iter().cloned().chain(once(cmd)).collect::>(); - let full_name = cmds - .iter() - .skip(1) - .map(|c| c.name.as_str()) - .collect::>() - .join(" "); - let slug = full_name.replace(' ', "-"); - ctx.push(format!("- [`{full_name}`](#{slug})",)); - print_commands_index(ctx, &cmds)?; - } - - Ok(()) +fn render_template(template: &str, tctx: &tera::Context) -> miette::Result { + let out = Tera::one_off(template, tctx, false).into_diagnostic()?; + Ok(out) } -fn print_commands(ctx: &UsageMdContext, cmds: &[&SchemaCmd]) -> miette::Result<()> { - let subcommands = cmds[cmds.len() - 1] - .subcommands - .values() - .filter(|c| !c.hide) - .collect::>(); - for cmd in subcommands { - let cmds = cmds.iter().cloned().chain(once(cmd)).collect::>(); - let full_name = cmds - .iter() - .skip(1) - .map(|c| c.name.as_str()) - .collect::>() - .join(" "); - ctx.push(format!("### `{full_name}`")); - print_commands(ctx, &cmds)?; +fn gather_subcommands(cmds: &[&SchemaCmd]) -> Vec { + let mut subcommands = vec![]; + for cmd in cmds { + if cmd.hide { + continue; + } + if !cmd.name.is_empty() { + subcommands.push((*cmd).clone()); + } + let more = gather_subcommands(&cmd.subcommands.values().collect::>()); + subcommands.extend(more); } - - Ok(()) + subcommands } -static CONFIG_TEMPLATE: &str = r#" -### `!KEY!` - -!ENV! -!DEFAULT! - -!HELP! -!LONG_HELP! -"#; - fn print_config(config: &SpecConfig) -> miette::Result { let mut all = vec![]; for (key, prop) in &config.props { @@ -384,14 +417,7 @@ fn print_config(config: &SpecConfig) -> miette::Result { tmpl("!ENV!", format!("* env: `{env}`")); // out = out.replace("!ENV!", &format!("* env: `{env}`")); } - if let Some(default) = prop - .default_note - .clone() - .or_else(|| match prop.default.is_null() { - true => None, - false => Some(prop.default.to_string()), - }) - { + if let Some(default) = prop.default_note.clone().or_else(|| prop.default.clone()) { tmpl("!DEFAULT!", format!("* default: `{default}`")); // out = out.replace("!DEFAULT!", &format!("* default: `{default}`")); } diff --git a/cli/src/cli/generate/mod.rs b/cli/src/cli/generate/mod.rs index c7fd452..5245fc5 100644 --- a/cli/src/cli/generate/mod.rs +++ b/cli/src/cli/generate/mod.rs @@ -1,4 +1,5 @@ use std::path::PathBuf; +use usage::error::UsageErr; use usage::Spec; @@ -27,7 +28,7 @@ impl Generate { } } -pub fn file_or_spec(file: &Option, spec: &Option) -> miette::Result { +pub fn file_or_spec(file: &Option, spec: &Option) -> Result { if let Some(file) = file { let (spec, _) = Spec::parse_file(file)?; Ok(spec) diff --git a/examples/MISE_README.md b/examples/MISE_README.md index 797927d..88d5c36 100644 --- a/examples/MISE_README.md +++ b/examples/MISE_README.md @@ -1,12 +1,17 @@ -# mise + +# {spec.name} + -## Usage -``` + +## Usage + +```bash mise.usage.kdl [flags] [args] ``` + ## Global Args @@ -18,160 +23,745 @@ mise.usage.kdl [flags] [args] - `-v,--verbose`: Show extra output (use -vv for even more) - `-y,--yes`: Answer yes to all confirmation prompts -## Commands Index +## Config + +### `activate_accessive` + +* env: `MISE_ACTIVATE_ACCESSIVE` +* default: `false` + +foooooooo + +### `color` + +* env: `MISE_COLOR` +* default: `true` + +### `jobs` + +* default: `4` + +### `timeout` + +* default: `1.5` + +### `user` + +* default: `"admin"` + + -- [`activate`](#activate) -- [`alias`](#alias) -- [`alias get`](#alias-get) -- [`alias ls`](#alias-ls) -- [`alias set`](#alias-set) -- [`alias unset`](#alias-unset) -- [`bin-paths`](#bin-paths) -- [`cache`](#cache) -- [`cache clear`](#cache-clear) -- [`completion`](#completion) -- [`config`](#config) -- [`config ls`](#config-ls) -- [`config generate`](#config-generate) -- [`current`](#current) -- [`deactivate`](#deactivate) -- [`direnv`](#direnv) -- [`direnv activate`](#direnv-activate) -- [`doctor`](#doctor) -- [`env`](#env) -- [`exec`](#exec) -- [`implode`](#implode) -- [`install`](#install) -- [`latest`](#latest) -- [`link`](#link) -- [`ls`](#ls) -- [`ls-remote`](#ls-remote) -- [`outdated`](#outdated) -- [`plugins`](#plugins) -- [`plugins install`](#plugins-install) -- [`plugins link`](#plugins-link) -- [`plugins ls`](#plugins-ls) -- [`plugins ls-remote`](#plugins-ls-remote) -- [`plugins uninstall`](#plugins-uninstall) -- [`plugins update`](#plugins-update) -- [`prune`](#prune) -- [`reshim`](#reshim) -- [`run`](#run) -- [`self-update`](#self-update) -- [`set`](#set) -- [`settings`](#settings) -- [`settings get`](#settings-get) -- [`settings ls`](#settings-ls) -- [`settings set`](#settings-set) -- [`settings unset`](#settings-unset) -- [`shell`](#shell) -- [`sync`](#sync) -- [`sync node`](#sync-node) -- [`sync python`](#sync-python) -- [`task`](#task) -- [`task edit`](#task-edit) -- [`task ls`](#task-ls) -- [`task run`](#task-run) -- [`trust`](#trust) -- [`uninstall`](#uninstall) -- [`unset`](#unset) -- [`upgrade`](#upgrade) -- [`usage`](#usage) -- [`use`](#use) -- [`version`](#version) -- [`watch`](#watch) -- [`where`](#where) -- [`which`](#which) + +## Commands Index + +* [`activate`](#activate) +* [`alias`](#alias) +* [`alias get`](#alias-get) +* [`alias ls`](#alias-ls) +* [`alias set`](#alias-set) +* [`alias unset`](#alias-unset) +* [`bin-paths`](#bin-paths) +* [`cache`](#cache) +* [`cache clear`](#cache-clear) +* [`completion`](#completion) +* [`config`](#config) +* [`config ls`](#config-ls) +* [`config generate`](#config-generate) +* [`current`](#current) +* [`deactivate`](#deactivate) +* [`direnv`](#direnv) +* [`direnv activate`](#direnv-activate) +* [`doctor`](#doctor) +* [`env`](#env) +* [`exec`](#exec) +* [`implode`](#implode) +* [`install`](#install) +* [`latest`](#latest) +* [`link`](#link) +* [`ls`](#ls) +* [`ls-remote`](#ls-remote) +* [`outdated`](#outdated) +* [`plugins`](#plugins) +* [`plugins install`](#plugins-install) +* [`plugins link`](#plugins-link) +* [`plugins ls`](#plugins-ls) +* [`plugins ls-remote`](#plugins-ls-remote) +* [`plugins uninstall`](#plugins-uninstall) +* [`plugins update`](#plugins-update) +* [`prune`](#prune) +* [`reshim`](#reshim) +* [`run`](#run) +* [`self-update`](#self-update) +* [`set`](#set) +* [`settings`](#settings) +* [`settings get`](#settings-get) +* [`settings ls`](#settings-ls) +* [`settings set`](#settings-set) +* [`settings unset`](#settings-unset) +* [`shell`](#shell) +* [`sync`](#sync) +* [`sync node`](#sync-node) +* [`sync python`](#sync-python) +* [`task`](#task) +* [`task edit`](#task-edit) +* [`task ls`](#task-ls) +* [`task run`](#task-run) +* [`trust`](#trust) +* [`uninstall`](#uninstall) +* [`unset`](#unset) +* [`upgrade`](#upgrade) +* [`usage`](#usage) +* [`use`](#use) +* [`version`](#version) +* [`watch`](#watch) +* [`where`](#where) +* [`which`](#which) + + ## Commands + ### `activate` + +#### Args + +* `[SHELL_TYPE]` – Shell type to generate the script for +#### Flags + +* `-s,--shell ` – Shell type to generate the script for +* `--status` – Show "mise: @" message when changing directories +* `-q,--quiet` – Suppress non-error messages + + ### `alias` + +#### Flags + +* `-p,--plugin ` – filter aliases by plugin +* `--no-header` – Don't show table header + + ### `alias get` + +#### Args + +* `` – The plugin to show the alias for +* `` – The alias to show + + ### `alias ls` + +#### Args + +* `[PLUGIN]` – Show aliases for +#### Flags + +* `--no-header` – Don't show table header + + ### `alias set` + +#### Args + +* `` – The plugin to set the alias for +* `` – The alias to set +* `` – The value to set the alias to + + ### `alias unset` + +#### Args + +* `` – The plugin to remove the alias from +* `` – The alias to remove + + ### `bin-paths` + + + ### `cache` + + + ### `cache clear` + +#### Args + +* `[PLUGIN]...` – Plugin(s) to clear cache for e.g.: node, python + + ### `completion` + +#### Args + +* `[SHELL]` – Shell type to generate completions for +#### Flags + +* `-s,--shell ` – Shell type to generate completions for + + ### `config` + +#### Flags + +* `--no-header` – Do not print table header + + ### `config ls` + +#### Flags + +* `--no-header` – Do not print table header + + ### `config generate` + +#### Flags + +* `-o,--output ` – Output to file instead of stdout + + ### `current` + +#### Args + +* `[PLUGIN]` – Plugin to show versions of e.g.: ruby, node, cargo:eza, npm:prettier, etc + + ### `deactivate` + + + ### `direnv` + + + ### `direnv activate` + + + ### `doctor` + + + ### `env` + +#### Args + +* `[TOOL@VERSION]...` – Tool(s) to use +#### Flags + +* `-s,--shell ` – Shell type to generate environment variables for +* `-J,--json` – Output in JSON format + + ### `exec` + +#### Args + +* `[TOOL@VERSION]...` – Tool(s) to start e.g.: node@20 python@3.10 +* `[COMMAND]...` – Command string to execute (same as --command) +#### Flags + +* `-c,--command ` – Command string to execute +* `-j,--jobs ` – Number of jobs to run in parallel +[default: 4] +* `--raw` – Directly pipe stdin/stdout/stderr from plugin to user Sets --jobs=1 + + ### `implode` + +#### Flags + +* `--config` – Also remove config directory +* `-n,--dry-run` – List directories that would be removed without actually removing them + + ### `install` + +#### Args + +* `[TOOL@VERSION]...` – Tool(s) to install e.g.: node@20 +#### Flags + +* `-f,--force` – Force reinstall even if already installed +* `-j,--jobs ` – Number of jobs to run in parallel +[default: 4] +* `--raw` – Directly pipe stdin/stdout/stderr from plugin to user Sets --jobs=1 +* `-v,--verbose` – Show installation output + + ### `latest` + +#### Args + +* `` – Tool to get the latest version of +* `[ASDF_VERSION]` – The version prefix to use when querying the latest version same as the first argument after the "@" used for asdf compatibility +#### Flags + +* `-i,--installed` – Show latest installed instead of available version + + ### `link` + +#### Args + +* `` – Tool name and version to create a symlink for +* `` – The local path to the tool version +e.g.: ~/.nvm/versions/node/v20.0.0 +#### Flags + +* `-f,--force` – Overwrite an existing tool version if it exists + + ### `ls` + +#### Args + +* `[PLUGIN]...` – Only show tool versions from [PLUGIN] +#### Flags + +* `-p,--plugin ` – +* `-c,--current` – Only show tool versions currently specified in a .tool-versions/.mise.toml +* `-g,--global` – Only show tool versions currently specified in a the global .tool-versions/.mise.toml +* `-i,--installed` – Only show tool versions that are installed Hides missing ones defined in .tool-versions/.mise.toml but not yet installed +* `--parseable` – Output in an easily parseable format +* `-J,--json` – Output in json format +* `-m,--missing` – Display missing tool versions +* `--prefix ` – Display versions matching this prefix +* `--no-header` – Don't display headers + + ### `ls-remote` + +#### Args + +* `[TOOL@VERSION]` – Plugin to get versions for +* `[PREFIX]` – The version prefix to use when querying the latest version +same as the first argument after the "@" +#### Flags + +* `--all` – Show all installed plugins and versions + + ### `outdated` + +#### Args + +* `[TOOL@VERSION]...` – Tool(s) to show outdated versions for +e.g.: node@20 python@3.10 +If not specified, all tools in global and local configs will be shown + + ### `plugins` + +#### Flags + +* `-a,--all` – list all available remote plugins + +same as `mise plugins ls-remote` +* `-c,--core` – The built-in plugins only +Normally these are not shown +* `--user` – List installed plugins + +This is the default behavior but can be used with --core +to show core and user plugins +* `-u,--urls` – Show the git url for each plugin +e.g.: https://github.com/asdf-vm/asdf-node.git +* `--refs` – Show the git refs for each plugin +e.g.: main 1234abc + + ### `plugins install` + +#### Args + +* `[NEW_PLUGIN]` – The name of the plugin to install +e.g.: node, ruby +Can specify multiple plugins: `mise plugins install node ruby python` +* `[GIT_URL]` – The git url of the plugin +* `[REST]...` – +#### Flags + +* `-f,--force` – Reinstall even if plugin exists +* `-a,--all` – Install all missing plugins +This will only install plugins that have matching shorthands. +i.e.: they don't need the full git repo url +* `-v,--verbose` – Show installation output + + ### `plugins link` + +#### Args + +* `` – The name of the plugin +e.g.: node, ruby +* `[PATH]` – The local path to the plugin +e.g.: ./mise-node +#### Flags + +* `-f,--force` – Overwrite existing plugin + + ### `plugins ls` + +#### Flags + +* `-a,--all` – List all available remote plugins +Same as `mise plugins ls-remote` +* `-c,--core` – The built-in plugins only +Normally these are not shown +* `--user` – List installed plugins + +This is the default behavior but can be used with --core +to show core and user plugins +* `-u,--urls` – Show the git url for each plugin +e.g.: https://github.com/asdf-vm/asdf-node.git +* `--refs` – Show the git refs for each plugin +e.g.: main 1234abc + + ### `plugins ls-remote` + +#### Flags + +* `-u,--urls` – Show the git url for each plugin e.g.: https://github.com/mise-plugins/rtx-nodejs.git +* `--only-names` – Only show the name of each plugin by default it will show a "*" next to installed plugins + + ### `plugins uninstall` + +#### Args + +* `[PLUGIN]...` – Plugin(s) to remove +#### Flags + +* `-p,--purge` – Also remove the plugin's installs, downloads, and cache +* `-a,--all` – Remove all plugins + + ### `plugins update` + +#### Args + +* `[PLUGIN]...` – Plugin(s) to update +#### Flags + +* `-j,--jobs ` – Number of jobs to run in parallel +Default: 4 + + ### `prune` + +#### Args + +* `[PLUGIN]...` – Prune only versions from this plugin(s) +#### Flags + +* `-n,--dry-run` – Do not actually delete anything + + ### `reshim` + +#### Args + +* `[PLUGIN]` – +* `[VERSION]` – + + ### `run` + +#### Args + +* `[TASK]` – Task to run +Can specify multiple tasks by separating with `:::` +e.g.: mise run task1 arg1 arg2 ::: task2 arg1 arg2 +* `[ARGS]...` – Arguments to pass to the task. Use ":::" to separate tasks +#### Flags + +* `-C,--cd ` – Change to this directory before executing the command +* `-n,--dry-run` – Don't actually run the task(s), just print them in order of execution +* `-f,--force` – Force the task to run even if outputs are up to date +* `-p,--prefix` – Print stdout/stderr by line, prefixed with the task's label +Defaults to true if --jobs > 1 +Configure with `task_output` config or `MISE_TASK_OUTPUT` env var +* `-i,--interleave` – Print directly to stdout/stderr instead of by line +Defaults to true if --jobs == 1 +Configure with `task_output` config or `MISE_TASK_OUTPUT` env var +* `-t,--tool ` – Tool(s) to also add e.g.: node@20 python@3.10 +* `-j,--jobs ` – Number of tasks to run in parallel +[default: 4] +Configure with `jobs` config or `MISE_JOBS` env var +* `-r,--raw` – Read/write directly to stdin/stdout/stderr instead of by line +Configure with `raw` config or `MISE_RAW` env var + + ### `self-update` + +#### Args + +* `[VERSION]` – Update to a specific version +#### Flags + +* `-f,--force` – Update even if already up to date +* `--no-plugins` – Disable auto-updating plugins +* `-y,--yes` – Skip confirmation prompt + + ### `set` + +#### Args + +* `[ENV_VARS]...` – Environment variable(s) to set +e.g.: NODE_ENV=production +#### Flags + +* `--file ` – The TOML file to update + +Defaults to MISE_DEFAULT_CONFIG_FILENAME environment variable, or ".mise.toml". +* `-g,--global` – Set the environment variable in the global config file +* `--remove ` – Remove the environment variable from config file + +Can be used multiple times. + + ### `settings` + + + ### `settings get` + +#### Args + +* `` – The setting to show + + ### `settings ls` + + + ### `settings set` + +#### Args + +* `` – The setting to set +* `` – The value to set + + ### `settings unset` + +#### Args + +* `` – The setting to remove + + ### `shell` + +#### Args + +* `[TOOL@VERSION]...` – Tool(s) to use +#### Flags + +* `-j,--jobs ` – Number of jobs to run in parallel +[default: 4] +* `--raw` – Directly pipe stdin/stdout/stderr from plugin to user Sets --jobs=1 +* `-u,--unset` – Removes a previously set version + + ### `sync` + + + ### `sync node` + +#### Flags + +* `--brew` – Get tool versions from Homebrew +* `--nvm` – Get tool versions from nvm +* `--nodenv` – Get tool versions from nodenv + + ### `sync python` + +#### Flags + +* `--pyenv` – Get tool versions from pyenv + + ### `task` + +#### Flags + +* `--no-header` – Do not print table header +* `--hidden` – Show hidden tasks + + ### `task edit` + +#### Args + +* `` – Task to edit +#### Flags + +* `-p,--path` – Display the path to the task instead of editing it + + ### `task ls` + +#### Flags + +* `--no-header` – Do not print table header +* `--hidden` – Show hidden tasks + + ### `task run` + +#### Args + +* `[TASK]` – Task to run +Can specify multiple tasks by separating with `:::` +e.g.: mise run task1 arg1 arg2 ::: task2 arg1 arg2 +* `[ARGS]...` – Arguments to pass to the task. Use ":::" to separate tasks +#### Flags + +* `-C,--cd ` – Change to this directory before executing the command +* `-n,--dry-run` – Don't actually run the task(s), just print them in order of execution +* `-f,--force` – Force the task to run even if outputs are up to date +* `-p,--prefix` – Print stdout/stderr by line, prefixed with the task's label +Defaults to true if --jobs > 1 +Configure with `task_output` config or `MISE_TASK_OUTPUT` env var +* `-i,--interleave` – Print directly to stdout/stderr instead of by line +Defaults to true if --jobs == 1 +Configure with `task_output` config or `MISE_TASK_OUTPUT` env var +* `-t,--tool ` – Tool(s) to also add e.g.: node@20 python@3.10 +* `-j,--jobs ` – Number of tasks to run in parallel +[default: 4] +Configure with `jobs` config or `MISE_JOBS` env var +* `-r,--raw` – Read/write directly to stdin/stdout/stderr instead of by line +Configure with `raw` config or `MISE_RAW` env var + + ### `trust` + +#### Args + +* `[CONFIG_FILE]` – The config file to trust +#### Flags + +* `-a,--all` – Trust all config files in the current directory and its parents +* `--untrust` – No longer trust this config + + ### `uninstall` + +#### Args + +* `[TOOL@VERSION]...` – Tool(s) to remove +#### Flags + +* `-a,--all` – Delete all installed versions +* `-n,--dry-run` – Do not actually delete anything + + ### `unset` + +#### Args + +* `[KEYS]...` – Environment variable(s) to remove +e.g.: NODE_ENV +#### Flags + +* `-f,--file ` – Specify a file to use instead of ".mise.toml" +* `-g,--global` – Use the global config file + + ### `upgrade` + +#### Args + +* `[TOOL@VERSION]...` – Tool(s) to upgrade +e.g.: node@20 python@3.10 +If not specified, all current tools will be upgraded +#### Flags + +* `-n,--dry-run` – Just print what would be done, don't actually do it +* `-j,--jobs ` – Number of jobs to run in parallel +[default: 4] +* `-i,--interactive` – Display multiselect menu to choose which tools to upgrade +* `--raw` – Directly pipe stdin/stdout/stderr from plugin to user Sets --jobs=1 + + ### `usage` + + + ### `use` + +#### Args + +* `[TOOL@VERSION]...` – Tool(s) to add to config file +e.g.: node@20, cargo:ripgrep@latest npm:prettier@3 +If no version is specified, it will default to @latest +#### Flags + +* `-f,--force` – Force reinstall even if already installed +* `--fuzzy` – Save fuzzy version to config file +e.g.: `mise use --fuzzy node@20` will save 20 as the version +this is the default behavior unless MISE_ASDF_COMPAT=1 +* `-g,--global` – Use the global config file (~/.config/mise/config.toml) instead of the local one +* `-e,--env ` – Modify an environment-specific config file like .mise..toml +* `-j,--jobs ` – Number of jobs to run in parallel +[default: 4] +* `--raw` – Directly pipe stdin/stdout/stderr from plugin to user Sets --jobs=1 +* `--remove ` – Remove the plugin(s) from config file +* `-p,--path ` – Specify a path to a config file or directory If a directory is specified, it will look for .mise.toml (default) or .tool-versions +* `--pin` – Save exact version to config file +e.g.: `mise use --pin node@20` will save 20.0.0 as the version +Set MISE_ASDF_COMPAT=1 to make this the default behavior + + ### `version` + + + ### `watch` -### `where` -### `which` - -## Config - -### `activate_accessive` -* env: `MISE_ACTIVATE_ACCESSIVE` -* default: `false` +#### Args -foooooooo +* `[ARGS]...` – Extra arguments +#### Flags -### `color` +* `-t,--task ` – Task to run +* `-g,--glob ` – Files to watch +Defaults to sources from the task(s) -* env: `MISE_COLOR` -* default: `true` -### `jobs` +### `where` -* default: `4` +#### Args -### `timeout` +* `` – Tool(s) to look up +e.g.: ruby@3 +if "@" is specified, it will show the latest installed version +that matches the prefix +otherwise, it will show the current, active installed version +* `[ASDF_VERSION]` – the version prefix to use when querying the latest version +same as the first argument after the "@" +used for asdf compatibility -* default: `1.5` -### `user` +### `which` -* default: `"admin"` +#### Args + +* `` – The bin name to look up +#### Flags + +* `--plugin` – Show the plugin name instead of the path +* `--version` – Show the version instead of the path +* `-t,--tool ` – Use a specific tool@version +e.g.: `mise which npm --tool=node@20` diff --git a/src/error.rs b/src/error.rs index b4076f9..6e43ac1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -14,6 +14,10 @@ pub enum UsageErr { #[source_code] NamedSource, ), + #[error("Invalid usage config")] + #[diagnostic(transparent)] + Miette(#[from] miette::MietteError), + #[error(transparent)] IO(#[from] std::io::Error), @@ -23,6 +27,10 @@ pub enum UsageErr { #[error(transparent)] #[diagnostic(transparent)] KdlError(#[from] kdl::KdlError), + + #[error(transparent)] + #[diagnostic(transparent)] + XXError(#[from] xx::error::XXError), } impl UsageErr { diff --git a/src/lib.rs b/src/lib.rs index f0ff007..3618dd9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate log; -#[macro_use] -extern crate miette; +// #[macro_use] +// extern crate miette; #[cfg(test)] #[macro_use] extern crate insta; diff --git a/src/parse/arg.rs b/src/parse/arg.rs index 84f1514..51ed1c3 100644 --- a/src/parse/arg.rs +++ b/src/parse/arg.rs @@ -3,13 +3,15 @@ use std::str::FromStr; #[cfg(feature = "clap")] use itertools::Itertools; use kdl::{KdlEntry, KdlNode}; +use serde::Serialize; use crate::error::UsageErr; use crate::parse::helpers::NodeHelper; -#[derive(Debug, Default)] +#[derive(Debug, Default, Serialize, Clone)] pub struct Arg { pub name: String, + pub usage: String, pub help: Option, pub long_help: Option, pub required: bool, @@ -81,6 +83,7 @@ impl TryFrom<&KdlNode> for Arg { k => bail_parse!(v.entry, "unsupported key {k}"), } } + arg.usage = arg.usage(); Ok(arg) } } @@ -137,6 +140,7 @@ impl From<&clap::Arg> for Arg { .cloned() .unwrap_or_default() .to_string(), + usage: "".into(), required, help, long_help, diff --git a/src/parse/cmd.rs b/src/parse/cmd.rs index 74f588e..8f9a85e 100644 --- a/src/parse/cmd.rs +++ b/src/parse/cmd.rs @@ -1,11 +1,13 @@ use crate::error::UsageErr; use crate::parse::helpers::NodeHelper; -use crate::{Arg, Flag}; +use crate::{Arg, Flag, Spec}; use indexmap::IndexMap; use kdl::{KdlDocument, KdlEntry, KdlNode}; +use serde::Serialize; -#[derive(Debug, Default)] +#[derive(Debug, Default, Serialize, Clone)] pub struct SchemaCmd { + pub full_cmd: Vec, pub subcommands: IndexMap, pub args: Vec, pub flags: Vec, @@ -211,3 +213,10 @@ impl From<&SchemaCmd> for clap::Command { app } } + +#[cfg(feature = "clap")] +impl From for Spec { + fn from(cmd: clap::Command) -> Self { + (&cmd).into() + } +} diff --git a/src/parse/config.rs b/src/parse/config.rs index b91aaf0..57e4cf7 100644 --- a/src/parse/config.rs +++ b/src/parse/config.rs @@ -1,13 +1,14 @@ use std::collections::BTreeMap; -use kdl::{KdlDocument, KdlEntry, KdlNode, KdlValue}; +use kdl::{KdlDocument, KdlEntry, KdlNode, }; +use serde::{Serialize}; use crate::bail_parse; use crate::error::UsageErr; use crate::parse::data_types::SpecDataTypes; use crate::parse::helpers::NodeHelper; -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone, Serialize)] pub struct SpecConfig { pub props: BTreeMap, } @@ -18,9 +19,9 @@ impl SpecConfig { } } -#[derive(Debug)] +#[derive(Debug, Clone, Serialize)] pub struct SpecConfigProp { - pub default: KdlValue, + pub default: Option, pub default_note: Option, pub data_type: SpecDataTypes, pub env: Option, @@ -32,8 +33,11 @@ impl SpecConfigProp { fn to_kdl_node(&self, key: String) -> KdlNode { let mut node = KdlNode::new("prop"); node.push(KdlEntry::new(key)); - if self.default != KdlValue::Null { - node.push(KdlEntry::new_prop("default", self.default.clone())); + if let Some(default) = &self.default { + node.push(KdlEntry::new_prop("default", default.clone())); + } + if let Some(default_note) = &self.default_note { + node.push(KdlEntry::new_prop("default_note", default_note.clone())); } if let Some(env) = &self.env { node.push(KdlEntry::new_prop("env", env.clone())); @@ -63,7 +67,7 @@ impl TryFrom<&KdlNode> for SpecConfig { let mut prop = SpecConfigProp::default(); for (k, v) in ph.props() { match k { - "default" => prop.default = v.value.clone(), + "default" => prop.default = v.value.to_string().into(), "default_note" => prop.default_note = Some(v.ensure_string()?), "data_type" => prop.data_type = v.ensure_string()?.parse()?, "env" => prop.env = v.ensure_string()?.to_string().into(), @@ -87,7 +91,7 @@ impl TryFrom<&KdlNode> for SpecConfig { impl Default for SpecConfigProp { fn default() -> Self { Self { - default: KdlValue::Null, + default: None, default_note: None, data_type: SpecDataTypes::Null, env: None, diff --git a/src/parse/data_types.rs b/src/parse/data_types.rs index 4802180..9381f57 100644 --- a/src/parse/data_types.rs +++ b/src/parse/data_types.rs @@ -1,8 +1,10 @@ +use serde::Serialize; use strum::EnumString; -#[derive(Debug, EnumString)] +#[derive(Debug, Copy, Clone, EnumString, Serialize, Default)] #[strum(serialize_all = "snake_case")] pub enum SpecDataTypes { + #[default] Null, String, Integer, diff --git a/src/parse/flag.rs b/src/parse/flag.rs index f2d2c9d..9600241 100644 --- a/src/parse/flag.rs +++ b/src/parse/flag.rs @@ -2,15 +2,17 @@ use std::str::FromStr; use itertools::Itertools; use kdl::{KdlDocument, KdlEntry, KdlNode}; +use serde::Serialize; use crate::error::UsageErr; use crate::error::UsageErr::InvalidFlag; use crate::parse::helpers::NodeHelper; use crate::{bail_parse, Arg}; -#[derive(Debug, Default)] +#[derive(Debug, Default, Serialize, Clone)] pub struct Flag { pub name: String, + pub usage: String, pub help: Option, pub long_help: Option, pub short: Vec, @@ -104,6 +106,7 @@ impl TryFrom<&KdlNode> for Flag { k => bail_parse!(child.node, "unsupported key {k}"), } } + flag.usage = flag.usage(); Ok(flag) } } @@ -168,6 +171,7 @@ impl From<&clap::Arg> for Flag { }; Self { name, + usage: "".into(), short, long, required, diff --git a/src/parse/spec.rs b/src/parse/spec.rs index b89cd00..a7a585b 100644 --- a/src/parse/spec.rs +++ b/src/parse/spec.rs @@ -10,14 +10,17 @@ use crate::error::UsageErr; use crate::parse::cmd::SchemaCmd; use crate::parse::config::SpecConfig; use miette::NamedSource; +use serde::Serialize; use std::cell::RefCell; +use std::iter::once; -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone, Serialize)] pub struct Spec { pub name: String, pub bin: String, pub cmd: SchemaCmd, pub config: SpecConfig, + pub version: Option, } impl Spec { @@ -25,7 +28,7 @@ impl Spec { static PARSING_FILE: RefCell> = RefCell::new(None); } - pub fn parse_file(file: &Path) -> miette::Result<(Spec, String)> { + pub fn parse_file(file: &Path) -> Result<(Spec, String), UsageErr> { let (spec, body) = split_script(file)?; Self::set_parsing_file(Some((file.to_path_buf(), spec.clone()))); let mut schema = Self::from_str(&spec)?; @@ -70,7 +73,7 @@ impl Spec { } } -fn split_script(file: &Path) -> miette::Result<(String, String)> { +fn split_script(file: &Path) -> Result<(String, String), UsageErr> { let full = file::read_to_string(file)?; let schema = full.strip_prefix("#!/usr/bin/env usage\n").unwrap_or(&full); let (schema, body) = schema.split_once("\n#!").unwrap_or((&schema, "")); @@ -85,9 +88,21 @@ fn get_string_prop(node: &KdlNode, name: &str) -> Option { .map(|s| s.to_string()) } +fn set_subcommand_ancestors(cmd: &mut SchemaCmd, ancestors: &[String]) { + let ancestors = ancestors.to_vec(); + for subcmd in cmd.subcommands.values_mut() { + subcmd.full_cmd = ancestors + .clone() + .into_iter() + .chain(once(subcmd.name.clone())) + .collect(); + set_subcommand_ancestors(subcmd, &subcmd.full_cmd.clone()); + } +} + impl FromStr for Spec { - type Err = miette::Error; - fn from_str(input: &str) -> Result { + type Err = UsageErr; + fn from_str(input: &str) -> miette::Result { let kdl: KdlDocument = input .parse() .map_err(|err: kdl::KdlError| UsageErr::KdlError(err))?; @@ -109,14 +124,14 @@ impl FromStr for Spec { let file = get_string_prop(node, "file") .map(context::prepend_load_root) .ok_or_else(|| UsageErr::new(node.to_string(), node.span()))?; - ensure!(file.exists(), "File not found: {}", file.display()); info!("include: {}", file.display()); let (spec, _) = split_script(&file)?; schema.merge(spec.parse()?); } - _ => Err(UsageErr::new(node.to_string(), node.span()))?, + k => bail_parse!(node, "unsupported key {k}"), } } + set_subcommand_ancestors(&mut schema.cmd, &[]); Ok(schema) } } @@ -158,6 +173,7 @@ impl From<&clap::Command> for Spec { bin: cmd.get_bin_name().unwrap_or_default().to_string(), name: cmd.get_name().to_string(), cmd: cmd.into(), + version: cmd.get_version().map(|v| v.to_string()), ..Default::default() } } @@ -181,13 +197,6 @@ impl From<&Spec> for clap::Command { } } -#[cfg(feature = "clap")] -impl From for Spec { - fn from(cmd: clap::Command) -> Self { - (&cmd).into() - } -} - #[cfg(test)] mod tests { use super::*;