aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml3
-rw-r--r--.gitignore3
-rw-r--r--.run/Cargo.lock523
-rw-r--r--.run/Cargo.toml2
-rw-r--r--.run/src/bin/install-mling.ps11
-rwxr-xr-x.run/src/bin/install-mling.sh1
-rw-r--r--.run/src/bin/package-mling-tmpls.rs177
-rw-r--r--CHANGELOG.md9
-rw-r--r--Cargo.lock10
-rw-r--r--Cargo.toml5
-rw-r--r--README.md4
-rw-r--r--docs/_zh_CN/pages/1-getting-started.md4
-rw-r--r--docs/pages/1-getting-started.md4
-rw-r--r--docs/res/guide.txt2
-rw-r--r--mingling_cli/Cargo.lock10
-rw-r--r--mingling_cli/Cargo.toml2
-rw-r--r--mingling_cli/src/errors.rs1
-rw-r--r--mingling_cli/src/linter/cmd_explain.rs33
-rw-r--r--mingling_cli/src/pkg_mgr.rs45
-rw-r--r--mingling_cli/src/pkg_mgr/cmd_install.rs35
-rw-r--r--mingling_cli/src/pkg_mgr/cmd_pkg_disable.rs26
-rw-r--r--mingling_cli/src/pkg_mgr/cmd_pkg_enable.rs25
-rw-r--r--mingling_cli/src/pkg_mgr/cmd_pkg_show.rs27
-rw-r--r--mingling_cli/src/pkg_mgr/cmd_uninstall.rs67
-rw-r--r--mingling_cli/src/proj_mgr.rs1
-rw-r--r--mingling_cli/src/utils.rs1
-rw-r--r--mingling_cli/src/utils/cargo_style.rs218
-rw-r--r--mingling_cli/tmpls/mingling_tmpl/0.4/checklist.toml63
-rw-r--r--mingling_cli/tmpls/mingling_tmpl/0.4/rule.toml95
-rw-r--r--mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.gitignore1
-rw-r--r--mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_command/class.toml0
-rw-r--r--mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_completion/class.toml0
-rw-r--r--mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_help/class.toml0
-rw-r--r--mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_outside_type/class.toml0
-rw-r--r--mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_resource/class.toml0
-rw-r--r--mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_setup/class.toml0
-rw-r--r--mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_setup_hook/class.toml0
-rw-r--r--mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_Cargo.toml79
-rw-r--r--mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_build.rs31
-rw-r--r--mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_src/tmpl_main.rs45
-rw-r--r--mingling_core/tmpls/comps/pwsh.ps131
-rw-r--r--mingling_pathf/test/Cargo.lock2
42 files changed, 1451 insertions, 135 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0cec42b..4dc56c5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -94,6 +94,9 @@ jobs:
- name: Run cov-test
run: cargo run --manifest-path .run/Cargo.toml --bin cov-test
+ - name: Package mling templates
+ run: cargo run --manifest-path .run/Cargo.toml --bin package-mling-tmpls
+
- name: Delete .temp directory before deployment
run: rm -rf .temp
diff --git a/.gitignore b/.gitignore
index aeaee81..67350b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,9 @@ docs/api-docs/
# Temp file for .run/src/bin/cov-test.rs
docs/cov-test/
+# Mling Template Exports
+/templates/
+
# Drafts
__*.md
__*/
diff --git a/.run/Cargo.lock b/.run/Cargo.lock
index 3117d0b..70021b9 100644
--- a/.run/Cargo.lock
+++ b/.run/Cargo.lock
@@ -9,6 +9,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
+name = "aes"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+]
+
+[[package]]
+name = "arbitrary"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
+dependencies = [
+ "derive_arbitrary",
+]
+
+[[package]]
name = "arg-picker"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -26,7 +46,7 @@ checksum = "04fddbb5c1f26450cfc6aded0559b759b292d400dba1ff4e45128f3e0e1ffe6b"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.119",
]
[[package]]
@@ -36,18 +56,74 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[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 = "bumpalo"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bzip2"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47"
+dependencies = [
+ "bzip2-sys",
+]
+
+[[package]]
+name = "bzip2-sys"
+version = "0.1.13+1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
+
+[[package]]
+name = "cc"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
+dependencies = [
+ "find-msvc-tools",
+ "jobserver",
+ "libc",
+ "shlex",
+]
+
+[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+]
+
+[[package]]
name = "colored"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -69,6 +145,36 @@ dependencies = [
]
[[package]]
+name = "constant_time_eq"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
+dependencies = [
+ "crc-catalog",
+]
+
+[[package]]
+name = "crc-catalog"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"
+
+[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -78,6 +184,67 @@ dependencies = [
]
[[package]]
+name = "crossbeam-utils"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
+
+[[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 = "deflate64"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2"
+
+[[package]]
+name = "deranged"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
+
+[[package]]
+name = "derive_arbitrary"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.3",
+]
+
+[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -110,6 +277,12 @@ dependencies = [
]
[[package]]
+name = "find-msvc-tools"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
+
+[[package]]
name = "flate2"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -144,12 +317,56 @@ dependencies = [
]
[[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.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "r-efi 5.3.0",
+ "wasip2",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi 6.0.0",
+]
+
+[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
name = "indexmap"
version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -173,12 +390,31 @@ dependencies = [
]
[[package]]
+name = "inout"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
+name = "jobserver"
+version = "0.1.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
+dependencies = [
+ "getrandom 0.4.3",
+ "libc",
+]
+
+[[package]]
name = "js-sys"
version = "0.3.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -223,6 +459,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
+name = "log"
+version = "0.4.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
+
+[[package]]
+name = "lzma-rs"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
+dependencies = [
+ "byteorder",
+ "crc",
+]
+
+[[package]]
+name = "lzma-sys"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
name = "memchr"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -239,24 +502,52 @@ dependencies = [
]
[[package]]
+name = "num-conv"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
+
+[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
+name = "pbkdf2"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
+dependencies = [
+ "digest",
+ "hmac",
+]
+
+[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
+name = "pkg-config"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
+
+[[package]]
name = "portable-atomic"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -275,6 +566,18 @@ dependencies = [
]
[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "r-efi"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+
+[[package]]
name = "rustix"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -320,7 +623,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.119",
]
[[package]]
@@ -346,6 +649,34 @@ dependencies = [
]
[[package]]
+name = "sha1"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "shlex"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
+
+[[package]]
name = "simd-adler32"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -358,6 +689,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -369,6 +706,17 @@ dependencies = [
]
[[package]]
+name = "syn"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
name = "tar"
version = "0.4.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -380,6 +728,45 @@ dependencies = [
]
[[package]]
+name = "thiserror"
+version = "2.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.3",
+]
+
+[[package]]
+name = "time"
+version = "0.3.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134"
+dependencies = [
+ "deranged",
+ "num-conv",
+ "powerfmt",
+ "serde_core",
+ "time-core",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
+
+[[package]]
name = "tokio"
version = "1.52.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -397,7 +784,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.119",
]
[[package]]
@@ -453,12 +840,20 @@ dependencies = [
"just_template",
"serde",
"serde_json",
+ "sha2",
"tar",
"tokio",
"toml",
+ "zip",
]
[[package]]
+name = "typenum"
+version = "1.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
+
+[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -477,6 +872,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3"
[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "wasip2"
+version = "1.0.4+wasi-0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
name = "wasm-bindgen"
version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -508,7 +918,7 @@ dependencies = [
"bumpalo",
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.119",
"wasm-bindgen-shared",
]
@@ -556,6 +966,12 @@ dependencies = [
]
[[package]]
+name = "wit-bindgen"
+version = "0.57.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
+
+[[package]]
name = "xattr"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -566,7 +982,106 @@ dependencies = [
]
[[package]]
+name = "xz2"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
+dependencies = [
+ "lzma-sys",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "zip"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50"
+dependencies = [
+ "aes",
+ "arbitrary",
+ "bzip2",
+ "constant_time_eq",
+ "crc32fast",
+ "crossbeam-utils",
+ "deflate64",
+ "displaydoc",
+ "flate2",
+ "getrandom 0.3.4",
+ "hmac",
+ "indexmap",
+ "lzma-rs",
+ "memchr",
+ "pbkdf2",
+ "sha1",
+ "thiserror",
+ "time",
+ "xz2",
+ "zeroize",
+ "zopfli",
+ "zstd",
+]
+
+[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+
+[[package]]
+name = "zopfli"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
+dependencies = [
+ "bumpalo",
+ "crc32fast",
+ "log",
+ "simd-adler32",
+]
+
+[[package]]
+name = "zstd"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "7.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
+dependencies = [
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.16+zstd.1.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
diff --git a/.run/Cargo.toml b/.run/Cargo.toml
index 4935f2d..9f5e0a4 100644
--- a/.run/Cargo.toml
+++ b/.run/Cargo.toml
@@ -21,6 +21,8 @@ tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
indicatif = "0.18.4"
flate2 = "1"
tar = "0.4"
+sha2 = "0.10"
+zip = "2"
arg-picker = "0.1.0"
[workspace]
diff --git a/.run/src/bin/install-mling.ps1 b/.run/src/bin/install-mling.ps1
index bc3bdc3..2bc28ee 100644
--- a/.run/src/bin/install-mling.ps1
+++ b/.run/src/bin/install-mling.ps1
@@ -5,5 +5,6 @@ cargo build --release --manifest-path mingling_cli/Cargo.toml
New-Item -ItemType Directory -Force -Path .temp/mling/bin, .temp/mling/scripts | Out-Null
Copy-Item .temp/target/release/mling.exe .temp/mling/bin/
+Copy-Item .temp/target/release/mingling-cli.exe .temp/mling/bin/
Copy-Item .temp/target/release/mling_comp.ps1 .temp/mling/scripts/mling_comp.ps1
Copy-Item mingling_cli/scripts/load_mling.ps1 .temp/mling/
diff --git a/.run/src/bin/install-mling.sh b/.run/src/bin/install-mling.sh
index f80bc2e..139e221 100755
--- a/.run/src/bin/install-mling.sh
+++ b/.run/src/bin/install-mling.sh
@@ -7,6 +7,7 @@ cargo build --release --manifest-path mingling_cli/Cargo.toml
mkdir -p .temp/mling/bin .temp/mling/scripts
cp .temp/target/release/mling .temp/mling/bin/
+cp .temp/target/release/mingling-cli .temp/mling/bin/
for comp in zsh sh fish; do
cp ".temp/target/release/mling_comp.$comp" ".temp/mling/scripts/mling_comp.$comp"
diff --git a/.run/src/bin/package-mling-tmpls.rs b/.run/src/bin/package-mling-tmpls.rs
new file mode 100644
index 0000000..84bcc16
--- /dev/null
+++ b/.run/src/bin/package-mling-tmpls.rs
@@ -0,0 +1,177 @@
+//! Package the versioned mingling project templates under
+//! `mingling_cli/tmpls/mingling_tmpl/` into per-version ZIP archives
+//! (e.g. `mingling-tmpl-0.4.zip`) plus SHA-256 checksum files
+//! (e.g. `mingling_tmpl-0.4.sha256`), written into `templates/`.
+
+use std::fs::File;
+use std::io::Read;
+use std::path::{Path, PathBuf};
+
+use sha2::{Digest, Sha256};
+use tools::{dependency_order::find_workspace_root, eprintln_cargo_style, println_cargo_style};
+use zip::write::SimpleFileOptions;
+use zip::CompressionMethod;
+
+fn main() {
+ // 1. Determine the workspace root
+ let cwd = std::env::current_dir().expect("failed to get current working directory");
+ let workspace_root = find_workspace_root(&cwd).expect("not inside a Cargo workspace");
+ println_cargo_style!("Workspace: {}", workspace_root.display());
+
+ // 2. Locate the versioned template directories
+ let tmpls_root = workspace_root.join("mingling_cli/tmpls/mingling_tmpl");
+ if !tmpls_root.is_dir() {
+ eprintln_cargo_style!(format!(
+ "template root not found: {}",
+ tmpls_root.display()
+ ));
+ std::process::exit(1);
+ }
+
+ let mut versions: Vec<String> = Vec::new();
+ for entry in std::fs::read_dir(&tmpls_root).expect("failed to read template root") {
+ let entry = entry.expect("failed to read template root entry");
+ let name = entry.file_name().to_string_lossy().to_string();
+ if entry.path().is_dir() && is_version(&name) {
+ versions.push(name);
+ }
+ }
+
+ if versions.is_empty() {
+ eprintln_cargo_style!("no versioned template directories found");
+ std::process::exit(1);
+ }
+
+ versions.sort_by(|a, b| cmp_versions(a, b));
+ for v in &versions {
+ println_cargo_style!("Template: {} ({})", v, tmpls_root.join(v).display());
+ }
+
+ // 3. Prepare the output directory
+ let out_dir = workspace_root.join("templates");
+ println_cargo_style!("Clean: templates/");
+ let _ = std::fs::remove_dir_all(&out_dir);
+ std::fs::create_dir_all(&out_dir).expect("failed to create templates/");
+
+ // 4. Package each version: ZIP + SHA-256
+ let mut failed = false;
+ for version in &versions {
+ let src_dir = tmpls_root.join(version);
+ let zip_name = format!("mingling-tmpl-{version}.zip");
+ let sha_name = format!("mingling_tmpl-{version}.sha256");
+ let zip_path = out_dir.join(&zip_name);
+ let sha_path = out_dir.join(&sha_name);
+
+ println_cargo_style!("Package: {}", zip_name);
+ if let Err(e) = package_template(&src_dir, &zip_path) {
+ eprintln_cargo_style!(format!("failed to package {}: {e}", zip_name));
+ failed = true;
+ continue;
+ }
+
+ let digest = sha256_of_file(&zip_path).unwrap_or_else(|e| {
+ eprintln_cargo_style!(format!("failed to hash {}: {e}", zip_path.display()));
+ std::process::exit(1);
+ });
+
+ // sha256sum-compatible line: "<hex> <zip file name>"
+ let checksum_line = format!("{digest} {zip_name}\n");
+ std::fs::write(&sha_path, checksum_line)
+ .unwrap_or_else(|e| panic!("failed to write {}: {e}", sha_path.display()));
+ println_cargo_style!("Checksum: {} {}", digest, sha_name);
+ }
+
+ if failed {
+ eprintln_cargo_style!("one or more templates failed to package");
+ std::process::exit(1);
+ }
+
+ println_cargo_style!("Done: templates/ is ready");
+}
+
+/// Version directory names look like `0.4`, `0.3`, `1.2.3`, ...
+fn is_version(name: &str) -> bool {
+ !name.is_empty()
+ && name
+ .split('.')
+ .all(|part| !part.is_empty() && part.chars().all(|c| c.is_ascii_digit()))
+}
+
+/// Compare two version strings numerically, component by component.
+fn cmp_versions(a: &str, b: &str) -> std::cmp::Ordering {
+ let a_parts: Vec<u64> = a.split('.').filter_map(|p| p.parse().ok()).collect();
+ let b_parts: Vec<u64> = b.split('.').filter_map(|p| p.parse().ok()).collect();
+ for (x, y) in a_parts.iter().zip(b_parts.iter()) {
+ match x.cmp(y) {
+ std::cmp::Ordering::Equal => continue,
+ ord => return ord,
+ }
+ }
+ a_parts.len().cmp(&b_parts.len())
+}
+
+/// Recursively ZIP the *contents* of `src_dir` (files at the archive root)
+/// into `dest_zip`, preserving the relative directory structure.
+fn package_template(src_dir: &Path, dest_zip: &Path) -> Result<(), zip::result::ZipError> {
+ let file = File::create(dest_zip).map_err(zip::result::ZipError::Io)?;
+ let mut writer = zip::ZipWriter::new(file);
+ let options = SimpleFileOptions::default().compression_method(CompressionMethod::Deflated);
+
+ // Collect entries first so the archive is written in a deterministic order.
+ let mut entries: Vec<PathBuf> = Vec::new();
+ collect_entries(src_dir, src_dir, &mut entries).map_err(zip::result::ZipError::Io)?;
+ entries.sort();
+
+ for rel_path in &entries {
+ let full_path = src_dir.join(rel_path);
+ // ZIP archives always use `/` as the path separator.
+ let name = rel_path.to_string_lossy().replace('\\', "/");
+ if full_path.is_dir() {
+ writer.add_directory(name, options)?;
+ } else {
+ writer.start_file(name, options)?;
+ let mut file = File::open(&full_path).map_err(zip::result::ZipError::Io)?;
+ std::io::copy(&mut file, &mut writer).map_err(zip::result::ZipError::Io)?;
+ }
+ }
+
+ writer.finish().map(|_| ())
+}
+
+/// Recursively collect all relative paths (files and directories) under `dir`.
+fn collect_entries(root: &Path, dir: &Path, out: &mut Vec<PathBuf>) -> std::io::Result<()> {
+ let mut children: Vec<PathBuf> = std::fs::read_dir(dir)?
+ .map(|e| e.map(|e| e.path()))
+ .collect::<std::io::Result<Vec<_>>>()?;
+ children.sort();
+
+ for child in children {
+ let rel = child
+ .strip_prefix(root)
+ .expect("collected entry is inside root")
+ .to_path_buf();
+ if child.is_dir() {
+ out.push(rel);
+ collect_entries(root, &child, out)?;
+ } else {
+ out.push(rel);
+ }
+ }
+ Ok(())
+}
+
+/// Compute the lowercase hex SHA-256 digest of a file.
+fn sha256_of_file(path: &Path) -> std::io::Result<String> {
+ let mut file = File::open(path)?;
+ let mut hasher = Sha256::new();
+ let mut buf = [0u8; 64 * 1024];
+ loop {
+ let n = file.read(&mut buf)?;
+ if n == 0 {
+ break;
+ }
+ hasher.update(&buf[..n]);
+ }
+ let digest = hasher.finalize();
+ Ok(digest.iter().map(|b| format!("{b:02x}")).collect())
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 93b64a6..75b53f2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -106,6 +106,15 @@ None
Structs that are analyzed by other patterns (e.g., `GroupedDerivePattern`, `ChainPattern`, etc.) continue to work exactly as before — only the standalone "bare struct with no macro association" detection has been removed.
+9. **[`comps:pwsh`]** Fixed and refactored the PowerShell completion script template in `mingling_core/tmpls/comps/pwsh.ps1`:
+
+ - **Option value passing** — Changed from the compact `"-f", ($line -replace '-', '^')` array-literal style to conditionally appended `$args += "-f"` / `$args += ($line -replace '-', '^')` pairs for each of `-f`, `-w`, `-p`, and `-c`. This ensures empty values are not passed to the completion engine, preventing spurious argument parsing.
+ - **Word index fix** — `$wordIndex` is now `$i + 1` (when the current word is found in the element list) and `$elements.Count + 1` (when not found), correcting off-by-one indexing. Since the elements array starts at index 0, the actual 1-based word index that the completion engine expects is the array index plus one.
+ - **Empty element filtering** — The `-a` argument loop now skips empty elements (`if ($element)`) so blank entries in the element list are not passed as standalone `-a` arguments with empty values.
+ - **Non-flag optional arguments** — The `-C` (cursor position) and `-i` (word index) parameters remain unconditionally passed, while `-f`, `-w`, `-p`, `-c`, and `-a` are only emitted when their corresponding values are non-empty.
+
+ These changes fix incorrect word-index computation and avoid sending empty option values to the underlying completion engine from PowerShell completions.
+
#### Optimizations:
1. **[`pathf`]** Added `is_module` field to `AnalyzeItem` and a new constructor `AnalyzeItem::local_module(module, item_name)` which sets `is_module: true`. The `type_mapping_builder` now tracks whether an item is a module: when generating `type_using.rs`, module items produce `use path::to::module::*;` (glob import) instead of the standard `use path::to::TypeName;` direct import. Non-module items continue to use direct imports as before. The internal data structure changed from `Vec<(String, String)>` to `Vec<(String, String, bool)>` to carry the `is_module` flag through the pipeline.
diff --git a/Cargo.lock b/Cargo.lock
index 8ab849c..7ba5c5d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -270,7 +270,7 @@ dependencies = [
[[package]]
name = "mingling"
-version = "0.3.0"
+version = "0.4.0"
dependencies = [
"arg-picker",
"mingling",
@@ -283,11 +283,11 @@ dependencies = [
[[package]]
name = "mingling-workspace"
-version = "0.3.0"
+version = "0.4.0"
[[package]]
name = "mingling_core"
-version = "0.3.0"
+version = "0.4.0"
dependencies = [
"env_logger",
"just_fmt 0.2.0",
@@ -304,7 +304,7 @@ dependencies = [
[[package]]
name = "mingling_macros"
-version = "0.3.0"
+version = "0.4.0"
dependencies = [
"just_fmt 0.2.0",
"proc-macro2",
@@ -314,7 +314,7 @@ dependencies = [
[[package]]
name = "mingling_pathf"
-version = "0.3.0"
+version = "0.4.0"
dependencies = [
"just_fmt 0.2.0",
"proc-macro2",
diff --git a/Cargo.toml b/Cargo.toml
index 0b3f18d..259095c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,9 +12,6 @@ members = [
# Picker2
"arg_picker",
"arg_picker_macros",
-
- # Scaffolding tool
- # "mling" (deprecated)
]
exclude = [
@@ -64,7 +61,7 @@ log = "0.4.33"
env_logger = "0.11.11"
[workspace.package]
-version = "0.3.0"
+version = "0.4.0"
edition = "2024"
license = "MIT OR Apache-2.0"
repository = "https://github.com/mingling-rs/mingling"
diff --git a/README.md b/README.md
index 3499d0d..830e13c 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@
💡 Its name comes from the Chinese pinyin **"Mìng Lìng"**, which means **"command"**.
-## WARNING
+## ⚠WARNING
Mingling is currently usable at a basic level, but it is still under active development, so many APIs are not yet mature. Any changes to the public API will be documented in detail in the [Changelog](https://github.com/mingling-rs/mingling/blob/main/CHANGELOG.md).
@@ -99,7 +99,7 @@ Add Mingling to your `Cargo.toml`:
```toml
[dependencies.mingling]
-version = "0.3.0"
+version = "0.4.0"
features = []
```
diff --git a/docs/_zh_CN/pages/1-getting-started.md b/docs/_zh_CN/pages/1-getting-started.md
index 2fca931..6aa6229 100644
--- a/docs/_zh_CN/pages/1-getting-started.md
+++ b/docs/_zh_CN/pages/1-getting-started.md
@@ -13,7 +13,7 @@ cd my-cli
```toml
[dependencies.mingling]
-version = "0.3.0"
+version = "0.4.0"
features = []
```
@@ -25,7 +25,7 @@ features = []
```toml
[dependencies.mingling]
-version = "0.3.0"
+version = "0.4.0"
features = [
"parser",
"comp",
diff --git a/docs/pages/1-getting-started.md b/docs/pages/1-getting-started.md
index 8c81100..443ed74 100644
--- a/docs/pages/1-getting-started.md
+++ b/docs/pages/1-getting-started.md
@@ -13,7 +13,7 @@ Add the following to `Cargo.toml`:
```toml
[dependencies.mingling]
-version = "0.3.0"
+version = "0.4.0"
features = []
```
@@ -25,7 +25,7 @@ Some features **directly affect the entire lifecycle behavior**, so you need to
```toml
[dependencies.mingling]
-version = "0.3.0"
+version = "0.4.0"
features = [
"parser",
"comp",
diff --git a/docs/res/guide.txt b/docs/res/guide.txt
index 657c7ca..74f9c69 100644
--- a/docs/res/guide.txt
+++ b/docs/res/guide.txt
@@ -5,5 +5,5 @@
│ > cargo add mingling │
│ │
│ Or add this to your Cargo.toml │
- │ > mingling = "0.3.0" │
+ │ > mingling = "0.4.0" │
└────────────────────────────────────┘
diff --git a/mingling_cli/Cargo.lock b/mingling_cli/Cargo.lock
index dff20e5..ddfe01c 100644
--- a/mingling_cli/Cargo.lock
+++ b/mingling_cli/Cargo.lock
@@ -322,7 +322,7 @@ dependencies = [
[[package]]
name = "mingling"
-version = "0.3.0"
+version = "0.4.0"
dependencies = [
"arg-picker",
"mingling_core",
@@ -331,7 +331,7 @@ dependencies = [
[[package]]
name = "mingling-cli"
-version = "0.3.0"
+version = "0.4.0"
dependencies = [
"annotate-snippets",
"cargo_metadata",
@@ -351,7 +351,7 @@ dependencies = [
[[package]]
name = "mingling_core"
-version = "0.3.0"
+version = "0.4.0"
dependencies = [
"just_fmt 0.2.0",
"just_template",
@@ -361,7 +361,7 @@ dependencies = [
[[package]]
name = "mingling_macros"
-version = "0.3.0"
+version = "0.4.0"
dependencies = [
"just_fmt 0.2.0",
"proc-macro2",
@@ -371,7 +371,7 @@ dependencies = [
[[package]]
name = "mingling_pathf"
-version = "0.3.0"
+version = "0.4.0"
dependencies = [
"just_fmt 0.2.0",
"proc-macro2",
diff --git a/mingling_cli/Cargo.toml b/mingling_cli/Cargo.toml
index fec4fc1..2bb3fd3 100644
--- a/mingling_cli/Cargo.toml
+++ b/mingling_cli/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mingling-cli"
-version = "0.3.0"
+version = "0.4.0"
edition = "2024"
license = "MIT OR Apache-2.0"
repository = "https://github.com/mingling-rs/mingling/tree/main/mingling_cli"
diff --git a/mingling_cli/src/errors.rs b/mingling_cli/src/errors.rs
index 5cdc43f..754dcc6 100644
--- a/mingling_cli/src/errors.rs
+++ b/mingling_cli/src/errors.rs
@@ -1,3 +1,2 @@
pub mod io_error;
pub mod serde_json;
-
diff --git a/mingling_cli/src/linter/cmd_explain.rs b/mingling_cli/src/linter/cmd_explain.rs
index d1758e1..5346255 100644
--- a/mingling_cli/src/linter/cmd_explain.rs
+++ b/mingling_cli/src/linter/cmd_explain.rs
@@ -1,6 +1,6 @@
-use crate::{Next, linter::registry::ResLintRegistry};
+use crate::{Next, eprintln_cargo, linter::registry::ResLintRegistry};
use mingling::{
- Grouped, LazyRes, Routable, ShellContext, Suggest, SuggestItem,
+ Grouped, LazyRes, RenderResult, Routable, ShellContext, Suggest, SuggestItem,
macros::{
arg, buffer, chain, completion, dispatcher, metadata, pack, pack_err, r_println, renderer,
routeify,
@@ -71,22 +71,29 @@ pub fn render_explain_lint(r: ResultExplainLint) {
r_println!("{}", r.summary);
}
-#[renderer(buffer)]
-pub fn render_error_no_explain_lint_provided(_: ErrorNoExplainLintProvided) {
- r_println!("No lint name provided");
- r_println!("");
- r_println!("Usage: mling explain <LINT>");
+#[renderer]
+pub fn render_error_no_explain_lint_provided(_: ErrorNoExplainLintProvided) -> RenderResult {
+ let mut r = RenderResult::new();
+ eprintln_cargo!(r, "No lint name provided");
+ r_println!(r, "");
+ r_println!(r, "Usage: mling explain <LINT>");
+ r
}
-#[renderer(buffer)]
-pub fn render_error_no_such_lint(err: ErrorNoSuchLint, registry: &mut LazyRes<ResLintRegistry>) {
+#[renderer]
+pub fn render_error_no_such_lint(
+ err: ErrorNoSuchLint,
+ registry: &mut LazyRes<ResLintRegistry>,
+) -> RenderResult {
+ let mut r = RenderResult::new();
let registry = registry.get_ref();
- r_println!("No such lint: \"{}\"", err.info);
- r_println!("");
- r_println!("Available lints:");
+ eprintln_cargo!(r, "No such lint: \"{}\"", err.info);
+ r_println!(r, "");
+ r_println!(r, "Available lints:");
for entry in registry.lints.iter() {
- r_println!(" {}", entry.name);
+ r_println!(r, " {}", entry.name);
}
+ r
}
#[completion(EntryExplain)]
diff --git a/mingling_cli/src/pkg_mgr.rs b/mingling_cli/src/pkg_mgr.rs
index f244fa7..080c50f 100644
--- a/mingling_cli/src/pkg_mgr.rs
+++ b/mingling_cli/src/pkg_mgr.rs
@@ -8,11 +8,11 @@ pub mod cmd_uninstall;
use std::path::PathBuf;
use mingling::{
- Program,
- macros::{buffer, pack_err, program_setup, r_println, renderer},
+ Program, RenderResult,
+ macros::{pack_err, program_setup, r_println, renderer},
};
-use crate::ThisProgram;
+use crate::{ThisProgram, eprintln_cargo, hprintln_cargo};
pack_err!(ErrorRootPackageNotFound);
pack_err!(ErrorNoDataDirectory);
@@ -33,24 +33,35 @@ pub fn package_manager_setup(program: &mut Program<ThisProgram>) {
program.with_resource(ResPackagesDir { path });
}
-#[renderer(buffer)]
-pub fn render_error_root_package_not_found(_: ErrorRootPackageNotFound) {
- r_println!("error: failed to determine the root package");
- r_println!("");
- r_println!("Run `mling install` / `mling uninstall` inside a Cargo workspace");
+#[renderer]
+pub fn render_error_root_package_not_found(_: ErrorRootPackageNotFound) -> RenderResult {
+ let mut r = RenderResult::new();
+ eprintln_cargo!(r, "failed to determine the root package");
+ r_println!(r, "");
+ hprintln_cargo!(
+ r,
+ "Run `mling install` / `mling uninstall` inside a Cargo workspace"
+ );
+ r
}
-#[renderer(buffer)]
-pub fn render_error_no_data_directory(_: ErrorNoDataDirectory) {
- r_println!("error: failed to determine the data directory");
+#[renderer]
+pub fn render_error_no_data_directory(_: ErrorNoDataDirectory) -> RenderResult {
+ let mut r = RenderResult::new();
+ eprintln_cargo!(r, "failed to determine the data directory");
+ r
}
-#[renderer(buffer)]
-pub fn render_error_package_spec_invalid(err: ErrorPackageSpecInvalid) {
- r_println!("error: invalid package spec: {}", err.info);
+#[renderer]
+pub fn render_error_package_spec_invalid(err: ErrorPackageSpecInvalid) -> RenderResult {
+ let mut r = RenderResult::new();
+ eprintln_cargo!(r, "invalid package spec: {}", err.info);
+ r
}
-#[renderer(buffer)]
-pub fn render_error_package_name_required(_: ErrorPackageNameRequired) {
- r_println!("error: a package name is required");
+#[renderer]
+pub fn render_error_package_name_required(_: ErrorPackageNameRequired) -> RenderResult {
+ let mut r = RenderResult::new();
+ eprintln_cargo!(r, "a package name is required");
+ r
}
diff --git a/mingling_cli/src/pkg_mgr/cmd_install.rs b/mingling_cli/src/pkg_mgr/cmd_install.rs
index a399e83..2b4082a 100644
--- a/mingling_cli/src/pkg_mgr/cmd_install.rs
+++ b/mingling_cli/src/pkg_mgr/cmd_install.rs
@@ -2,15 +2,16 @@ use std::{env, fs, io, path::PathBuf, process::Command};
use cargo_metadata::TargetKind;
use mingling::{
- Grouped, LazyRes, Routable,
- macros::{buffer, chain, command, metadata, pack_err, r_println, renderer, routeify},
+ Grouped, LazyRes, RenderResult, Routable,
+ macros::{chain, command, metadata, pack_err, renderer, routeify},
metadata::Description,
};
use crate::{
- Next,
+ Next, eprintln_cargo,
metadata::setup::ResMetadata,
pkg_mgr::{ErrorNoDataDirectory, ErrorRootPackageNotFound, ResPackagesDir},
+ println_cargo,
};
pack_err!(ErrorBuildFailed = String);
@@ -159,20 +160,26 @@ pub fn handle_state_install_copy(
.to_chain()
}
-#[renderer(buffer)]
-pub fn render_result_install(r: ResultInstall) {
- r_println!("Installed to {}", r.install_dir.display());
- for file in r.installed {
- r_println!(" {}", file.display());
+#[renderer]
+pub fn render_result_install(result: ResultInstall) -> RenderResult {
+ let mut r = RenderResult::new();
+ println_cargo!(r, "Installed: {}", result.install_dir.display());
+ for file in result.installed {
+ println_cargo!(r, "Copy: {}", file.display());
}
+ r
}
-#[renderer(buffer)]
-pub fn render_error_build_failed(err: ErrorBuildFailed) {
- r_println!("error: {}", err.info);
+#[renderer]
+pub fn render_error_build_failed(err: ErrorBuildFailed) -> RenderResult {
+ let mut r = RenderResult::new();
+ eprintln_cargo!(r, "{}", err.info);
+ r
}
-#[renderer(buffer)]
-pub fn render_error_binary_not_found(err: ErrorBinaryNotFound) {
- r_println!("error: binary not found: {}", err.info);
+#[renderer]
+pub fn render_error_binary_not_found(err: ErrorBinaryNotFound) -> RenderResult {
+ let mut r = RenderResult::new();
+ eprintln_cargo!(r, "binary not found: {}", err.info);
+ r
}
diff --git a/mingling_cli/src/pkg_mgr/cmd_pkg_disable.rs b/mingling_cli/src/pkg_mgr/cmd_pkg_disable.rs
index 679421c..041bb09 100644
--- a/mingling_cli/src/pkg_mgr/cmd_pkg_disable.rs
+++ b/mingling_cli/src/pkg_mgr/cmd_pkg_disable.rs
@@ -1,20 +1,18 @@
use std::{fs, io};
use mingling::{
- Grouped, Routable, ShellContext, Suggest, SuggestItem,
- macros::{
- arg, buffer, chain, command, completion, metadata, pack, pack_err, r_println, renderer,
- routeify,
- },
+ Grouped, RenderResult, Routable, ShellContext, Suggest, SuggestItem,
+ macros::{arg, chain, command, completion, metadata, pack, pack_err, renderer, routeify},
metadata::Description,
picker::{EntryPicker, PickerArg},
};
use crate::{
- Next,
+ Next, eprintln_cargo,
pkg_mgr::{
ErrorNoDataDirectory, ErrorPackageNameRequired, ErrorPackageSpecInvalid, ResPackagesDir,
},
+ println_cargo,
};
/// Positional argument: package name
@@ -78,14 +76,18 @@ pub fn handle_state_pkg_disable(p: StatePkgDisable, packages_dir: &ResPackagesDi
.to_chain()
}
-#[renderer(buffer)]
-pub fn render_result_pkg_disable(r: ResultPkgDisable) {
- r_println!("Disabled {}", r.name);
+#[renderer]
+pub fn render_result_pkg_disable(result: ResultPkgDisable) -> RenderResult {
+ let mut r = RenderResult::new();
+ println_cargo!(r, "Disabled: {}", result.name);
+ r
}
-#[renderer(buffer)]
-pub fn render_error_package_not_enabled(err: ErrorPackageNotEnabled) {
- r_println!("error: package is not enabled: {}", err.info);
+#[renderer]
+pub fn render_error_package_not_enabled(err: ErrorPackageNotEnabled) -> RenderResult {
+ let mut r = RenderResult::new();
+ eprintln_cargo!(r, "package is not enabled: {}", err.info);
+ r
}
#[completion(EntryPkgDisable)]
diff --git a/mingling_cli/src/pkg_mgr/cmd_pkg_enable.rs b/mingling_cli/src/pkg_mgr/cmd_pkg_enable.rs
index b545a4d..8f6a234 100644
--- a/mingling_cli/src/pkg_mgr/cmd_pkg_enable.rs
+++ b/mingling_cli/src/pkg_mgr/cmd_pkg_enable.rs
@@ -1,20 +1,18 @@
use std::{fs, io};
use mingling::{
- Grouped, Routable, ShellContext, Suggest, SuggestItem,
- macros::{
- arg, buffer, chain, command, completion, metadata, pack, pack_err, r_println, renderer,
- routeify,
- },
+ Grouped, RenderResult, Routable, ShellContext, Suggest, SuggestItem,
+ macros::{arg, chain, command, completion, metadata, pack, pack_err, renderer, routeify},
metadata::Description,
picker::{EntryPicker, PickerArg},
};
use crate::{
- Next,
+ Next, eprintln_cargo,
pkg_mgr::{
ErrorNoDataDirectory, ErrorPackageNameRequired, ErrorPackageSpecInvalid, ResPackagesDir,
},
+ println_cargo,
};
/// Positional argument: package spec (`foo`, `foo@0`, `foo@0.1`, `foo@0.1.2`)
@@ -102,9 +100,18 @@ pub fn handle_state_pkg_enable(p: StatePkgEnable, packages_dir: &ResPackagesDir)
ResultPkgEnable { name, version }.to_chain()
}
-#[renderer(buffer)]
-pub fn render_result_pkg_enable(r: ResultPkgEnable) {
- r_println!("Enabled {}@{}", r.name, r.version);
+#[renderer]
+pub fn render_result_pkg_enable(result: ResultPkgEnable) -> RenderResult {
+ let mut r = RenderResult::new();
+ println_cargo!(r, "Enabled: {}@{}", result.name, result.version);
+ r
+}
+
+#[renderer]
+pub fn render_error_no_matching_version(err: ErrorNoMatchingVersion) -> RenderResult {
+ let mut r = RenderResult::new();
+ eprintln_cargo!(r, "no matching version for: {}", err.info);
+ r
}
#[completion(EntryPkgEnable)]
diff --git a/mingling_cli/src/pkg_mgr/cmd_pkg_show.rs b/mingling_cli/src/pkg_mgr/cmd_pkg_show.rs
index 68aef2b..88c79ef 100644
--- a/mingling_cli/src/pkg_mgr/cmd_pkg_show.rs
+++ b/mingling_cli/src/pkg_mgr/cmd_pkg_show.rs
@@ -2,13 +2,13 @@ use std::{collections::BTreeMap, fs, io};
use colored::Colorize;
use mingling::{
- Grouped, Routable,
- macros::{buffer, command, metadata, r_println, renderer, routeify},
+ Grouped, RenderResult, Routable,
+ macros::{buffer, command, metadata, pack_err, r_println, renderer, routeify},
metadata::Description,
};
use crate::{
- Next,
+ Next, eprintln_cargo,
pkg_mgr::{ErrorNoDataDirectory, ResPackagesDir},
};
@@ -31,6 +31,8 @@ pub fn desc_pkg_show() -> Description {
"Show locally installed packages".into()
}
+pack_err!(ErrorNoPackagesInstalled);
+
#[command(node = "pkg-show", routeify)]
pub fn package_show(packages_dir: &ResPackagesDir) -> Next {
let packages_dir = &packages_dir.path;
@@ -79,17 +81,17 @@ pub fn package_show(packages_dir: &ResPackagesDir) -> Next {
pkg.versions.sort_by(|a, b| compare_versions(b, a));
}
- ResultPkgShow {
- packages: entries.into_values().collect(),
+ let packages: Vec<PkgShowEntry> = entries.into_values().collect();
+
+ if packages.is_empty() {
+ return ErrorNoPackagesInstalled::default().into();
}
- .to_chain()
+
+ ResultPkgShow { packages }.to_chain()
}
#[renderer(buffer)]
pub fn render_result_pkg_show(r: ResultPkgShow) {
- if r.packages.is_empty() {
- r_println!("No packages installed");
- }
for pkg in r.packages {
if let Some(enabled) = &pkg.enabled {
r_println!("{}", format!("{} ({})", pkg.name, enabled).bright_cyan());
@@ -102,6 +104,13 @@ pub fn render_result_pkg_show(r: ResultPkgShow) {
}
}
+#[renderer]
+pub fn render_error_no_packages_installed(_: ErrorNoPackagesInstalled) -> RenderResult {
+ let mut r = RenderResult::new();
+ eprintln_cargo!(r, "No packages installed");
+ r
+}
+
/// Newest first; unparsable versions sort last, compared lexicographically.
fn compare_versions(a: &str, b: &str) -> std::cmp::Ordering {
match (semver::Version::parse(a), semver::Version::parse(b)) {
diff --git a/mingling_cli/src/pkg_mgr/cmd_uninstall.rs b/mingling_cli/src/pkg_mgr/cmd_uninstall.rs
index 69c5aa5..988437c 100644
--- a/mingling_cli/src/pkg_mgr/cmd_uninstall.rs
+++ b/mingling_cli/src/pkg_mgr/cmd_uninstall.rs
@@ -1,32 +1,35 @@
use std::{fs, io, path::PathBuf};
use mingling::{
- Grouped, LazyRes, Routable, ShellContext, Suggest, SuggestItem,
- macros::{
- arg, buffer, chain, command, completion, metadata, pack, r_println, renderer, routeify,
- },
+ LazyRes, RenderResult, Routable, ShellContext, Suggest, SuggestItem,
+ macros::{arg, chain, command, completion, metadata, pack, pack_err, renderer, routeify},
metadata::Description,
picker::{EntryPicker, PickerArg},
};
use crate::{
- Next,
+ Next, eprintln_cargo,
metadata::setup::ResMetadata,
pkg_mgr::{
ErrorNoDataDirectory, ErrorPackageSpecInvalid, ErrorRootPackageNotFound, ResPackagesDir,
},
+ println_cargo,
};
/// Optional positional argument: package spec (`name` or `name@version`)
pub static ARG_PACKAGE: PickerArg<Option<String>> = arg![Option<String>];
+// Directory names to remove, e.g. `["omg@0.1.0", "omg@0.1.1"]`
pack!(StateUninstallPackages = Vec<String>);
-#[derive(Debug, Default, Grouped)]
-pub struct ResultUninstall {
- pub removed: Vec<PathBuf>,
- pub not_installed: Vec<PathBuf>,
-}
+// Directories that were successfully removed.
+pack!(ResultPackageUninstalled = Vec<PathBuf>);
+
+// Directories that were not installed.
+pack_err!(ErrorPackageNotInstall = Vec<PathBuf>);
+
+// No installed package matched the given spec.
+pack_err!(ErrorNoMatchingPackages);
/// `{data_dir}/.mingling`
#[metadata(EntryUninstall)]
@@ -96,6 +99,7 @@ pub fn handle_state_uninstall_packages(
if packages_dir.as_os_str().is_empty() {
return ErrorNoDataDirectory::default().to_chain();
}
+
let mut removed = Vec::new();
let mut not_installed = Vec::new();
@@ -111,25 +115,38 @@ pub fn handle_state_uninstall_packages(
removed.push(dir);
}
- ResultUninstall {
- removed,
- not_installed,
+ if removed.is_empty() && not_installed.is_empty() {
+ return ErrorNoMatchingPackages::default().to_chain();
+ }
+ if !removed.is_empty() {
+ return ResultPackageUninstalled::new(removed).to_chain();
}
- .to_chain()
+ ErrorPackageNotInstall::new(not_installed).to_chain()
}
-#[renderer(buffer)]
-pub fn render_result_uninstall(r: ResultUninstall) {
- if r.removed.is_empty() && r.not_installed.is_empty() {
- r_println!("No matching packages installed");
- } else {
- for dir in r.removed {
- r_println!("Uninstalled: {}", dir.display());
- }
- for dir in r.not_installed {
- r_println!("Not installed: {}", dir.display());
- }
+#[renderer]
+pub fn render_result_package_uninstalled(r: ResultPackageUninstalled) -> RenderResult {
+ let mut result = RenderResult::new();
+ for dir in r.inner {
+ println_cargo!(result, "Uninstalled: {}", dir.display());
+ }
+ result
+}
+
+#[renderer]
+pub fn render_error_package_not_install(err: ErrorPackageNotInstall) -> RenderResult {
+ let mut result = RenderResult::new();
+ for dir in err.info {
+ eprintln_cargo!(result, "not installed: {}", dir.display());
}
+ result
+}
+
+#[renderer]
+pub fn render_error_no_matching_packages(_: ErrorNoMatchingPackages) -> RenderResult {
+ let mut result = RenderResult::new();
+ eprintln_cargo!(result, "no matching packages installed");
+ result
}
#[completion(EntryUninstall)]
diff --git a/mingling_cli/src/proj_mgr.rs b/mingling_cli/src/proj_mgr.rs
index e69de29..8b13789 100644
--- a/mingling_cli/src/proj_mgr.rs
+++ b/mingling_cli/src/proj_mgr.rs
@@ -0,0 +1 @@
+
diff --git a/mingling_cli/src/utils.rs b/mingling_cli/src/utils.rs
index 8754563..6acf1c6 100644
--- a/mingling_cli/src/utils.rs
+++ b/mingling_cli/src/utils.rs
@@ -1 +1,2 @@
+pub mod cargo_style;
pub mod display;
diff --git a/mingling_cli/src/utils/cargo_style.rs b/mingling_cli/src/utils/cargo_style.rs
new file mode 100644
index 0000000..8c19c07
--- /dev/null
+++ b/mingling_cli/src/utils/cargo_style.rs
@@ -0,0 +1,218 @@
+use colored::Colorize;
+
+/// Formats a message in cargo-style format with a bold green prefix.
+///
+/// The message should be in the format `prefix: content`. The prefix will be
+/// bold green and right-padded to 12 characters. If there is no colon in the
+/// string, the entire string is printed as content with an empty prefix.
+///
+/// # Macros
+///
+/// - `format_cargo!("prefix: {}", arg)` — format-style invocation
+/// - `format_cargo!(expr)` — direct expression invocation
+///
+/// # Panics
+///
+/// Panics if the prefix (text before the first `:`) exceeds 12 characters.
+///
+/// # Examples
+///
+/// ```ignore
+/// format_cargo!("Compiling: hello.rs");
+/// // Output: " Compiling hello.rs" (green bold "Compiling" padded to 12)
+/// ```
+#[macro_export]
+macro_rules! format_cargo {
+ ($fmt:literal, $($arg:tt)*) => {
+ $crate::utils::cargo_style::get_cargo_info_format(format!($fmt, $($arg)*))
+ };
+ ($cmd:expr) => {
+ $crate::utils::cargo_style::get_cargo_info_format($cmd)
+ };
+}
+
+/// Formats an error message in cargo-style format with a bold red "error" prefix.
+///
+/// # Macros
+///
+/// - `eformat_cargo!("prefix: {}", arg)` — format-style invocation
+/// - `eformat_cargo!(expr)` — direct expression invocation
+///
+/// # Examples
+///
+/// ```ignore
+/// eformat_cargo!("failed to parse input");
+/// // Output: "error: failed to parse input" (red bold "error")
+/// ```
+#[macro_export]
+macro_rules! eformat_cargo {
+ ($fmt:literal, $($arg:tt)*) => {
+ $crate::utils::cargo_style::get_cargo_error_format(format!($fmt, $($arg)*))
+ };
+ ($cmd:expr) => {
+ $crate::utils::cargo_style::get_cargo_error_format($cmd)
+ };
+}
+
+/// Formats a help message in cargo-style format with a bright white "help" prefix.
+///
+/// # Macros
+///
+/// - `hformat_cargo!("prefix: {}", arg)` — format-style invocation
+/// - `hformat_cargo!(expr)` — direct expression invocation
+///
+/// # Examples
+///
+/// ```ignore
+/// hformat_cargo!("use --verbose for more info");
+/// // Output: "help: use --verbose for more info" (bright white "help")
+/// ```
+#[macro_export]
+macro_rules! hformat_cargo {
+ ($fmt:literal, $($arg:tt)*) => {
+ $crate::utils::cargo_style::get_cargo_help_format(format!($fmt, $($arg)*))
+ };
+ ($cmd:expr) => {
+ $crate::utils::cargo_style::get_cargo_help_format($cmd)
+ };
+}
+
+/// Print a message in cargo-style format into a `RenderResult` buffer.
+///
+/// The first argument must be the renderer's buffer variable (e.g.
+/// `__render_result_buffer` inside a `#[renderer(buffer)]` function).
+///
+/// # Examples
+///
+/// ```ignore
+/// #[renderer(buffer)]
+/// fn render_ok(_: ResOk) {
+/// println_cargo!(__render_result_buffer, "Compiling: {}", name);
+/// }
+/// ```
+#[macro_export]
+macro_rules! println_cargo {
+ ($buf:ident, $fmt:literal, $($arg:tt)*) => {
+ ::mingling::macros::r_println!($buf, "{}", $crate::utils::cargo_style::get_cargo_info_format(format!($fmt, $($arg)*)))
+ };
+ ($buf:ident, $cmd:expr) => {
+ ::mingling::macros::r_println!($buf, "{}", $crate::utils::cargo_style::get_cargo_info_format($cmd))
+ };
+}
+
+/// Print an error message in cargo-style format into a `RenderResult` buffer.
+///
+/// # Examples
+///
+/// ```ignore
+/// #[renderer(buffer)]
+/// fn render_err(_: ResErr) {
+/// eprintln_cargo!(__render_result_buffer, "failed to parse input");
+/// }
+/// ```
+#[macro_export]
+macro_rules! eprintln_cargo {
+ ($buf:ident, $fmt:literal, $($arg:tt)*) => {
+ ::mingling::macros::r_println!($buf, "{}", $crate::utils::cargo_style::get_cargo_error_format(format!($fmt, $($arg)*)))
+ };
+ ($buf:ident, $cmd:expr) => {
+ ::mingling::macros::r_println!($buf, "{}", $crate::utils::cargo_style::get_cargo_error_format($cmd))
+ };
+}
+
+/// Print a help message in cargo-style format into a `RenderResult` buffer.
+///
+/// # Examples
+///
+/// ```ignore
+/// #[renderer(buffer)]
+/// fn render_help(_: ResHelp) {
+/// hprintln_cargo!(__render_result_buffer, "use --verbose for more info");
+/// }
+/// ```
+#[macro_export]
+macro_rules! hprintln_cargo {
+ ($buf:ident, $fmt:literal, $($arg:tt)*) => {
+ ::mingling::macros::r_println!($buf, "{}", $crate::utils::cargo_style::get_cargo_help_format(format!($fmt, $($arg)*)))
+ };
+ ($buf:ident, $cmd:expr) => {
+ ::mingling::macros::r_println!($buf, "{}", $crate::utils::cargo_style::get_cargo_help_format($cmd))
+ };
+}
+
+/// Format a message in cargo style format, with bold green prefix.
+///
+/// The input string is split at the first `:`. The part before the colon becomes
+/// the prefix (bold green, right-padded to 12 characters), and the part after
+/// becomes the content.
+///
+/// If no colon is found, the entire string is treated as content and no prefix
+/// is shown.
+///
+/// # Panics
+///
+/// Panics if the prefix (text before the first `:`) exceeds 12 characters.
+///
+/// # Examples
+///
+/// ```ignore
+/// get_cargo_info_format("Compiling: my_program.rs");
+/// // returns " Compiling my_program.rs"
+/// ```
+pub fn get_cargo_info_format(str: impl Into<String>) -> String {
+ let s = str.into();
+ let (prefix, content) = if let Some(pos) = s.find(':') {
+ (
+ s[..pos].trim().to_string(),
+ s[pos + 1..].trim_start().to_string(),
+ )
+ } else {
+ (String::new(), s.trim().to_string())
+ };
+
+ assert!(
+ prefix.len() <= 12,
+ "prefix length exceeds 12: '{}' has length {}",
+ prefix,
+ prefix.len()
+ );
+
+ let padding = " ".repeat(12 - prefix.len());
+
+ format!(
+ "{}{} {}",
+ padding,
+ prefix.bold().bright_green(),
+ content.trim()
+ )
+}
+
+/// Format an error message in cargo style format, with bold red "error" prefix.
+///
+/// The input string is printed as the error content, prefixed by a bold red
+/// `error:` label.
+///
+/// # Examples
+///
+/// ```ignore
+/// get_cargo_error_format("something went wrong");
+/// // returns "error: something went wrong"
+/// ```
+pub fn get_cargo_error_format(str: impl Into<String>) -> String {
+ format!("{}: {}", "error".bold().bright_red(), str.into())
+}
+
+/// Format a help message in cargo style format, with bright white "help" prefix.
+///
+/// The input string is printed as the help content, prefixed by a bright white
+/// `help:` label (not bold).
+///
+/// # Examples
+///
+/// ```ignore
+/// get_cargo_help_format("use --verbose for more info");
+/// // returns "help: use --verbose for more info"
+/// ```
+pub fn get_cargo_help_format(str: impl Into<String>) -> String {
+ format!("{}: {}", "help".bright_white(), str.into())
+}
diff --git a/mingling_cli/tmpls/mingling_tmpl/0.4/checklist.toml b/mingling_cli/tmpls/mingling_tmpl/0.4/checklist.toml
new file mode 100644
index 0000000..0a5d4a2
--- /dev/null
+++ b/mingling_cli/tmpls/mingling_tmpl/0.4/checklist.toml
@@ -0,0 +1,63 @@
+# --------------------------------------------------------------------------- #
+#
+# Mingling 0.4 Template
+#
+# > Welcome to Mingling.
+# > Before starting your project, please fill out this `checklist` file,
+# > as it will determine the structure of your project.
+#
+# --------------------------------------------------------------------------- #
+
+### 1. Please enter your program name:
+
+program_name = "my-cli"
+
+### 2. Please select the parser used in the project,
+
+# Clap Derive, the most mainstream option.
+# When enabled,
+# Mingling will use the struct implementing clap::Parser as the Entry Type
+# ------------------
+# parser = "clap"
+# ------------------
+
+# Builtin, Mingling's built-in argument parser, lightweight, order-sensitive, suitable for simple parsing
+# ------------------
+# parser = "builtin"
+# ------------------
+
+# Picker, based on `arg-picker`, a lightweight and type-safe argument parser that aligns better with Mingling's paradigm
+# ------------------
+parser = "picker"
+# ------------------
+
+### 3. If you have async requirements, please select an async runtime
+# Hint: You can only enable one of them
+# ------------------
+# tokio = true
+# async_std = true
+# ------------------
+
+### 4. If you need flexible management of your program's exit codes, enable this
+# ------------------
+exit_code = true
+# ------------------
+
+### 5. If your project has completion needs, enable this
+# ------------------
+completion = true
+# ------------------
+
+### 6. If you expect the number of commands to be > 20,
+# enable the compile-time dispatch tree for faster matching
+# ------------------
+# dispatch_tree = true
+# ------------------
+
+### 7. If you want the program to be able to output structured data controlled by flags such as `--json`, enable this
+# ------------------
+# structural_renderer = true
+# ------------------
+
+### Final. All set.
+# Please run `mling proj-init` in this directory to continue creating the project.
diff --git a/mingling_cli/tmpls/mingling_tmpl/0.4/rule.toml b/mingling_cli/tmpls/mingling_tmpl/0.4/rule.toml
new file mode 100644
index 0000000..0ff6ffb
--- /dev/null
+++ b/mingling_cli/tmpls/mingling_tmpl/0.4/rule.toml
@@ -0,0 +1,95 @@
+[[user.input]]
+name = "program_name"
+
+[[user.toggle]]
+name = "exit_code"
+
+[[user.toggle]]
+name = "tokio"
+
+[[user.toggle]]
+name = "async_std"
+
+[[user.toggle-mutex]]
+mutex = [ "tokio", "async_std" ]
+reason = "You can only select one async runtime"
+
+[[user.toggle]]
+name = "completion"
+
+[[user.toggle]]
+name = "pathf"
+default = true
+
+[[user.toggle]]
+name = "structural_renderer"
+
+[[user.toggle]]
+name = "dispatch_tree"
+
+[[user.toggle]]
+name = "use_parser"
+
+[[user.selection]]
+name = "parser"
+option = [ "clap", "builtin", "picker" ]
+
+[[user.toggle-mutex]]
+mutex = [ "clap", "builtin", "picker" ]
+reason = "You can only select one argument parser"
+
+[[display]]
+name = "ASYNC_STD"
+rule = "async_std"
+
+[[display]]
+name = "TOKIO"
+rule = "tokio"
+
+[[display]]
+name = "ASYNC"
+rule = "tokio || async_std"
+
+[[display]]
+name = "BUILD_TIME"
+rule = "completion || pathf"
+
+[[hide-file]]
+file = "./build.rs"
+rule = "!completion && !pathf"
+
+[[display]]
+name = "PARSER_CLAP"
+rule = "use_parser && parser == clap"
+
+[[display]]
+name = "PARSER_BUILTIN"
+rule = "use_parser && parser == builtin"
+
+[[display]]
+name = "PARSER_PICKER"
+rule = "use_parser && parser == picker"
+
+[[display]]
+name = "COMPLETION"
+rule = "completion"
+
+[[display]]
+name = "STRUCTURAL_RENDERER"
+rule = "structural_renderer"
+
+[[display]]
+name = "EXIT_CODE"
+rule = "exit_code"
+
+[[display]]
+name = "DISPATCH_TREE"
+rule = "dispatch_tree"
+
+[[display]]
+name = "NOT_DISPATCH_TREE"
+rule = "!dispatch_tree"
+
+[[display]]
+name = "PATHF"
+rule = "pathf"
diff --git a/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.gitignore b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_command/class.toml b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_command/class.toml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_command/class.toml
diff --git a/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_completion/class.toml b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_completion/class.toml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_completion/class.toml
diff --git a/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_help/class.toml b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_help/class.toml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_help/class.toml
diff --git a/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_outside_type/class.toml b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_outside_type/class.toml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_outside_type/class.toml
diff --git a/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_resource/class.toml b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_resource/class.toml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_resource/class.toml
diff --git a/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_setup/class.toml b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_setup/class.toml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_setup/class.toml
diff --git a/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_setup_hook/class.toml b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_setup_hook/class.toml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_.mling/tmpl_setup_hook/class.toml
diff --git a/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_Cargo.toml b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_Cargo.toml
new file mode 100644
index 0000000..2345911
--- /dev/null
+++ b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_Cargo.toml
@@ -0,0 +1,79 @@
+[package]
+name = "<<<program_name>>>"
+version = "0.1.0"
+edition = "2024"
+
+[[bin]]
+name = "<<<program_name>>>"
+path = "src/main.rs"
+
+[profile.dev]
+opt-level = 0
+debug = true
+panic = "unwind"
+lto = false
+
+[profile.release]
+opt-level = 3
+# If you need to use exec_panic hook, enable `unwind`
+# panic = "unwind"
+panic = "abort"
+lto = true
+
+[dependencies.mingling]
+version = "0.4"
+features = [
+ "extras",
+??? >>> ASYNC
+ "async",
+??? <<<
+??? >>> PARSER_PICKER
+ "picker",
+??? <<<
+??? >>> PARSER_CLAP
+ "clap",
+??? <<<
+??? >>> PARSER_BUILTIN
+ "parser",
+??? <<<
+??? >>> DISPATCH_TREE
+ "dispatch_tree",
+??? <<<
+??? >>> PATHF
+ "pathf",
+??? <<<
+??? >>> COMPLETION
+ "comp",
+??? <<<
+??? >>> STRUCTURAL_RENDERER
+ "structural_renderer",
+??? <<<
+]
+
+??? >>> BUILD_TIME
+[build-dependencies.mingling]
+version = "0.4"
+features = [
+ "build",
+??? >>> DISPATCH_TREE
+ "dispatch_tree",
+??? <<<
+??? >>> PATHF
+ "pathf",
+??? <<<
+??? >>> COMPLETION
+ "comp",
+??? <<<
+]
+??? <<<
+
+[dependencies]
+??? >>> PARSER_CLAP
+clap = { version = "4", features = [ "derive" ] }
+??? <<<
+??? >>> TOKIO
+tokio = { version = "1", features = [ "full" ] }
+??? <<<
+??? >>> ASYNC_STD
+async-std = "1"
+??? <<<
diff --git a/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_build.rs b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_build.rs
new file mode 100644
index 0000000..e90070c
--- /dev/null
+++ b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_build.rs
@@ -0,0 +1,31 @@
+fn main() {
+??? >>> COMPLETION
+ build_scripts();
+??? <<<
+??? >>> PATHF
+ build_pathf_mapping();
+??? <<<
+}
+??? >>> COMPLETION
+
+/// Generate completion scripts
+fn build_scripts() {
+ // `env!("CARGO_PKG_NAME")` equals the crate name, which matches the binary name.
+ // If your binary name differs from the crate name, specify it explicitly.
+ mingling::build::build_comp_scripts(
+ // Your binary name:
+ env!("CARGO_PKG_NAME"),
+ )
+ .unwrap();
+}
+??? <<<
+??? >>> PATHF
+
+/// Use `pathf` to analyze the workspace's mingling macro calls
+/// and generate type path mappings for them
+fn build_pathf_mapping() {
+ // Build pathf type mapping to ensure that the enabled `pathf` feature
+ // can correctly scan macros in the project
+ mingling::build::analyze_and_build_type_mapping().unwrap();
+}
+??? <<<
diff --git a/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_src/tmpl_main.rs b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_src/tmpl_main.rs
new file mode 100644
index 0000000..02f3cc9
--- /dev/null
+++ b/mingling_cli/tmpls/mingling_tmpl/0.4/tmpl_src/tmpl_main.rs
@@ -0,0 +1,45 @@
+pub(crate) mod commands;
+pub(crate) mod helps;
+pub(crate) mod renderers;
+
+use mingling::macros::buffer;
+use mingling::prelude::*;
+??? >>> EXIT_CODE
+use mingling::setup::ExitCodeSetup;
+??? <<<
+use mingling::setup::picker::BasicProgramSetup;
+
+fn main() {
+ let mut program = ThisProgram::new();
+
+ // Plugins
+ program.with_setup(BasicProgramSetup);
+??? >>> EXIT_CODE
+ program.with_setup(ExitCodeSetup::default());
+??? <<<
+??? >>> COMPLETION
+
+ // Completion Dispatcher
+ program.with_dispatcher(CMDCompletion);
+??? <<<
+??? >>> NOT_DISPATCH_TREE
+
+ // Dispatchers
+ program.with_dispatcher(commands::greet::CMDGreet);
+??? <<<
+
+ program.exec_and_exit();
+}
+
+#[renderer(buffer)]
+fn handle_error_dispatcher_not_found(prev: ErrorDispatcherNotFound) {
+ r_println!("Error: cannot match \"{}\" to any command", prev.join(" "));
+}
+
+#[renderer(buffer)]
+fn handle_error_renderer_not_found(prev: ErrorRendererNotFound) {
+ let type_name = prev.inner;
+ r_println!("Error: renderer not found for \"{}\"", type_name);
+}
+
+gen_program!();
diff --git a/mingling_core/tmpls/comps/pwsh.ps1 b/mingling_core/tmpls/comps/pwsh.ps1
index eccb776..d72a027 100644
--- a/mingling_core/tmpls/comps/pwsh.ps1
+++ b/mingling_core/tmpls/comps/pwsh.ps1
@@ -18,7 +18,7 @@ Register-ArgumentCompleter -Native -CommandName '<<<bin_name>>>' -ScriptBlock {
$found = $false
for ($i = 0; $i -lt $elements.Count; $i++) {
if ($elements[$i] -eq $currentWord) {
- $wordIndex = $i
+ $wordIndex = $i + 1
if ($i -gt 0) {
$previousWord = $elements[$i - 1]
}
@@ -28,25 +28,40 @@ Register-ArgumentCompleter -Native -CommandName '<<<bin_name>>>' -ScriptBlock {
}
if (-not $found) {
- $wordIndex = $elements.Count
+ $wordIndex = $elements.Count + 1
if ($elements.Count -gt 0) {
$previousWord = $elements[-1]
}
}
$args = @(
- "-f", ($line -replace '-', '^')
"-C", $cursorPosition.ToString()
- "-w", ($currentWord -replace '-', '^')
- "-p", ($previousWord -replace '-', '^')
- "-c", ($commandName -replace '-', '^')
"-i", $wordIndex.ToString()
"-F", "Powershell"
)
+ if ($line) {
+ $args += "-f"
+ $args += ($line -replace '-', '^')
+ }
+ if ($currentWord) {
+ $args += "-w"
+ $args += ($currentWord -replace '-', '^')
+ }
+ if ($previousWord) {
+ $args += "-p"
+ $args += ($previousWord -replace '-', '^')
+ }
+ if ($commandName) {
+ $args += "-c"
+ $args += ($commandName -replace '-', '^')
+ }
+
foreach ($element in $elements) {
- $args += "-a"
- $args += ($element -replace '-', '^')
+ if ($element) {
+ $args += "-a"
+ $args += ($element -replace '-', '^')
+ }
}
$originalEncoding = [Console]::OutputEncoding
diff --git a/mingling_pathf/test/Cargo.lock b/mingling_pathf/test/Cargo.lock
index 1260662..e7de689 100644
--- a/mingling_pathf/test/Cargo.lock
+++ b/mingling_pathf/test/Cargo.lock
@@ -16,7 +16,7 @@ checksum = "6170dccbc3ea15dfb7f2da964097f814aba1dd8f746d4ffc56f33245c38e6d96"
[[package]]
name = "mingling_pathf"
-version = "0.3.0"
+version = "0.4.0"
dependencies = [
"just_fmt 0.2.0",
"proc-macro2",