├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── benchmark.zsh ├── changelog.md ├── license ├── readme.md ├── src ├── identifier.rs ├── main.rs ├── plugin.rs └── plugins.rs └── zrat.png /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branch: 6 | - main 7 | tags: 8 | - "*.*.*" 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: test 16 | run: cargo test 17 | 18 | release: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: checkout 22 | uses: actions/checkout@v4 23 | - name: build 24 | run: cargo build --release 25 | - name: release 26 | uses: softprops/action-gh-release@v2 27 | if: startsWith(github.ref, 'refs/tags/') 28 | with: 29 | files: target/release/zr 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .vscode 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | 8 | os: 9 | - linux 10 | - osx 11 | 12 | matrix: 13 | allow_failures: 14 | - rust: nightly 15 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "bitflags" 16 | version = "2.6.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 19 | 20 | [[package]] 21 | name = "block-buffer" 22 | version = "0.10.4" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 25 | dependencies = [ 26 | "generic-array", 27 | ] 28 | 29 | [[package]] 30 | name = "cc" 31 | version = "1.2.3" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" 34 | dependencies = [ 35 | "jobserver", 36 | "libc", 37 | "shlex", 38 | ] 39 | 40 | [[package]] 41 | name = "cfg-if" 42 | version = "1.0.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 45 | 46 | [[package]] 47 | name = "console" 48 | version = "0.15.8" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" 51 | dependencies = [ 52 | "encode_unicode", 53 | "lazy_static", 54 | "libc", 55 | "unicode-width", 56 | "windows-sys 0.52.0", 57 | ] 58 | 59 | [[package]] 60 | name = "cpufeatures" 61 | version = "0.2.16" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" 64 | dependencies = [ 65 | "libc", 66 | ] 67 | 68 | [[package]] 69 | name = "crypto-common" 70 | version = "0.1.6" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 73 | dependencies = [ 74 | "generic-array", 75 | "typenum", 76 | ] 77 | 78 | [[package]] 79 | name = "dialoguer" 80 | version = "0.11.0" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" 83 | dependencies = [ 84 | "console", 85 | "shell-words", 86 | "tempfile", 87 | "thiserror 1.0.69", 88 | "zeroize", 89 | ] 90 | 91 | [[package]] 92 | name = "digest" 93 | version = "0.10.7" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 96 | dependencies = [ 97 | "block-buffer", 98 | "crypto-common", 99 | ] 100 | 101 | [[package]] 102 | name = "directories" 103 | version = "5.0.1" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" 106 | dependencies = [ 107 | "dirs-sys", 108 | ] 109 | 110 | [[package]] 111 | name = "dirs" 112 | version = "5.0.1" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" 115 | dependencies = [ 116 | "dirs-sys", 117 | ] 118 | 119 | [[package]] 120 | name = "dirs-sys" 121 | version = "0.4.1" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" 124 | dependencies = [ 125 | "libc", 126 | "option-ext", 127 | "redox_users", 128 | "windows-sys 0.48.0", 129 | ] 130 | 131 | [[package]] 132 | name = "displaydoc" 133 | version = "0.2.5" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 136 | dependencies = [ 137 | "proc-macro2", 138 | "quote", 139 | "syn", 140 | ] 141 | 142 | [[package]] 143 | name = "encode_unicode" 144 | version = "0.3.6" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 147 | 148 | [[package]] 149 | name = "errno" 150 | version = "0.3.10" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 153 | dependencies = [ 154 | "libc", 155 | "windows-sys 0.59.0", 156 | ] 157 | 158 | [[package]] 159 | name = "fastrand" 160 | version = "2.3.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 163 | 164 | [[package]] 165 | name = "form_urlencoded" 166 | version = "1.2.1" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 169 | dependencies = [ 170 | "percent-encoding", 171 | ] 172 | 173 | [[package]] 174 | name = "generic-array" 175 | version = "0.14.7" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 178 | dependencies = [ 179 | "typenum", 180 | "version_check", 181 | ] 182 | 183 | [[package]] 184 | name = "getrandom" 185 | version = "0.2.15" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 188 | dependencies = [ 189 | "cfg-if", 190 | "libc", 191 | "wasi", 192 | ] 193 | 194 | [[package]] 195 | name = "git2" 196 | version = "0.19.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" 199 | dependencies = [ 200 | "bitflags", 201 | "libc", 202 | "libgit2-sys", 203 | "log", 204 | "openssl-probe", 205 | "openssl-sys", 206 | "url", 207 | ] 208 | 209 | [[package]] 210 | name = "git2_credentials" 211 | version = "0.14.0" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "6d07f2ee0eefaac0335b06027e0d84a917615519a06bd08e9232938455a99558" 214 | dependencies = [ 215 | "dialoguer", 216 | "dirs", 217 | "git2", 218 | "pest", 219 | "pest_derive", 220 | "regex", 221 | ] 222 | 223 | [[package]] 224 | name = "icu_collections" 225 | version = "1.5.0" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 228 | dependencies = [ 229 | "displaydoc", 230 | "yoke", 231 | "zerofrom", 232 | "zerovec", 233 | ] 234 | 235 | [[package]] 236 | name = "icu_locid" 237 | version = "1.5.0" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 240 | dependencies = [ 241 | "displaydoc", 242 | "litemap", 243 | "tinystr", 244 | "writeable", 245 | "zerovec", 246 | ] 247 | 248 | [[package]] 249 | name = "icu_locid_transform" 250 | version = "1.5.0" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 253 | dependencies = [ 254 | "displaydoc", 255 | "icu_locid", 256 | "icu_locid_transform_data", 257 | "icu_provider", 258 | "tinystr", 259 | "zerovec", 260 | ] 261 | 262 | [[package]] 263 | name = "icu_locid_transform_data" 264 | version = "1.5.0" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" 267 | 268 | [[package]] 269 | name = "icu_normalizer" 270 | version = "1.5.0" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 273 | dependencies = [ 274 | "displaydoc", 275 | "icu_collections", 276 | "icu_normalizer_data", 277 | "icu_properties", 278 | "icu_provider", 279 | "smallvec", 280 | "utf16_iter", 281 | "utf8_iter", 282 | "write16", 283 | "zerovec", 284 | ] 285 | 286 | [[package]] 287 | name = "icu_normalizer_data" 288 | version = "1.5.0" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" 291 | 292 | [[package]] 293 | name = "icu_properties" 294 | version = "1.5.1" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 297 | dependencies = [ 298 | "displaydoc", 299 | "icu_collections", 300 | "icu_locid_transform", 301 | "icu_properties_data", 302 | "icu_provider", 303 | "tinystr", 304 | "zerovec", 305 | ] 306 | 307 | [[package]] 308 | name = "icu_properties_data" 309 | version = "1.5.0" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" 312 | 313 | [[package]] 314 | name = "icu_provider" 315 | version = "1.5.0" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 318 | dependencies = [ 319 | "displaydoc", 320 | "icu_locid", 321 | "icu_provider_macros", 322 | "stable_deref_trait", 323 | "tinystr", 324 | "writeable", 325 | "yoke", 326 | "zerofrom", 327 | "zerovec", 328 | ] 329 | 330 | [[package]] 331 | name = "icu_provider_macros" 332 | version = "1.5.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 335 | dependencies = [ 336 | "proc-macro2", 337 | "quote", 338 | "syn", 339 | ] 340 | 341 | [[package]] 342 | name = "idna" 343 | version = "1.0.3" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 346 | dependencies = [ 347 | "idna_adapter", 348 | "smallvec", 349 | "utf8_iter", 350 | ] 351 | 352 | [[package]] 353 | name = "idna_adapter" 354 | version = "1.2.0" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 357 | dependencies = [ 358 | "icu_normalizer", 359 | "icu_properties", 360 | ] 361 | 362 | [[package]] 363 | name = "jobserver" 364 | version = "0.1.32" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" 367 | dependencies = [ 368 | "libc", 369 | ] 370 | 371 | [[package]] 372 | name = "lazy_static" 373 | version = "1.5.0" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 376 | 377 | [[package]] 378 | name = "libc" 379 | version = "0.2.168" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" 382 | 383 | [[package]] 384 | name = "libgit2-sys" 385 | version = "0.17.0+1.8.1" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224" 388 | dependencies = [ 389 | "cc", 390 | "libc", 391 | "libssh2-sys", 392 | "libz-sys", 393 | "openssl-sys", 394 | "pkg-config", 395 | ] 396 | 397 | [[package]] 398 | name = "libredox" 399 | version = "0.1.3" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" 402 | dependencies = [ 403 | "bitflags", 404 | "libc", 405 | ] 406 | 407 | [[package]] 408 | name = "libssh2-sys" 409 | version = "0.3.0" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" 412 | dependencies = [ 413 | "cc", 414 | "libc", 415 | "libz-sys", 416 | "openssl-sys", 417 | "pkg-config", 418 | "vcpkg", 419 | ] 420 | 421 | [[package]] 422 | name = "libz-sys" 423 | version = "1.1.20" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" 426 | dependencies = [ 427 | "cc", 428 | "libc", 429 | "pkg-config", 430 | "vcpkg", 431 | ] 432 | 433 | [[package]] 434 | name = "linux-raw-sys" 435 | version = "0.4.14" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 438 | 439 | [[package]] 440 | name = "litemap" 441 | version = "0.7.4" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" 444 | 445 | [[package]] 446 | name = "log" 447 | version = "0.4.22" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 450 | 451 | [[package]] 452 | name = "memchr" 453 | version = "2.7.4" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 456 | 457 | [[package]] 458 | name = "once_cell" 459 | version = "1.20.2" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 462 | 463 | [[package]] 464 | name = "openssl-probe" 465 | version = "0.1.5" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 468 | 469 | [[package]] 470 | name = "openssl-sys" 471 | version = "0.9.104" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" 474 | dependencies = [ 475 | "cc", 476 | "libc", 477 | "pkg-config", 478 | "vcpkg", 479 | ] 480 | 481 | [[package]] 482 | name = "option-ext" 483 | version = "0.2.0" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" 486 | 487 | [[package]] 488 | name = "percent-encoding" 489 | version = "2.3.1" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 492 | 493 | [[package]] 494 | name = "pest" 495 | version = "2.7.15" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" 498 | dependencies = [ 499 | "memchr", 500 | "thiserror 2.0.6", 501 | "ucd-trie", 502 | ] 503 | 504 | [[package]] 505 | name = "pest_derive" 506 | version = "2.7.15" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" 509 | dependencies = [ 510 | "pest", 511 | "pest_generator", 512 | ] 513 | 514 | [[package]] 515 | name = "pest_generator" 516 | version = "2.7.15" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" 519 | dependencies = [ 520 | "pest", 521 | "pest_meta", 522 | "proc-macro2", 523 | "quote", 524 | "syn", 525 | ] 526 | 527 | [[package]] 528 | name = "pest_meta" 529 | version = "2.7.15" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" 532 | dependencies = [ 533 | "once_cell", 534 | "pest", 535 | "sha2", 536 | ] 537 | 538 | [[package]] 539 | name = "pkg-config" 540 | version = "0.3.31" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" 543 | 544 | [[package]] 545 | name = "proc-macro2" 546 | version = "1.0.92" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" 549 | dependencies = [ 550 | "unicode-ident", 551 | ] 552 | 553 | [[package]] 554 | name = "quote" 555 | version = "1.0.37" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 558 | dependencies = [ 559 | "proc-macro2", 560 | ] 561 | 562 | [[package]] 563 | name = "redox_users" 564 | version = "0.4.6" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" 567 | dependencies = [ 568 | "getrandom", 569 | "libredox", 570 | "thiserror 1.0.69", 571 | ] 572 | 573 | [[package]] 574 | name = "regex" 575 | version = "1.11.1" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 578 | dependencies = [ 579 | "aho-corasick", 580 | "memchr", 581 | "regex-automata", 582 | "regex-syntax", 583 | ] 584 | 585 | [[package]] 586 | name = "regex-automata" 587 | version = "0.4.9" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 590 | dependencies = [ 591 | "aho-corasick", 592 | "memchr", 593 | "regex-syntax", 594 | ] 595 | 596 | [[package]] 597 | name = "regex-syntax" 598 | version = "0.8.5" 599 | source = "registry+https://github.com/rust-lang/crates.io-index" 600 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 601 | 602 | [[package]] 603 | name = "rustix" 604 | version = "0.38.42" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" 607 | dependencies = [ 608 | "bitflags", 609 | "errno", 610 | "libc", 611 | "linux-raw-sys", 612 | "windows-sys 0.59.0", 613 | ] 614 | 615 | [[package]] 616 | name = "serde" 617 | version = "1.0.215" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" 620 | dependencies = [ 621 | "serde_derive", 622 | ] 623 | 624 | [[package]] 625 | name = "serde_derive" 626 | version = "1.0.215" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" 629 | dependencies = [ 630 | "proc-macro2", 631 | "quote", 632 | "syn", 633 | ] 634 | 635 | [[package]] 636 | name = "sha2" 637 | version = "0.10.8" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 640 | dependencies = [ 641 | "cfg-if", 642 | "cpufeatures", 643 | "digest", 644 | ] 645 | 646 | [[package]] 647 | name = "shell-words" 648 | version = "1.1.0" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" 651 | 652 | [[package]] 653 | name = "shlex" 654 | version = "1.3.0" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 657 | 658 | [[package]] 659 | name = "smallvec" 660 | version = "1.13.2" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 663 | 664 | [[package]] 665 | name = "stable_deref_trait" 666 | version = "1.2.0" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 669 | 670 | [[package]] 671 | name = "syn" 672 | version = "2.0.90" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" 675 | dependencies = [ 676 | "proc-macro2", 677 | "quote", 678 | "unicode-ident", 679 | ] 680 | 681 | [[package]] 682 | name = "synstructure" 683 | version = "0.13.1" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 686 | dependencies = [ 687 | "proc-macro2", 688 | "quote", 689 | "syn", 690 | ] 691 | 692 | [[package]] 693 | name = "tempfile" 694 | version = "3.14.0" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" 697 | dependencies = [ 698 | "cfg-if", 699 | "fastrand", 700 | "once_cell", 701 | "rustix", 702 | "windows-sys 0.59.0", 703 | ] 704 | 705 | [[package]] 706 | name = "thiserror" 707 | version = "1.0.69" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 710 | dependencies = [ 711 | "thiserror-impl 1.0.69", 712 | ] 713 | 714 | [[package]] 715 | name = "thiserror" 716 | version = "2.0.6" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" 719 | dependencies = [ 720 | "thiserror-impl 2.0.6", 721 | ] 722 | 723 | [[package]] 724 | name = "thiserror-impl" 725 | version = "1.0.69" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 728 | dependencies = [ 729 | "proc-macro2", 730 | "quote", 731 | "syn", 732 | ] 733 | 734 | [[package]] 735 | name = "thiserror-impl" 736 | version = "2.0.6" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" 739 | dependencies = [ 740 | "proc-macro2", 741 | "quote", 742 | "syn", 743 | ] 744 | 745 | [[package]] 746 | name = "tinystr" 747 | version = "0.7.6" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 750 | dependencies = [ 751 | "displaydoc", 752 | "zerovec", 753 | ] 754 | 755 | [[package]] 756 | name = "typenum" 757 | version = "1.17.0" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 760 | 761 | [[package]] 762 | name = "ucd-trie" 763 | version = "0.1.7" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" 766 | 767 | [[package]] 768 | name = "unicode-ident" 769 | version = "1.0.14" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 772 | 773 | [[package]] 774 | name = "unicode-width" 775 | version = "0.1.14" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 778 | 779 | [[package]] 780 | name = "url" 781 | version = "2.5.4" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 784 | dependencies = [ 785 | "form_urlencoded", 786 | "idna", 787 | "percent-encoding", 788 | ] 789 | 790 | [[package]] 791 | name = "utf16_iter" 792 | version = "1.0.5" 793 | source = "registry+https://github.com/rust-lang/crates.io-index" 794 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 795 | 796 | [[package]] 797 | name = "utf8_iter" 798 | version = "1.0.4" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 801 | 802 | [[package]] 803 | name = "vcpkg" 804 | version = "0.2.15" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 807 | 808 | [[package]] 809 | name = "version_check" 810 | version = "0.9.5" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 813 | 814 | [[package]] 815 | name = "wasi" 816 | version = "0.11.0+wasi-snapshot-preview1" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 819 | 820 | [[package]] 821 | name = "windows-sys" 822 | version = "0.48.0" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 825 | dependencies = [ 826 | "windows-targets 0.48.5", 827 | ] 828 | 829 | [[package]] 830 | name = "windows-sys" 831 | version = "0.52.0" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 834 | dependencies = [ 835 | "windows-targets 0.52.6", 836 | ] 837 | 838 | [[package]] 839 | name = "windows-sys" 840 | version = "0.59.0" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 843 | dependencies = [ 844 | "windows-targets 0.52.6", 845 | ] 846 | 847 | [[package]] 848 | name = "windows-targets" 849 | version = "0.48.5" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 852 | dependencies = [ 853 | "windows_aarch64_gnullvm 0.48.5", 854 | "windows_aarch64_msvc 0.48.5", 855 | "windows_i686_gnu 0.48.5", 856 | "windows_i686_msvc 0.48.5", 857 | "windows_x86_64_gnu 0.48.5", 858 | "windows_x86_64_gnullvm 0.48.5", 859 | "windows_x86_64_msvc 0.48.5", 860 | ] 861 | 862 | [[package]] 863 | name = "windows-targets" 864 | version = "0.52.6" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 867 | dependencies = [ 868 | "windows_aarch64_gnullvm 0.52.6", 869 | "windows_aarch64_msvc 0.52.6", 870 | "windows_i686_gnu 0.52.6", 871 | "windows_i686_gnullvm", 872 | "windows_i686_msvc 0.52.6", 873 | "windows_x86_64_gnu 0.52.6", 874 | "windows_x86_64_gnullvm 0.52.6", 875 | "windows_x86_64_msvc 0.52.6", 876 | ] 877 | 878 | [[package]] 879 | name = "windows_aarch64_gnullvm" 880 | version = "0.48.5" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 883 | 884 | [[package]] 885 | name = "windows_aarch64_gnullvm" 886 | version = "0.52.6" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 889 | 890 | [[package]] 891 | name = "windows_aarch64_msvc" 892 | version = "0.48.5" 893 | source = "registry+https://github.com/rust-lang/crates.io-index" 894 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 895 | 896 | [[package]] 897 | name = "windows_aarch64_msvc" 898 | version = "0.52.6" 899 | source = "registry+https://github.com/rust-lang/crates.io-index" 900 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 901 | 902 | [[package]] 903 | name = "windows_i686_gnu" 904 | version = "0.48.5" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 907 | 908 | [[package]] 909 | name = "windows_i686_gnu" 910 | version = "0.52.6" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 913 | 914 | [[package]] 915 | name = "windows_i686_gnullvm" 916 | version = "0.52.6" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 919 | 920 | [[package]] 921 | name = "windows_i686_msvc" 922 | version = "0.48.5" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 925 | 926 | [[package]] 927 | name = "windows_i686_msvc" 928 | version = "0.52.6" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 931 | 932 | [[package]] 933 | name = "windows_x86_64_gnu" 934 | version = "0.48.5" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 937 | 938 | [[package]] 939 | name = "windows_x86_64_gnu" 940 | version = "0.52.6" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 943 | 944 | [[package]] 945 | name = "windows_x86_64_gnullvm" 946 | version = "0.48.5" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 949 | 950 | [[package]] 951 | name = "windows_x86_64_gnullvm" 952 | version = "0.52.6" 953 | source = "registry+https://github.com/rust-lang/crates.io-index" 954 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 955 | 956 | [[package]] 957 | name = "windows_x86_64_msvc" 958 | version = "0.48.5" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 961 | 962 | [[package]] 963 | name = "windows_x86_64_msvc" 964 | version = "0.52.6" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 967 | 968 | [[package]] 969 | name = "write16" 970 | version = "1.0.0" 971 | source = "registry+https://github.com/rust-lang/crates.io-index" 972 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 973 | 974 | [[package]] 975 | name = "writeable" 976 | version = "0.5.5" 977 | source = "registry+https://github.com/rust-lang/crates.io-index" 978 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 979 | 980 | [[package]] 981 | name = "yoke" 982 | version = "0.7.5" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" 985 | dependencies = [ 986 | "serde", 987 | "stable_deref_trait", 988 | "yoke-derive", 989 | "zerofrom", 990 | ] 991 | 992 | [[package]] 993 | name = "yoke-derive" 994 | version = "0.7.5" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" 997 | dependencies = [ 998 | "proc-macro2", 999 | "quote", 1000 | "syn", 1001 | "synstructure", 1002 | ] 1003 | 1004 | [[package]] 1005 | name = "zerofrom" 1006 | version = "0.1.5" 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" 1008 | checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" 1009 | dependencies = [ 1010 | "zerofrom-derive", 1011 | ] 1012 | 1013 | [[package]] 1014 | name = "zerofrom-derive" 1015 | version = "0.1.5" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" 1018 | dependencies = [ 1019 | "proc-macro2", 1020 | "quote", 1021 | "syn", 1022 | "synstructure", 1023 | ] 1024 | 1025 | [[package]] 1026 | name = "zeroize" 1027 | version = "1.8.1" 1028 | source = "registry+https://github.com/rust-lang/crates.io-index" 1029 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 1030 | 1031 | [[package]] 1032 | name = "zerovec" 1033 | version = "0.10.4" 1034 | source = "registry+https://github.com/rust-lang/crates.io-index" 1035 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 1036 | dependencies = [ 1037 | "yoke", 1038 | "zerofrom", 1039 | "zerovec-derive", 1040 | ] 1041 | 1042 | [[package]] 1043 | name = "zerovec-derive" 1044 | version = "0.10.3" 1045 | source = "registry+https://github.com/rust-lang/crates.io-index" 1046 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 1047 | dependencies = [ 1048 | "proc-macro2", 1049 | "quote", 1050 | "syn", 1051 | ] 1052 | 1053 | [[package]] 1054 | name = "zr" 1055 | version = "1.2.1" 1056 | dependencies = [ 1057 | "directories", 1058 | "git2", 1059 | "git2_credentials", 1060 | "url", 1061 | ] 1062 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zr" 3 | edition = "2021" 4 | version = "1.2.1" 5 | authors = ["Jonathan Dahan "] 6 | description = "quick, simple zsh package manager" 7 | readme = "readme.md" 8 | keywords = ["zsh", "package", "manager", "cli"] 9 | license = "MPL-2.0" 10 | repository = "https://github.com/jedahan/zr.git" 11 | 12 | [dependencies] 13 | directories = "5.0.1" 14 | git2 = "0.19.0" 15 | git2_credentials = "0.14.0" 16 | url = "2.5.4" 17 | 18 | [profile.release] 19 | opt-level = "s" 20 | lto = true 21 | codegen-units = 1 22 | panic = "abort" 23 | strip = true 24 | -------------------------------------------------------------------------------- /benchmark.zsh: -------------------------------------------------------------------------------- 1 | # hyperfine --warmup 3 'zsh -d -f -l -c "source $PWD/benchmark.zsh && zrinit && exit"' 2 | function zrinit { 3 | XDG_CACHE_HOME=/tmp/zrbench ${1:-target/release/zr} \ 4 | sorin-ionescu/prezto.git/modules/git/alias.zsh \ 5 | sorin-ionescu/prezto.git/modules/history/init.zsh \ 6 | junegunn/fzf.git/shell/key-bindings.zsh \ 7 | zsh-users/zsh-autosuggestions \ 8 | zdharma/fast-syntax-highlighting \ 9 | molovo/tipz \ 10 | geometry-zsh/geometry \ 11 | jedahan/geometry-hydrate \ 12 | jedahan/geometry-todo \ 13 | geometry-zsh/geometry \ 14 | ael-code/zsh-colored-man-pages \ 15 | momo-lab/zsh-abbrev-alias \ 16 | jedahan/alacritty-completions \ 17 | zpm-zsh/ssh 18 | } 19 | 20 | # For comparing different release profiles 21 | 22 | # hyperfine --warmup 3 \ 23 | # 'zsh -d -f -l -c "source $PWD/benchmark.zsh && zrinit target/release/zr-0 && exit"' \ 24 | # 'zsh -d -f -l -c "source $PWD/benchmark.zsh && zrinit target/release/zr-s && exit"' \ 25 | # 'zsh -d -f -l -c "source $PWD/benchmark.zsh && zrinit target/release/zr-3 && exit"' \ 26 | # 'zsh -d -f -l -c "source $PWD/benchmark.zsh && zrinit target/release/zr-lto && exit"' \ 27 | # 'zsh -d -f -l -c "source $PWD/benchmark.zsh && zrinit target/release/zr-default && exit"' 28 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # 1.2.1 2 | * change some build settings to make the binary smaller 3 | 4 | # 1.2.0 5 | * add +help, +version 6 | 7 | # 1.1.1 8 | * default to recursive clones (thanks @olets) 9 | * update dependencies 10 | 11 | # 1.1.0 12 | * update dependencies to support openssl 3 (thanks @TimB87) 13 | 14 | # 1.0.2 15 | * cleanup help output 16 | 17 | # 1.0.1 18 | * add `functions` directories to fpath if functions are found 19 | * updated dependencies 20 | 21 | # 1.0.0 22 | * Finally released! 23 | * :warning: if you are updating from 0.8.2 (last published crates.io version) this will break your config! 24 | * read the changelog for 0.9.0 on how to update 25 | * Thank you @ralsei and @tekumara 26 | 27 | # 0.9.0 28 | 29 | * Deprecated `load`, `update`, and `list` 30 | * Write to stdout instead of ~/.config/zr 31 | * Use XDG_CACHE_DIR/zr or $HOME/.cache/zr for caching plugins 32 | 33 | * Added support for completions, thanks @Avi-D-coder! 34 | 35 | * Strip binary, reducing size from 4.9Mb to 1.8Mb 36 | * Removed dependency on clap, reducing size by 700kb 37 | 38 | # 0.8.2 39 | 40 | * Support git ssh credentials during updates! 41 | 42 | # 0.8.1 43 | 44 | * Only add a directory to PATH and fpath if there are executable files inside (non-windows specific) 45 | 46 | # 0.8.0 47 | 48 | - Removed `-h` shorthand for `--home`, now `-h` is for `--help` 49 | * Bumped minor version of deps 50 | 51 | # 0.7.2 52 | 53 | * Bumped patch version of deps 54 | 55 | # 0.7.1 56 | 57 | + Added some documentation which can be seen with `cargo doc -p zr --open`. 58 | * Removed extraneous `plugins` directory 59 | * Bumped minor version of libc crate 60 | 61 | # 0.7.0 62 | 63 | We finally have support for arbitrary git sources! 64 | It is recommended to delete your `ZR_HOME` directory. 65 | 66 | If you are cloning from non-github repo, and specifying the plugin path, you must have `.git` as a separator so `zr` knows what part of the path points to the git repo, and what points to the file. 67 | See the readme for examples. 68 | 69 | + added --home, -h config flag to specify home 70 | + added non-github repository support 71 | 72 | zr supports https://whatever.com/some/repo[.git/path/to/files.zsh] 73 | 74 | - Breaking change: plugins are cloned into top-level `ZR_HOME` (no author/name subdirectories) 75 | - Breaking change: `zr list` now shows full identifier for sources (no author/name shortname) 76 | 77 | # 0.6.6 78 | 79 | + Update to rust 2018 80 | 81 | # 0.6.5 82 | 83 | + Fix path generation on windows (thanks @nshtg!) 84 | 85 | # 0.6.4 86 | 87 | + Update dependencies 88 | + Minor cleanup 89 | 90 | # 0.6.3 91 | 92 | + Update dependencies 93 | 94 | # 0.6.2 95 | 96 | + License under the MPL-2.0 97 | + Publish to crates.io 98 | 99 | # 0.6.1 100 | 101 | + Create supporting directories if they do not exist 102 | 103 | # 0.6.0 sabanus 104 | 105 | - Breaking change: `load` now requires the path to be part of the plugin definition 106 | 107 | zr load author/plugin some/file.zsh 108 | 109 | Now must be 110 | 111 | zr load author/plugin/some/file.zsh 112 | 113 | This fixes a lot of brittleness 114 | 115 | # 0.5.0 116 | 117 | + Added sample zshrc benchmark 118 | - Removed `add` and `reset` 119 | 120 | # 0.4.8 121 | 122 | + Added output when cloning repositories 123 | - Deprecated `add` and `reset`, as `load` is fast enough, and you can regen when mtime is different via 124 | 125 | [[ ~/.zshrc -nt ~/.zr/init.zsh ]] && { zr load \ ... } 126 | 127 | # 0.4.6 fink 128 | 129 | Added `load` command, which is about twice as fast as generating init.zsh. 130 | If you had a *zshrc* that looked like: 131 | 132 | zr add some/plugin 133 | zr add other/plugin some/file.zsh 134 | 135 | You can migrate to 136 | 137 | zr load \ 138 | some/plugin \ 139 | other/plugin some/file.zsh 140 | 141 | Which will generate everything in one go. 142 | 143 | This might be fast enough to always regenerate init.zsh on shell load. 144 | 145 | Also added benchmarks, which can be run on nightly now. 146 | 147 | # 0.4.3 neotoma 148 | 149 | This is the first public release of zr! 150 | 151 | Thank you to [SX91](https://github.com/SX91) for contributing fixes for linux. 152 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | Mozilla Public License, version 2.0 2 | 3 | 1. Definitions 4 | 5 | 1.1. “Contributor” 6 | 7 | means each individual or legal entity that creates, contributes to the 8 | creation of, or owns Covered Software. 9 | 10 | 1.2. “Contributor Version” 11 | 12 | means the combination of the Contributions of others (if any) used by a 13 | Contributor and that particular Contributor’s Contribution. 14 | 15 | 1.3. “Contribution” 16 | 17 | means Covered Software of a particular Contributor. 18 | 19 | 1.4. “Covered Software” 20 | 21 | means Source Code Form to which the initial Contributor has attached the 22 | notice in Exhibit A, the Executable Form of such Source Code Form, and 23 | Modifications of such Source Code Form, in each case including portions 24 | thereof. 25 | 26 | 1.5. “Incompatible With Secondary Licenses” 27 | means 28 | 29 | a. that the initial Contributor has attached the notice described in 30 | Exhibit B to the Covered Software; or 31 | 32 | b. that the Covered Software was made available under the terms of version 33 | 1.1 or earlier of the License, but not also under the terms of a 34 | Secondary License. 35 | 36 | 1.6. “Executable Form” 37 | 38 | means any form of the work other than Source Code Form. 39 | 40 | 1.7. “Larger Work” 41 | 42 | means a work that combines Covered Software with other material, in a separate 43 | file or files, that is not Covered Software. 44 | 45 | 1.8. “License” 46 | 47 | means this document. 48 | 49 | 1.9. “Licensable” 50 | 51 | means having the right to grant, to the maximum extent possible, whether at the 52 | time of the initial grant or subsequently, any and all of the rights conveyed by 53 | this License. 54 | 55 | 1.10. “Modifications” 56 | 57 | means any of the following: 58 | 59 | a. any file in Source Code Form that results from an addition to, deletion 60 | from, or modification of the contents of Covered Software; or 61 | 62 | b. any new file in Source Code Form that contains any Covered Software. 63 | 64 | 1.11. “Patent Claims” of a Contributor 65 | 66 | means any patent claim(s), including without limitation, method, process, 67 | and apparatus claims, in any patent Licensable by such Contributor that 68 | would be infringed, but for the grant of the License, by the making, 69 | using, selling, offering for sale, having made, import, or transfer of 70 | either its Contributions or its Contributor Version. 71 | 72 | 1.12. “Secondary License” 73 | 74 | means either the GNU General Public License, Version 2.0, the GNU Lesser 75 | General Public License, Version 2.1, the GNU Affero General Public 76 | License, Version 3.0, or any later versions of those licenses. 77 | 78 | 1.13. “Source Code Form” 79 | 80 | means the form of the work preferred for making modifications. 81 | 82 | 1.14. “You” (or “Your”) 83 | 84 | means an individual or a legal entity exercising rights under this 85 | License. For legal entities, “You” includes any entity that controls, is 86 | controlled by, or is under common control with You. For purposes of this 87 | definition, “control” means (a) the power, direct or indirect, to cause 88 | the direction or management of such entity, whether by contract or 89 | otherwise, or (b) ownership of more than fifty percent (50%) of the 90 | outstanding shares or beneficial ownership of such entity. 91 | 92 | 93 | 2. License Grants and Conditions 94 | 95 | 2.1. Grants 96 | 97 | Each Contributor hereby grants You a world-wide, royalty-free, 98 | non-exclusive license: 99 | 100 | a. under intellectual property rights (other than patent or trademark) 101 | Licensable by such Contributor to use, reproduce, make available, 102 | modify, display, perform, distribute, and otherwise exploit its 103 | Contributions, either on an unmodified basis, with Modifications, or as 104 | part of a Larger Work; and 105 | 106 | b. under Patent Claims of such Contributor to make, use, sell, offer for 107 | sale, have made, import, and otherwise transfer either its Contributions 108 | or its Contributor Version. 109 | 110 | 2.2. Effective Date 111 | 112 | The licenses granted in Section 2.1 with respect to any Contribution become 113 | effective for each Contribution on the date the Contributor first distributes 114 | such Contribution. 115 | 116 | 2.3. Limitations on Grant Scope 117 | 118 | The licenses granted in this Section 2 are the only rights granted under this 119 | License. No additional rights or licenses will be implied from the distribution 120 | or licensing of Covered Software under this License. Notwithstanding Section 121 | 2.1(b) above, no patent license is granted by a Contributor: 122 | 123 | a. for any code that a Contributor has removed from Covered Software; or 124 | 125 | b. for infringements caused by: (i) Your and any other third party’s 126 | modifications of Covered Software, or (ii) the combination of its 127 | Contributions with other software (except as part of its Contributor 128 | Version); or 129 | 130 | c. under Patent Claims infringed by Covered Software in the absence of its 131 | Contributions. 132 | 133 | This License does not grant any rights in the trademarks, service marks, or 134 | logos of any Contributor (except as may be necessary to comply with the 135 | notice requirements in Section 3.4). 136 | 137 | 2.4. Subsequent Licenses 138 | 139 | No Contributor makes additional grants as a result of Your choice to 140 | distribute the Covered Software under a subsequent version of this License 141 | (see Section 10.2) or under the terms of a Secondary License (if permitted 142 | under the terms of Section 3.3). 143 | 144 | 2.5. Representation 145 | 146 | Each Contributor represents that the Contributor believes its Contributions 147 | are its original creation(s) or it has sufficient rights to grant the 148 | rights to its Contributions conveyed by this License. 149 | 150 | 2.6. Fair Use 151 | 152 | This License is not intended to limit any rights You have under applicable 153 | copyright doctrines of fair use, fair dealing, or other equivalents. 154 | 155 | 2.7. Conditions 156 | 157 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 158 | Section 2.1. 159 | 160 | 161 | 3. Responsibilities 162 | 163 | 3.1. Distribution of Source Form 164 | 165 | All distribution of Covered Software in Source Code Form, including any 166 | Modifications that You create or to which You contribute, must be under the 167 | terms of this License. You must inform recipients that the Source Code Form 168 | of the Covered Software is governed by the terms of this License, and how 169 | they can obtain a copy of this License. You may not attempt to alter or 170 | restrict the recipients’ rights in the Source Code Form. 171 | 172 | 3.2. Distribution of Executable Form 173 | 174 | If You distribute Covered Software in Executable Form then: 175 | 176 | a. such Covered Software must also be made available in Source Code Form, 177 | as described in Section 3.1, and You must inform recipients of the 178 | Executable Form how they can obtain a copy of such Source Code Form by 179 | reasonable means in a timely manner, at a charge no more than the cost 180 | of distribution to the recipient; and 181 | 182 | b. You may distribute such Executable Form under the terms of this License, 183 | or sublicense it under different terms, provided that the license for 184 | the Executable Form does not attempt to limit or alter the recipients’ 185 | rights in the Source Code Form under this License. 186 | 187 | 3.3. Distribution of a Larger Work 188 | 189 | You may create and distribute a Larger Work under terms of Your choice, 190 | provided that You also comply with the requirements of this License for the 191 | Covered Software. If the Larger Work is a combination of Covered Software 192 | with a work governed by one or more Secondary Licenses, and the Covered 193 | Software is not Incompatible With Secondary Licenses, this License permits 194 | You to additionally distribute such Covered Software under the terms of 195 | such Secondary License(s), so that the recipient of the Larger Work may, at 196 | their option, further distribute the Covered Software under the terms of 197 | either this License or such Secondary License(s). 198 | 199 | 3.4. Notices 200 | 201 | You may not remove or alter the substance of any license notices (including 202 | copyright notices, patent notices, disclaimers of warranty, or limitations 203 | of liability) contained within the Source Code Form of the Covered 204 | Software, except that You may alter any license notices to the extent 205 | required to remedy known factual inaccuracies. 206 | 207 | 3.5. Application of Additional Terms 208 | 209 | You may choose to offer, and to charge a fee for, warranty, support, 210 | indemnity or liability obligations to one or more recipients of Covered 211 | Software. However, You may do so only on Your own behalf, and not on behalf 212 | of any Contributor. You must make it absolutely clear that any such 213 | warranty, support, indemnity, or liability obligation is offered by You 214 | alone, and You hereby agree to indemnify every Contributor for any 215 | liability incurred by such Contributor as a result of warranty, support, 216 | indemnity or liability terms You offer. You may include additional 217 | disclaimers of warranty and limitations of liability specific to any 218 | jurisdiction. 219 | 220 | 4. Inability to Comply Due to Statute or Regulation 221 | 222 | If it is impossible for You to comply with any of the terms of this License 223 | with respect to some or all of the Covered Software due to statute, judicial 224 | order, or regulation then You must: (a) comply with the terms of this License 225 | to the maximum extent possible; and (b) describe the limitations and the code 226 | they affect. Such description must be placed in a text file included with all 227 | distributions of the Covered Software under this License. Except to the 228 | extent prohibited by statute or regulation, such description must be 229 | sufficiently detailed for a recipient of ordinary skill to be able to 230 | understand it. 231 | 232 | 5. Termination 233 | 234 | 5.1. The rights granted under this License will terminate automatically if You 235 | fail to comply with any of its terms. However, if You become compliant, 236 | then the rights granted under this License from a particular Contributor 237 | are reinstated (a) provisionally, unless and until such Contributor 238 | explicitly and finally terminates Your grants, and (b) on an ongoing basis, 239 | if such Contributor fails to notify You of the non-compliance by some 240 | reasonable means prior to 60 days after You have come back into compliance. 241 | Moreover, Your grants from a particular Contributor are reinstated on an 242 | ongoing basis if such Contributor notifies You of the non-compliance by 243 | some reasonable means, this is the first time You have received notice of 244 | non-compliance with this License from such Contributor, and You become 245 | compliant prior to 30 days after Your receipt of the notice. 246 | 247 | 5.2. If You initiate litigation against any entity by asserting a patent 248 | infringement claim (excluding declaratory judgment actions, counter-claims, 249 | and cross-claims) alleging that a Contributor Version directly or 250 | indirectly infringes any patent, then the rights granted to You by any and 251 | all Contributors for the Covered Software under Section 2.1 of this License 252 | shall terminate. 253 | 254 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 255 | license agreements (excluding distributors and resellers) which have been 256 | validly granted by You or Your distributors under this License prior to 257 | termination shall survive termination. 258 | 259 | 6. Disclaimer of Warranty 260 | 261 | Covered Software is provided under this License on an “as is” basis, without 262 | warranty of any kind, either expressed, implied, or statutory, including, 263 | without limitation, warranties that the Covered Software is free of defects, 264 | merchantable, fit for a particular purpose or non-infringing. The entire 265 | risk as to the quality and performance of the Covered Software is with You. 266 | Should any Covered Software prove defective in any respect, You (not any 267 | Contributor) assume the cost of any necessary servicing, repair, or 268 | correction. This disclaimer of warranty constitutes an essential part of this 269 | License. No use of any Covered Software is authorized under this License 270 | except under this disclaimer. 271 | 272 | 7. Limitation of Liability 273 | 274 | Under no circumstances and under no legal theory, whether tort (including 275 | negligence), contract, or otherwise, shall any Contributor, or anyone who 276 | distributes Covered Software as permitted above, be liable to You for any 277 | direct, indirect, special, incidental, or consequential damages of any 278 | character including, without limitation, damages for lost profits, loss of 279 | goodwill, work stoppage, computer failure or malfunction, or any and all 280 | other commercial damages or losses, even if such party shall have been 281 | informed of the possibility of such damages. This limitation of liability 282 | shall not apply to liability for death or personal injury resulting from such 283 | party’s negligence to the extent applicable law prohibits such limitation. 284 | Some jurisdictions do not allow the exclusion or limitation of incidental or 285 | consequential damages, so this exclusion and limitation may not apply to You. 286 | 287 | 8. Litigation 288 | 289 | Any litigation relating to this License may be brought only in the courts of 290 | a jurisdiction where the defendant maintains its principal place of business 291 | and such litigation shall be governed by laws of that jurisdiction, without 292 | reference to its conflict-of-law provisions. Nothing in this Section shall 293 | prevent a party’s ability to bring cross-claims or counter-claims. 294 | 295 | 9. Miscellaneous 296 | 297 | This License represents the complete agreement concerning the subject matter 298 | hereof. If any provision of this License is held to be unenforceable, such 299 | provision shall be reformed only to the extent necessary to make it 300 | enforceable. Any law or regulation which provides that the language of a 301 | contract shall be construed against the drafter shall not be used to construe 302 | this License against a Contributor. 303 | 304 | 305 | 10. Versions of the License 306 | 307 | 10.1. New Versions 308 | 309 | Mozilla Foundation is the license steward. Except as provided in Section 310 | 10.3, no one other than the license steward has the right to modify or 311 | publish new versions of this License. Each version will be given a 312 | distinguishing version number. 313 | 314 | 10.2. Effect of New Versions 315 | 316 | You may distribute the Covered Software under the terms of the version of 317 | the License under which You originally received the Covered Software, or 318 | under the terms of any subsequent version published by the license 319 | steward. 320 | 321 | 10.3. Modified Versions 322 | 323 | If you create software not governed by this License, and you want to 324 | create a new license for such software, you may create and use a modified 325 | version of this License if you rename the license and remove any 326 | references to the name of the license steward (except to note that such 327 | modified license differs from this License). 328 | 329 | 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses 330 | If You choose to distribute Source Code Form that is Incompatible With 331 | Secondary Licenses under the terms of this version of the License, the 332 | notice described in Exhibit B of this License must be attached. 333 | 334 | Exhibit A - Source Code Form License Notice 335 | 336 | This Source Code Form is subject to the 337 | terms of the Mozilla Public License, v. 338 | 2.0. If a copy of the MPL was not 339 | distributed with this file, You can 340 | obtain one at 341 | http://mozilla.org/MPL/2.0/. 342 | 343 | If it is not possible or desirable to put the notice in a particular file, then 344 | You may include the notice in a location (such as a LICENSE file in a relevant 345 | directory) where a recipient would be likely to look for such a notice. 346 | 347 | You may add additional accurate notices of copyright ownership. 348 | 349 | Exhibit B - “Incompatible With Secondary Licenses” Notice 350 | 351 | This Source Code Form is “Incompatible 352 | With Secondary Licenses”, as defined by 353 | the Mozilla Public License, v. 2.0. 354 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | zrat 2 | 3 | ### zr(at) 4 | 5 | nimble zsh plugin manager 6 | 7 | zr 1.2.1 8 | by Jonathan Dahan 9 | 10 | Example 11 | 12 | . <(zr geometry-zsh/geometry junegunn/fzf.git/shell/key-bindings.zsh) 13 | 14 | Format 15 | 16 | zr author/name *.zsh from github.com/author/name 17 | zr author/name/file.zsh file.zsh from github.com/author/name 18 | zr https://gitlab.com/a/plugin *.zsh from gitlab.com/a/plugin 19 | zr https://gitlab.com/a/plugin.git/file.zsh file.zsh from gitlab.com/a/plugin 20 | 21 | Commands 22 | 23 | zr +update update plugins from already sourced zsh 24 | zr +help show help 25 | 26 | #### install 27 | 28 | `zr` is published to crates.io, and can be installed with `cargo install zr` 29 | 30 | #### usage 31 | 32 | Add this to your *~/.zshrc*: 33 | 34 | ```zsh 35 | . <(zr frmendes/geometry junegunn/fzf.git/shell/key-bindings.zsh) 36 | ``` 37 | 38 | A bit more complex example, that only generates when .zshrc has been updated: 39 | 40 | ```zsh 41 | # Generate new ~/.config/zr.zsh if it does not exist or if ~/.zshrc has been changed 42 | if [[ ! -f ~/.config/zr.zsh ]] || [[ ~/.zshrc -nt ~/.config/zr.zsh ]]; then 43 | zr \ 44 | frmendes/geometry \ 45 | jedahan/geometry-hydrate \ 46 | junegunn/fzf.git/shell/key-bindings.zsh \ 47 | > ~/.config/zr.zsh 48 | fi 49 | 50 | source ~/.config/zr.zsh 51 | ``` 52 | 53 | #### identifiers 54 | 55 | `zr` supports four identifier formats. The last format requires `.git` as a delimeter. 56 | 57 | format | resolves to 58 | -------------------------------------------|----------- 59 | `author/name` | __*.zsh__ from https://github.com/author/name 60 | `author/name/file.zsh` | __file.zsh__ from https://github.com/author/name 61 | `https://gitlab.com/a/plugin` | __*.zsh__ from https://gitlab.com/a/plugin 62 | `https://gitlab.com/a/plugin.git/file.zsh` | __file.zsh__ from https://gitlab.com/a/plugin.git. The `.git` is used as a delimeter, and is required. 63 | 64 | #### speed 65 | 66 | The following two benchmarks show on a 2024 14" macbook pro m4 67 | * it takes 10ms to generate a sourceable script from a dozen or so repos 68 | * it takes an additional 30ms for zsh to load said script 69 | 70 | ```zsh 71 | $ hyperfine --warmup 3 \ 72 | --command-name "generate shell script" \ 73 | 'zsh -d -f -l -c "source benchmark.zsh && zrinit && exit"' \ 74 | --command-name "generate and load shell script" \ 75 | 'zsh -d -f -l -c "source benchmark.zsh && . <(zrinit) && exit"' 76 | 77 | Benchmark 1: generate shell script 78 | Time (mean ± σ): 8.2 ms ± 1.3 ms [User: 3.4 ms, System: 3.7 ms] 79 | Range (min … max): 7.2 ms … 12.1 ms 258 runs 80 | 81 | Benchmark 2: generate and load shell script 82 | Time (mean ± σ): 42.1 ms ± 2.6 ms [User: 22.4 ms, System: 58.2 ms] 83 | Range (min … max): 38.7 ms … 51.2 ms 54 runs 84 | ``` 85 | 86 | ```zsh 87 | # benchmark.zsh 88 | function zrinit { 89 | XDG_CACHE_HOME=/tmp/zrbenchmark zr sorin-ionescu/prezto.git/modules/git/alias.zsh \ 90 | sorin-ionescu/prezto.git/modules/history/init.zsh \ 91 | junegunn/fzf.git/shell/key-bindings.zsh \ 92 | zsh-users/zsh-autosuggestions \ 93 | zdharma/fast-syntax-highlighting \ 94 | molovo/tipz \ 95 | geometry-zsh/geometry \ 96 | jedahan/geometry-hydrate \ 97 | jedahan/geometry-todo \ 98 | geometry-zsh/geometry \ 99 | ael-code/zsh-colored-man-pages \ 100 | momo-lab/zsh-abbrev-alias \ 101 | jedahan/alacritty-completions \ 102 | zpm-zsh/ssh 103 | } 104 | ``` 105 | 106 | #### publishing 107 | 108 | git tag 1.2.1 109 | git push --tags 110 | cargo publish --locked 111 | 112 | #### thanks 113 | 114 | - [SX91](https://github.com/SX91) for linux fixes 115 | - [alanpearce](https://github.com/alanpearce) for bug reports and nix package 116 | - [nshtg](https://github.com/nshtg) for bug reports and windows fix 117 | - [foray1010](https://github.com/foray1010) for improving install instructions 118 | - [Avi-D-coder](https://github.com/avi-d-coder) for adding completions support 119 | - [ralsei](https://github.com/ralsei) for prodding to update crates.io 120 | - [tekumara](https://github.com/tekumara) for helping figure out --update 121 | - [myrovh](https://github.com/myrovh) for fixing panics on some linux systems 122 | - [TimB87](https://github.com/TimB87) for adding openssl 3 support 123 | - [olets](https://github.com/olets) for adding recursive clone support 124 | - everyone on [#rust-beginners](irc://irc.mozilla.org/rust-beginners) 125 | -------------------------------------------------------------------------------- /src/identifier.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | use url::{ParseError, Url}; 3 | 4 | #[derive(Clone, Debug, PartialEq)] 5 | pub struct Identifier { 6 | pub name: String, 7 | pub url: Url, 8 | pub dir: PathBuf, 9 | pub file: Option, 10 | } 11 | 12 | impl Identifier { 13 | pub fn new(name: String) -> Self { 14 | if !name.contains('/') { 15 | panic!("'{}' is not a valid identifier", &name) 16 | } 17 | 18 | let uri = if name.contains(".git") { 19 | format!("{}.git", name.split(".git").next().unwrap()) 20 | } else { 21 | name.clone() 22 | }; 23 | 24 | let url = match Url::parse(&uri) { 25 | Ok(url) => url, 26 | Err(ParseError::RelativeUrlWithoutBase) => { 27 | Url::parse(&format!("https://github.com/{}", &uri)).unwrap() 28 | } 29 | Err(e) => panic!("{}", e), 30 | }; 31 | 32 | let file = name.split(".git/").nth(1).map(String::from); 33 | let dir = PathBuf::from(url.path_segments().unwrap().collect::>().join("/")); 34 | Identifier { 35 | name, 36 | url, 37 | dir, 38 | file, 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | //! # zr - a fast, friendly zsh package manager 2 | //! 3 | //! At its core, zr: 4 | //! * takes a list of urls to git repositories 5 | //! * downloads the code from those repos 6 | //! * and generates an init.zsh to setup paths and load zsh scripts for your zshrc 7 | //! 8 | extern crate directories; 9 | extern crate git2_credentials; 10 | extern crate url; 11 | 12 | use directories::ProjectDirs; 13 | use std::env; 14 | use std::fs::OpenOptions; 15 | use std::io::{BufRead, BufReader}; 16 | 17 | pub mod identifier; 18 | pub mod plugin; 19 | pub mod plugins; 20 | 21 | use crate::identifier::Identifier; 22 | use crate::plugins::Plugins; 23 | 24 | const NAME: &str = env!("CARGO_PKG_NAME"); 25 | const VERSION: &str = env!("CARGO_PKG_VERSION"); 26 | 27 | fn main() { 28 | if let Some(subcommand) = env::args().nth(1) { 29 | if ["--update", "+update", "-u", "+u" ].contains(&subcommand.as_str()) { 30 | update(); 31 | return; 32 | } 33 | if ["--help", "+help", "-h", "+h" ].contains(&subcommand.as_str()) { 34 | help(); 35 | return; 36 | } 37 | if ["--version", "+version", "-v", "+v" ].contains(&subcommand.as_str()) { 38 | version(); 39 | return; 40 | } 41 | if subcommand.as_str().contains('/') { 42 | let identifiers = env::args().skip(1).map(Identifier::new).collect(); 43 | let plugins = load(identifiers); 44 | println!("{}", plugins); 45 | return; 46 | } 47 | } 48 | help() 49 | } 50 | 51 | fn version() { 52 | println!("{name} {version}", version=VERSION, name=NAME); 53 | } 54 | 55 | fn help() { 56 | version(); 57 | println!("by Jonathan Dahan 58 | 59 | Example 60 | 61 | . <(zr geometry-zsh/geometry junegunn/fzf.git/shell/key-bindings.zsh) 62 | 63 | Format 64 | 65 | {name} author/name *.zsh from github.com/author/name 66 | {name} author/name/file.zsh file.zsh from github.com/author/name 67 | {name} https://gitlab.com/a/plugin *.zsh from gitlab.com/a/plugin 68 | {name} https://gitlab.com/a/plugin.git/file.zsh file.zsh from gitlab.com/a/plugin 69 | 70 | Commands 71 | 72 | {name} +update update plugins from already sourced zsh 73 | {name} +help show help", name=NAME); 74 | } 75 | 76 | /// Take a list of identifiers (from cli args) and output sourceable zsh 77 | pub fn load(identifiers: Vec) -> plugins::Plugins { 78 | let dirs = ProjectDirs::from("", "", NAME).expect("could not get cache directory"); 79 | 80 | return Plugins::new(dirs.cache_dir(), identifiers); 81 | } 82 | 83 | pub fn update() { 84 | let zr = env::var_os("_ZR").expect("_ZR env variable unset, bailing on update"); 85 | 86 | if let Ok(init_file) = OpenOptions::new().read(true).open(zr) { 87 | let identifiers = BufReader::new(&init_file) 88 | .lines() 89 | .map(|line| line.unwrap()) 90 | .filter(|line| line.starts_with("# ")) 91 | .map(|line| String::from(line.split_whitespace().last().unwrap())) 92 | .map(Identifier::new) 93 | .collect(); 94 | 95 | let plugins = load(identifiers); 96 | plugins.update().expect("could not update plugins") 97 | } 98 | } 99 | 100 | #[test] 101 | fn test_load() { 102 | let _input = "geometry-zsh/geometry.git/geometry.zsh zsh-users/zsh-autosuggestions"; 103 | 104 | let _expected_output = " 105 | export _ZR=$0 106 | # geometry-zsh/geometry.git/geometry.zsh 107 | source geometry.zsh 108 | 109 | # zsh-users/zsh-autosuggestions 110 | source /home/micro/.cache/zr/zsh-users/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh 111 | source /home/micro/.cache/zr/zsh-users/zsh-autosuggestions/zsh-autosuggestions.zsh 112 | fpath+=/home/micro/.cache/zr/zsh-users/zsh-autosuggestions/ 113 | PATH=/home/micro/.cache/zr/zsh-users/zsh-autosuggestions:$PATH 114 | "; 115 | } 116 | -------------------------------------------------------------------------------- /src/plugin.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use std::ffi::OsStr; 3 | use std::iter::FromIterator; 4 | use std::path::{Path, PathBuf}; 5 | use std::{fmt, result}; 6 | 7 | use crate::identifier::Identifier; 8 | //#[cfg(!windows)] 9 | use std::os::unix::fs::PermissionsExt; 10 | 11 | /// A Plugin is an in-memory representation of the identifier and files to load 12 | pub struct Plugin { 13 | pub identifier: Identifier, 14 | pub files: HashSet, 15 | } 16 | 17 | impl Plugin { 18 | /// Simple git clone; does not support ssh authentication yet 19 | fn clone_if_empty(source: &url::Url, path: &Path) -> Result<(), std::io::Error> { 20 | if !path.is_dir() { 21 | eprintln!("cloning {} into {:?}", source, path); 22 | git2::Repository::clone_recurse(source.as_str(), path).unwrap(); 23 | } 24 | Ok(()) 25 | } 26 | 27 | /// The only thing you need to know is an identifier and the cache directory 28 | /// 29 | /// Side-effects include 30 | /// 31 | /// * attempting to create the cache if it does not exist 32 | /// * downloading the repo if it is empty 33 | /// 34 | pub fn new(cache: &Path, identifier: Identifier) -> Result { 35 | let Identifier { 36 | ref url, 37 | ref dir, 38 | ref file, 39 | .. 40 | } = identifier.clone(); 41 | let path = &cache.join(dir); 42 | Plugin::clone_if_empty(url, path)?; 43 | 44 | // If we were given an Identifier with a file, return a plugin with just that file 45 | if let Some(file) = file { 46 | let mut files = HashSet::with_capacity(1); 47 | files.insert(path.join(file)); 48 | return Ok(Plugin { identifier, files }); 49 | }; 50 | 51 | // Get a list of all files with an extension 52 | let files: Vec = path 53 | .read_dir() 54 | .unwrap() 55 | .filter_map(result::Result::ok) 56 | .map(|file| file.path()) 57 | .filter(|file| file.is_file()) 58 | .collect(); 59 | 60 | let name = dir.components().last().unwrap(); 61 | 62 | let sources: Vec = { 63 | if let Some(antigen_plugin_file) = files 64 | .iter() 65 | .find(|&file| *file == path.join(name).with_extension("plugin.zsh")) 66 | { 67 | vec![antigen_plugin_file.to_owned()] 68 | } else if let Some(prezto_plugin_file) = 69 | files.iter().find(|&file| *file == path.join("init.zsh")) 70 | { 71 | vec![prezto_plugin_file.to_owned()] 72 | } else { 73 | let zsh_plugin_files: Vec<_> = files 74 | .iter() 75 | .cloned() 76 | .filter(|file| file.extension() == Some(OsStr::new("zsh"))) 77 | .collect(); 78 | if !zsh_plugin_files.is_empty() { 79 | zsh_plugin_files 80 | } else { 81 | let completion_files: Vec<_> = files 82 | .iter() 83 | .cloned() 84 | .filter(|file| { 85 | if let Some(name) = file.file_name() { 86 | name.to_string_lossy().starts_with('_') 87 | } else { 88 | false 89 | } 90 | }) 91 | .collect(); 92 | if !completion_files.is_empty() { 93 | completion_files 94 | } else { 95 | files 96 | .iter() 97 | .cloned() 98 | .filter(|file| file.extension() == Some(OsStr::new("sh"))) 99 | .collect() 100 | } 101 | } 102 | } 103 | }; 104 | 105 | Ok(Plugin { 106 | identifier, 107 | files: HashSet::from_iter(sources), 108 | }) 109 | } 110 | } 111 | 112 | /// This actually is the serialization for init.zsh 113 | impl fmt::Display for Plugin { 114 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 115 | let mut basedirs = HashSet::new(); 116 | writeln!(formatter, "# {}", self.identifier.name)?; 117 | for file in &self.files { 118 | if let Some(basedir) = file.parent() { 119 | basedirs.insert(basedir); 120 | } 121 | if let Some(filename) = file.to_str() { 122 | if !file.file_name().unwrap().to_string_lossy().starts_with('_') { 123 | writeln!(formatter, "source {}", filename.replace('\\', "/"))?; 124 | } 125 | } 126 | } 127 | 128 | if cfg!(windows) { 129 | for basedir in basedirs.iter() { 130 | let dir = basedir.to_string_lossy().replace('\\', "/"); 131 | writeln!(formatter, "fpath+={}/", dir)?; 132 | writeln!(formatter, "PATH={}:$PATH", dir)?; 133 | } 134 | } else { 135 | // Add directories to fpath and PATH if we find any executable file 136 | for dir in basedirs.iter() { 137 | if let Ok(files) = dir.read_dir() { 138 | let has_exe = files 139 | .filter_map(|files| files.ok()) 140 | .filter_map(|direntry| direntry.metadata().ok()) 141 | .filter(|metadata| metadata.is_file()) 142 | .any(|metadata| metadata.permissions().mode() & 0o111 != 0); 143 | let has_completions = dir 144 | .read_dir() 145 | .unwrap() 146 | .filter_map(|files| files.ok()) 147 | .any(|direntry| direntry.file_name().to_string_lossy().starts_with('_')); 148 | let has_functions = dir 149 | .read_dir() 150 | .unwrap() 151 | .filter_map(|files| files.ok()) 152 | .any(|direntry| direntry.file_name().to_string_lossy() == "functions"); 153 | if has_exe { 154 | writeln!(formatter, "PATH={}:$PATH", dir.display())?; 155 | } 156 | if has_exe || has_completions { 157 | writeln!(formatter, "fpath+={}/", dir.display())?; 158 | } 159 | if has_functions { 160 | writeln!(formatter, "fpath+={}/functions", dir.display())?; 161 | } 162 | } 163 | } 164 | } 165 | 166 | Ok(()) 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/plugins.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::fs::create_dir_all; 3 | use std::io::Error; 4 | use std::path::{Path, PathBuf}; 5 | 6 | use git2_credentials::CredentialHandler; 7 | 8 | use crate::identifier::Identifier; 9 | use crate::plugin::Plugin; 10 | 11 | /// Plugins are collected into different `home`s 12 | pub struct Plugins { 13 | plugins: Vec, 14 | cache: PathBuf, 15 | } 16 | 17 | impl Plugins { 18 | pub fn update(&self) -> Result<(), Error> { 19 | for plugin in &self.plugins { 20 | let dir = self.cache.join(&plugin.identifier.dir); 21 | let repo = git2::Repository::open(&dir).unwrap(); 22 | let mut callbacks = git2::RemoteCallbacks::new(); 23 | 24 | let git_config = git2::Config::open_default().unwrap(); 25 | let mut credential_handler = CredentialHandler::new(git_config); 26 | callbacks.credentials(move |url, username, allowed| { 27 | credential_handler.try_next_credential(url, username, allowed) 28 | }); 29 | 30 | callbacks.update_tips(|refspec, from, to| { 31 | eprintln!( 32 | "{} {} {:.6} ↓ {:.6}", 33 | &plugin.identifier.name, 34 | refspec.replace("refs/heads/", ""), 35 | from, 36 | to 37 | ); 38 | true 39 | }); 40 | let mut options = git2::FetchOptions::new(); 41 | options.remote_callbacks(callbacks); 42 | 43 | let mut remote = repo.find_remote("origin").unwrap(); 44 | remote 45 | .fetch(&["refs/heads/*:refs/heads/*"], Some(&mut options), None) 46 | .unwrap(); 47 | } 48 | Ok(()) 49 | } 50 | 51 | pub fn new(cache: &Path, identifiers: Vec) -> Plugins { 52 | if !cache.exists() { 53 | create_dir_all(cache).expect("failed to create the cache directory"); 54 | } 55 | 56 | let mut plugins: Vec = vec![]; 57 | 58 | for identifier in identifiers { 59 | if let Some(plugin) = plugins 60 | .iter_mut() 61 | .find(|plugin| plugin.identifier == identifier) 62 | { 63 | if let Some(file) = &identifier.file { 64 | plugin.files.insert(cache.join(&identifier.dir).join(file)); 65 | } 66 | }; 67 | 68 | if let Ok(plugin) = Plugin::new(cache, identifier) { 69 | plugins.push(plugin); 70 | } 71 | } 72 | 73 | Plugins { 74 | plugins, 75 | cache: cache.to_path_buf(), 76 | } 77 | } 78 | } 79 | 80 | impl fmt::Display for Plugins { 81 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 82 | writeln!(formatter, "export _ZR=$0")?; 83 | for plugin in &self.plugins { 84 | writeln!(formatter, "{}", plugin)?; 85 | } 86 | Ok(()) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /zrat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jedahan/zr/bd12bb569bc5a545c77da50cbc52a6b2ec97134b/zrat.png --------------------------------------------------------------------------------