├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE.txt ├── README.md └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /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 = "any_key" 16 | version = "0.1.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d21bb2cdab8087ed9d69411dd99c608dbede1df847c255b4d609f0399a3cb452" 19 | dependencies = [ 20 | "debugit", 21 | "mopa", 22 | ] 23 | 24 | [[package]] 25 | name = "arrayvec" 26 | version = "0.7.6" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 29 | 30 | [[package]] 31 | name = "async-channel" 32 | version = "2.3.1" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" 35 | dependencies = [ 36 | "concurrent-queue", 37 | "event-listener-strategy", 38 | "futures-core", 39 | "pin-project-lite", 40 | ] 41 | 42 | [[package]] 43 | name = "async-executor" 44 | version = "1.13.1" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" 47 | dependencies = [ 48 | "async-task", 49 | "concurrent-queue", 50 | "fastrand", 51 | "futures-lite", 52 | "slab", 53 | ] 54 | 55 | [[package]] 56 | name = "async-fs" 57 | version = "2.1.2" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" 60 | dependencies = [ 61 | "async-lock", 62 | "blocking", 63 | "futures-lite", 64 | ] 65 | 66 | [[package]] 67 | name = "async-io" 68 | version = "2.4.0" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" 71 | dependencies = [ 72 | "async-lock", 73 | "cfg-if", 74 | "concurrent-queue", 75 | "futures-io", 76 | "futures-lite", 77 | "parking", 78 | "polling", 79 | "rustix", 80 | "slab", 81 | "tracing", 82 | "windows-sys 0.59.0", 83 | ] 84 | 85 | [[package]] 86 | name = "async-lock" 87 | version = "3.4.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" 90 | dependencies = [ 91 | "event-listener", 92 | "event-listener-strategy", 93 | "pin-project-lite", 94 | ] 95 | 96 | [[package]] 97 | name = "async-net" 98 | version = "2.0.0" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" 101 | dependencies = [ 102 | "async-io", 103 | "blocking", 104 | "futures-lite", 105 | ] 106 | 107 | [[package]] 108 | name = "async-process" 109 | version = "2.3.0" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" 112 | dependencies = [ 113 | "async-channel", 114 | "async-io", 115 | "async-lock", 116 | "async-signal", 117 | "async-task", 118 | "blocking", 119 | "cfg-if", 120 | "event-listener", 121 | "futures-lite", 122 | "rustix", 123 | "tracing", 124 | ] 125 | 126 | [[package]] 127 | name = "async-signal" 128 | version = "0.2.10" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" 131 | dependencies = [ 132 | "async-io", 133 | "async-lock", 134 | "atomic-waker", 135 | "cfg-if", 136 | "futures-core", 137 | "futures-io", 138 | "rustix", 139 | "signal-hook-registry", 140 | "slab", 141 | "windows-sys 0.59.0", 142 | ] 143 | 144 | [[package]] 145 | name = "async-task" 146 | version = "4.7.1" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" 149 | 150 | [[package]] 151 | name = "atomic-waker" 152 | version = "1.1.2" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 155 | 156 | [[package]] 157 | name = "autocfg" 158 | version = "1.4.0" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 161 | 162 | [[package]] 163 | name = "bitflags" 164 | version = "2.9.0" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" 167 | 168 | [[package]] 169 | name = "blocking" 170 | version = "1.6.1" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" 173 | dependencies = [ 174 | "async-channel", 175 | "async-task", 176 | "futures-io", 177 | "futures-lite", 178 | "piper", 179 | ] 180 | 181 | [[package]] 182 | name = "cfg-if" 183 | version = "1.0.0" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 186 | 187 | [[package]] 188 | name = "concurrent-queue" 189 | version = "2.5.0" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" 192 | dependencies = [ 193 | "crossbeam-utils", 194 | ] 195 | 196 | [[package]] 197 | name = "crossbeam-utils" 198 | version = "0.8.21" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 201 | 202 | [[package]] 203 | name = "crossterm" 204 | version = "0.28.1" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" 207 | dependencies = [ 208 | "bitflags", 209 | "crossterm_winapi", 210 | "futures-core", 211 | "mio", 212 | "parking_lot", 213 | "rustix", 214 | "signal-hook", 215 | "signal-hook-mio", 216 | "winapi", 217 | ] 218 | 219 | [[package]] 220 | name = "crossterm_winapi" 221 | version = "0.9.1" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 224 | dependencies = [ 225 | "winapi", 226 | ] 227 | 228 | [[package]] 229 | name = "debugit" 230 | version = "0.1.2" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "63c2f7e3034df2b09f750327e23c1adfe33301e6b7388f05bb4fcc0fa46825e3" 233 | dependencies = [ 234 | "version_check 0.1.5", 235 | ] 236 | 237 | [[package]] 238 | name = "errno" 239 | version = "0.3.10" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 242 | dependencies = [ 243 | "libc", 244 | "windows-sys 0.59.0", 245 | ] 246 | 247 | [[package]] 248 | name = "event-listener" 249 | version = "5.4.0" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" 252 | dependencies = [ 253 | "concurrent-queue", 254 | "parking", 255 | "pin-project-lite", 256 | ] 257 | 258 | [[package]] 259 | name = "event-listener-strategy" 260 | version = "0.5.4" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" 263 | dependencies = [ 264 | "event-listener", 265 | "pin-project-lite", 266 | ] 267 | 268 | [[package]] 269 | name = "fastrand" 270 | version = "2.3.0" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 273 | 274 | [[package]] 275 | name = "futures" 276 | version = "0.3.31" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 279 | dependencies = [ 280 | "futures-channel", 281 | "futures-core", 282 | "futures-executor", 283 | "futures-io", 284 | "futures-sink", 285 | "futures-task", 286 | "futures-util", 287 | ] 288 | 289 | [[package]] 290 | name = "futures-channel" 291 | version = "0.3.31" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 294 | dependencies = [ 295 | "futures-core", 296 | "futures-sink", 297 | ] 298 | 299 | [[package]] 300 | name = "futures-core" 301 | version = "0.3.31" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 304 | 305 | [[package]] 306 | name = "futures-executor" 307 | version = "0.3.31" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 310 | dependencies = [ 311 | "futures-core", 312 | "futures-task", 313 | "futures-util", 314 | ] 315 | 316 | [[package]] 317 | name = "futures-io" 318 | version = "0.3.31" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 321 | 322 | [[package]] 323 | name = "futures-lite" 324 | version = "2.6.0" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" 327 | dependencies = [ 328 | "fastrand", 329 | "futures-core", 330 | "futures-io", 331 | "parking", 332 | "pin-project-lite", 333 | ] 334 | 335 | [[package]] 336 | name = "futures-macro" 337 | version = "0.3.31" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 340 | dependencies = [ 341 | "proc-macro2", 342 | "quote", 343 | "syn", 344 | ] 345 | 346 | [[package]] 347 | name = "futures-sink" 348 | version = "0.3.31" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 351 | 352 | [[package]] 353 | name = "futures-task" 354 | version = "0.3.31" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 357 | 358 | [[package]] 359 | name = "futures-util" 360 | version = "0.3.31" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 363 | dependencies = [ 364 | "futures-channel", 365 | "futures-core", 366 | "futures-io", 367 | "futures-macro", 368 | "futures-sink", 369 | "futures-task", 370 | "memchr", 371 | "pin-project-lite", 372 | "pin-utils", 373 | "slab", 374 | ] 375 | 376 | [[package]] 377 | name = "generational-box" 378 | version = "0.5.6" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "557cf2cbacd0504c6bf8c29f52f8071e0de1d9783346713dc6121d7fa1e5d0e0" 381 | dependencies = [ 382 | "parking_lot", 383 | ] 384 | 385 | [[package]] 386 | name = "getrandom" 387 | version = "0.3.2" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" 390 | dependencies = [ 391 | "cfg-if", 392 | "libc", 393 | "r-efi", 394 | "wasi 0.14.2+wasi-0.2.4", 395 | ] 396 | 397 | [[package]] 398 | name = "grid" 399 | version = "0.14.0" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "be136d9dacc2a13cc70bb6c8f902b414fb2641f8db1314637c6b7933411a8f82" 402 | 403 | [[package]] 404 | name = "hermit-abi" 405 | version = "0.4.0" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" 408 | 409 | [[package]] 410 | name = "iocraft" 411 | version = "0.7.4" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "2dc6805810bb6c554e8eeb716fded65cb709c10a202a8243f102c212f6b9eedb" 414 | dependencies = [ 415 | "any_key", 416 | "bitflags", 417 | "crossterm", 418 | "futures", 419 | "generational-box", 420 | "iocraft-macros", 421 | "taffy", 422 | "textwrap", 423 | "unicode-width 0.1.14", 424 | ] 425 | 426 | [[package]] 427 | name = "iocraft-macros" 428 | version = "0.2.3" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "d2a8b12929295d5fea225570fd69a41084e2949d56ce0c20ff22494ef13e0d56" 431 | dependencies = [ 432 | "proc-macro2", 433 | "quote", 434 | "syn", 435 | "uuid", 436 | ] 437 | 438 | [[package]] 439 | name = "libc" 440 | version = "0.2.171" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 443 | 444 | [[package]] 445 | name = "linux-raw-sys" 446 | version = "0.4.15" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 449 | 450 | [[package]] 451 | name = "lock_api" 452 | version = "0.4.12" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 455 | dependencies = [ 456 | "autocfg", 457 | "scopeguard", 458 | ] 459 | 460 | [[package]] 461 | name = "log" 462 | version = "0.4.27" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 465 | 466 | [[package]] 467 | name = "memchr" 468 | version = "2.7.4" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 471 | 472 | [[package]] 473 | name = "mio" 474 | version = "1.0.3" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 477 | dependencies = [ 478 | "libc", 479 | "log", 480 | "wasi 0.11.0+wasi-snapshot-preview1", 481 | "windows-sys 0.52.0", 482 | ] 483 | 484 | [[package]] 485 | name = "mopa" 486 | version = "0.2.2" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915" 489 | 490 | [[package]] 491 | name = "num-traits" 492 | version = "0.2.19" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 495 | dependencies = [ 496 | "autocfg", 497 | ] 498 | 499 | [[package]] 500 | name = "parking" 501 | version = "2.2.1" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" 504 | 505 | [[package]] 506 | name = "parking_lot" 507 | version = "0.12.3" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 510 | dependencies = [ 511 | "lock_api", 512 | "parking_lot_core", 513 | ] 514 | 515 | [[package]] 516 | name = "parking_lot_core" 517 | version = "0.9.10" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 520 | dependencies = [ 521 | "cfg-if", 522 | "libc", 523 | "redox_syscall", 524 | "smallvec", 525 | "windows-targets", 526 | ] 527 | 528 | [[package]] 529 | name = "pin-project-lite" 530 | version = "0.2.16" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 533 | 534 | [[package]] 535 | name = "pin-utils" 536 | version = "0.1.0" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 539 | 540 | [[package]] 541 | name = "piper" 542 | version = "0.2.4" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" 545 | dependencies = [ 546 | "atomic-waker", 547 | "fastrand", 548 | "futures-io", 549 | ] 550 | 551 | [[package]] 552 | name = "polling" 553 | version = "3.7.4" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" 556 | dependencies = [ 557 | "cfg-if", 558 | "concurrent-queue", 559 | "hermit-abi", 560 | "pin-project-lite", 561 | "rustix", 562 | "tracing", 563 | "windows-sys 0.59.0", 564 | ] 565 | 566 | [[package]] 567 | name = "proc-macro2" 568 | version = "1.0.94" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 571 | dependencies = [ 572 | "unicode-ident", 573 | ] 574 | 575 | [[package]] 576 | name = "quote" 577 | version = "1.0.40" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 580 | dependencies = [ 581 | "proc-macro2", 582 | ] 583 | 584 | [[package]] 585 | name = "r-efi" 586 | version = "5.2.0" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" 589 | 590 | [[package]] 591 | name = "redox_syscall" 592 | version = "0.5.10" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" 595 | dependencies = [ 596 | "bitflags", 597 | ] 598 | 599 | [[package]] 600 | name = "regex" 601 | version = "1.11.1" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 604 | dependencies = [ 605 | "aho-corasick", 606 | "memchr", 607 | "regex-automata", 608 | "regex-syntax", 609 | ] 610 | 611 | [[package]] 612 | name = "regex-automata" 613 | version = "0.4.9" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 616 | dependencies = [ 617 | "aho-corasick", 618 | "memchr", 619 | "regex-syntax", 620 | ] 621 | 622 | [[package]] 623 | name = "regex-syntax" 624 | version = "0.8.5" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 627 | 628 | [[package]] 629 | name = "regname" 630 | version = "0.1.0" 631 | dependencies = [ 632 | "iocraft", 633 | "regex", 634 | "smol", 635 | ] 636 | 637 | [[package]] 638 | name = "rustix" 639 | version = "0.38.44" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 642 | dependencies = [ 643 | "bitflags", 644 | "errno", 645 | "libc", 646 | "linux-raw-sys", 647 | "windows-sys 0.59.0", 648 | ] 649 | 650 | [[package]] 651 | name = "scopeguard" 652 | version = "1.2.0" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 655 | 656 | [[package]] 657 | name = "serde" 658 | version = "1.0.219" 659 | source = "registry+https://github.com/rust-lang/crates.io-index" 660 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 661 | dependencies = [ 662 | "serde_derive", 663 | ] 664 | 665 | [[package]] 666 | name = "serde_derive" 667 | version = "1.0.219" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 670 | dependencies = [ 671 | "proc-macro2", 672 | "quote", 673 | "syn", 674 | ] 675 | 676 | [[package]] 677 | name = "signal-hook" 678 | version = "0.3.17" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" 681 | dependencies = [ 682 | "libc", 683 | "signal-hook-registry", 684 | ] 685 | 686 | [[package]] 687 | name = "signal-hook-mio" 688 | version = "0.2.4" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" 691 | dependencies = [ 692 | "libc", 693 | "mio", 694 | "signal-hook", 695 | ] 696 | 697 | [[package]] 698 | name = "signal-hook-registry" 699 | version = "1.4.2" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 702 | dependencies = [ 703 | "libc", 704 | ] 705 | 706 | [[package]] 707 | name = "slab" 708 | version = "0.4.9" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 711 | dependencies = [ 712 | "autocfg", 713 | ] 714 | 715 | [[package]] 716 | name = "slotmap" 717 | version = "1.0.7" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" 720 | dependencies = [ 721 | "version_check 0.9.5", 722 | ] 723 | 724 | [[package]] 725 | name = "smallvec" 726 | version = "1.14.0" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 729 | 730 | [[package]] 731 | name = "smawk" 732 | version = "0.3.2" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" 735 | 736 | [[package]] 737 | name = "smol" 738 | version = "2.0.2" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" 741 | dependencies = [ 742 | "async-channel", 743 | "async-executor", 744 | "async-fs", 745 | "async-io", 746 | "async-lock", 747 | "async-net", 748 | "async-process", 749 | "blocking", 750 | "futures-lite", 751 | ] 752 | 753 | [[package]] 754 | name = "syn" 755 | version = "2.0.100" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 758 | dependencies = [ 759 | "proc-macro2", 760 | "quote", 761 | "unicode-ident", 762 | ] 763 | 764 | [[package]] 765 | name = "taffy" 766 | version = "0.5.2" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | checksum = "9cb893bff0f80ae17d3a57e030622a967b8dbc90e38284d9b4b1442e23873c94" 769 | dependencies = [ 770 | "arrayvec", 771 | "grid", 772 | "num-traits", 773 | "serde", 774 | "slotmap", 775 | ] 776 | 777 | [[package]] 778 | name = "textwrap" 779 | version = "0.16.2" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" 782 | dependencies = [ 783 | "smawk", 784 | "unicode-linebreak", 785 | "unicode-width 0.2.0", 786 | ] 787 | 788 | [[package]] 789 | name = "tracing" 790 | version = "0.1.41" 791 | source = "registry+https://github.com/rust-lang/crates.io-index" 792 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 793 | dependencies = [ 794 | "pin-project-lite", 795 | "tracing-core", 796 | ] 797 | 798 | [[package]] 799 | name = "tracing-core" 800 | version = "0.1.33" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 803 | 804 | [[package]] 805 | name = "unicode-ident" 806 | version = "1.0.18" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 809 | 810 | [[package]] 811 | name = "unicode-linebreak" 812 | version = "0.1.5" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" 815 | 816 | [[package]] 817 | name = "unicode-width" 818 | version = "0.1.14" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 821 | 822 | [[package]] 823 | name = "unicode-width" 824 | version = "0.2.0" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" 827 | 828 | [[package]] 829 | name = "uuid" 830 | version = "1.16.0" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" 833 | dependencies = [ 834 | "getrandom", 835 | ] 836 | 837 | [[package]] 838 | name = "version_check" 839 | version = "0.1.5" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" 842 | 843 | [[package]] 844 | name = "version_check" 845 | version = "0.9.5" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 848 | 849 | [[package]] 850 | name = "wasi" 851 | version = "0.11.0+wasi-snapshot-preview1" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 854 | 855 | [[package]] 856 | name = "wasi" 857 | version = "0.14.2+wasi-0.2.4" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 860 | dependencies = [ 861 | "wit-bindgen-rt", 862 | ] 863 | 864 | [[package]] 865 | name = "winapi" 866 | version = "0.3.9" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 869 | dependencies = [ 870 | "winapi-i686-pc-windows-gnu", 871 | "winapi-x86_64-pc-windows-gnu", 872 | ] 873 | 874 | [[package]] 875 | name = "winapi-i686-pc-windows-gnu" 876 | version = "0.4.0" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 879 | 880 | [[package]] 881 | name = "winapi-x86_64-pc-windows-gnu" 882 | version = "0.4.0" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 885 | 886 | [[package]] 887 | name = "windows-sys" 888 | version = "0.52.0" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 891 | dependencies = [ 892 | "windows-targets", 893 | ] 894 | 895 | [[package]] 896 | name = "windows-sys" 897 | version = "0.59.0" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 900 | dependencies = [ 901 | "windows-targets", 902 | ] 903 | 904 | [[package]] 905 | name = "windows-targets" 906 | version = "0.52.6" 907 | source = "registry+https://github.com/rust-lang/crates.io-index" 908 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 909 | dependencies = [ 910 | "windows_aarch64_gnullvm", 911 | "windows_aarch64_msvc", 912 | "windows_i686_gnu", 913 | "windows_i686_gnullvm", 914 | "windows_i686_msvc", 915 | "windows_x86_64_gnu", 916 | "windows_x86_64_gnullvm", 917 | "windows_x86_64_msvc", 918 | ] 919 | 920 | [[package]] 921 | name = "windows_aarch64_gnullvm" 922 | version = "0.52.6" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 925 | 926 | [[package]] 927 | name = "windows_aarch64_msvc" 928 | version = "0.52.6" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 931 | 932 | [[package]] 933 | name = "windows_i686_gnu" 934 | version = "0.52.6" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 937 | 938 | [[package]] 939 | name = "windows_i686_gnullvm" 940 | version = "0.52.6" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 943 | 944 | [[package]] 945 | name = "windows_i686_msvc" 946 | version = "0.52.6" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 949 | 950 | [[package]] 951 | name = "windows_x86_64_gnu" 952 | version = "0.52.6" 953 | source = "registry+https://github.com/rust-lang/crates.io-index" 954 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 955 | 956 | [[package]] 957 | name = "windows_x86_64_gnullvm" 958 | version = "0.52.6" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 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 = "wit-bindgen-rt" 970 | version = "0.39.0" 971 | source = "registry+https://github.com/rust-lang/crates.io-index" 972 | checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 973 | dependencies = [ 974 | "bitflags", 975 | ] 976 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "regname" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | iocraft = "0.7.4" 8 | regex = "1.11.1" 9 | smol = "2.0.2" 10 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 David Delassus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # regname 2 | 3 | Mass renamer TUI written in Rust. 4 | 5 | [![asciicast](https://asciinema.org/a/711473.svg)](https://asciinema.org/a/711473) 6 | 7 | ## :package: Installation 8 | 9 | ```bash 10 | cargo install --locked --git https://github.com/linkdd/regname 11 | ``` 12 | 13 | ## :memo: Usage 14 | 15 | ```bash 16 | regname path/to/a/folder 17 | ``` 18 | 19 | Then, use: 20 | 21 | - `TAB` to select either the "Match" input field or "Rename" input field 22 | - `CTRL+C` to cancel 23 | - `ENTER` to rename all matching files 24 | 25 | ## :page_facing_up: License 26 | 27 | This project is released under the terms of the [MIT License](./LICENSE.txt). 28 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use iocraft::prelude::*; 2 | use regex::Regex; 3 | use smol; 4 | 5 | #[derive(Clone, Copy, PartialEq)] 6 | enum FocusedField { 7 | Match, 8 | Rename, 9 | } 10 | 11 | #[derive(Default, Props)] 12 | struct AppProps { 13 | dir: std::path::PathBuf, 14 | exit_code: i32, 15 | out_buffer: String, 16 | err_buffer: String, 17 | } 18 | 19 | #[component] 20 | fn App(props: &mut AppProps, mut hooks: Hooks) -> impl Into> { 21 | let (width, height) = hooks.use_terminal_size(); 22 | let mut system = hooks.use_context_mut::(); 23 | 24 | let mut out_buffer = hooks.use_state(|| String::new()); 25 | let mut err_buffer = hooks.use_state(|| String::new()); 26 | let mut should_exit = hooks.use_state::, _>(|| None); 27 | let mut scroll_offset = hooks.use_state(|| 0); 28 | 29 | let mut eprintln = |msg: &str| { 30 | err_buffer.set(format!("{}{}\n", err_buffer.read().as_str(), msg)); 31 | }; 32 | 33 | let items = hooks.use_state(|| { 34 | let entries = match std::fs::read_dir(&props.dir) { 35 | Ok(entries) => entries.collect::>(), 36 | Err(err) => { 37 | eprintln(&format!("ERROR: Could not read directory: {}", err)); 38 | should_exit.set(Some(1)); 39 | vec![] 40 | } 41 | }; 42 | 43 | let mut items = vec![]; 44 | 45 | for entry in entries { 46 | if let Err(err) = entry { 47 | eprintln(&format!("ERROR: Could not read entry: {}", err)); 48 | should_exit.set(Some(1)); 49 | continue; 50 | } 51 | 52 | let entry = entry.unwrap(); 53 | 54 | let metadata = entry.metadata(); 55 | if let Err(err) = metadata { 56 | eprintln(&format!("ERROR: Could not read entry metadata: {}", err)); 57 | should_exit.set(Some(1)); 58 | continue; 59 | } 60 | let metadata = metadata.unwrap(); 61 | 62 | let is_file = metadata.is_file(); 63 | let path = entry.path(); 64 | 65 | let filename = path.file_name(); 66 | if let Some(filename) = filename { 67 | let filename = filename.to_string_lossy().to_string(); 68 | items.push((is_file, filename)); 69 | } 70 | } 71 | 72 | items.sort_by(|a, b| { 73 | let a = a.1.to_lowercase(); 74 | let b = b.1.to_lowercase(); 75 | a.cmp(&b) 76 | }); 77 | 78 | items 79 | }); 80 | 81 | let mut match_field = hooks.use_state(|| "(.*)".to_string()); 82 | let mut rename_field = hooks.use_state(|| "$1".to_string()); 83 | let mut focused_field = hooks.use_state(|| FocusedField::Match); 84 | 85 | let item_count = items.read().len() as i32; 86 | 87 | let match_re = Regex::new(&match_field.read().to_string()) 88 | .unwrap_or_else(|_| Regex::new("^$").unwrap()); 89 | 90 | let item_elts = items 91 | .read() 92 | .iter() 93 | .map(|(is_file, filename)| { 94 | let label = if *is_file { 95 | format!("📄 {}", filename) 96 | } else { 97 | format!("📁 {}/", filename) 98 | }; 99 | 100 | element! { 101 | Text( 102 | color: if match_re.is_match(filename) { 103 | Color::Green 104 | } else { 105 | Color::Grey 106 | }, 107 | content: label, 108 | ) 109 | } 110 | }) 111 | .collect::>(); 112 | 113 | let renamed_elts = items 114 | .read() 115 | .iter() 116 | .map(|(is_file, filename)| { 117 | let pattern = rename_field.to_string(); 118 | 119 | if match_re.is_match(filename) && !pattern.is_empty() { 120 | let renamed = match_re.replace_all(filename, &pattern); 121 | let label = if *is_file { 122 | format!("📄 {}", renamed) 123 | } else { 124 | format!("📁 {}/", renamed) 125 | }; 126 | 127 | element! { 128 | Text( 129 | color: Color::Red, 130 | content: label, 131 | ) 132 | } 133 | } 134 | else { 135 | let label = if *is_file { 136 | format!("📄 {}", filename) 137 | } else { 138 | format!("📁 {}/", filename) 139 | }; 140 | 141 | element! { 142 | Text( 143 | color: Color::Grey, 144 | content: label, 145 | ) 146 | } 147 | } 148 | }) 149 | .collect::>(); 150 | 151 | hooks.use_terminal_events({ 152 | let dir = props.dir.clone(); 153 | 154 | move |event| { 155 | let mut oprintln = |msg: &str| { 156 | out_buffer.set(format!("{}{}\n", out_buffer.read().as_str(), msg)); 157 | }; 158 | let mut eprintln = |msg: &str| { 159 | err_buffer.set(format!("{}{}\n", err_buffer.read().as_str(), msg)); 160 | }; 161 | 162 | let match_re = Regex::new(&match_field.read().to_string()) 163 | .unwrap_or_else(|_| Regex::new("^$").unwrap()); 164 | 165 | match event { 166 | TerminalEvent::Key(KeyEvent { code, kind, .. }) 167 | if { kind != KeyEventKind::Release } => 168 | { 169 | match code { 170 | KeyCode::Up => scroll_offset.set((scroll_offset.get() - 1).max(0)), 171 | KeyCode::Down => { 172 | scroll_offset.set((scroll_offset.get() + 1).min(item_count - 1)) 173 | } 174 | KeyCode::Tab => match focused_field.get() { 175 | FocusedField::Match => { 176 | focused_field.set(FocusedField::Rename); 177 | } 178 | FocusedField::Rename => { 179 | focused_field.set(FocusedField::Match); 180 | } 181 | }, 182 | KeyCode::Enter => { 183 | let pattern = rename_field.to_string(); 184 | 185 | for (_, filename) in items.read().iter() { 186 | if match_re.is_match(filename) && !pattern.is_empty() { 187 | let renamed = match_re.replace_all( 188 | filename, 189 | &pattern, 190 | ).to_string(); 191 | 192 | oprintln(&format!("{} -> {}", filename, renamed)); 193 | 194 | let old_path = dir.join(filename); 195 | let new_path = dir.join(&renamed); 196 | 197 | match std::fs::rename(old_path, new_path) { 198 | Ok(_) => {} 199 | Err(err) => { 200 | eprintln(&format!( 201 | "ERROR: Could not rename file: {}", 202 | err, 203 | )); 204 | should_exit.set(Some(1)); 205 | break; 206 | } 207 | } 208 | } 209 | } 210 | 211 | should_exit.set(Some(0)); 212 | } 213 | _ => {} 214 | } 215 | } 216 | _ => {} 217 | } 218 | } 219 | }); 220 | 221 | if should_exit.get().is_some() { 222 | props.exit_code = should_exit.get().unwrap(); 223 | props.out_buffer = out_buffer.read().to_string(); 224 | props.err_buffer = err_buffer.read().to_string(); 225 | system.exit(); 226 | } 227 | 228 | element! { 229 | View( 230 | width, 231 | height, 232 | flex_direction: FlexDirection::Column, 233 | align_items: AlignItems::Stretch, 234 | ) { 235 | View( 236 | width: Size::Percent(100f32), 237 | height: 1, 238 | flex_direction: FlexDirection::Row, 239 | align_items: AlignItems::Stretch, 240 | ) { 241 | View( 242 | flex_shrink: 1f32, 243 | ) { 244 | Text(content: "Directory: ") 245 | } 246 | View( 247 | flex_grow: 1f32, 248 | background_color: Color::Black, 249 | padding_left: 1, 250 | padding_right: 1, 251 | ) { 252 | TextInput(value: format!("{}", props.dir.display())) 253 | } 254 | } 255 | View( 256 | width: Size::Percent(100f32), 257 | height: height - 2, 258 | flex_direction: FlexDirection::Row, 259 | align_items: AlignItems::Stretch, 260 | justify_content: JustifyContent::Stretch, 261 | ) { 262 | View( 263 | flex_grow: 1f32, 264 | border_style: BorderStyle::Double, 265 | border_color: Color::Blue, 266 | padding_left: 1, 267 | padding_right: 1, 268 | overflow: Overflow::Scroll, 269 | ) { 270 | View( 271 | width: Size::Percent(100f32), 272 | height: item_count, 273 | ) { 274 | View( 275 | width: Size::Percent(100f32), 276 | position: Position::Absolute, 277 | top: -scroll_offset.get(), 278 | flex_direction: FlexDirection::Column, 279 | align_items: AlignItems::Start, 280 | ) { 281 | #(item_elts) 282 | } 283 | } 284 | } 285 | View( 286 | flex_grow: 1f32, 287 | border_style: BorderStyle::Double, 288 | border_color: Color::Blue, 289 | padding_left: 1, 290 | padding_right: 1, 291 | overflow: Overflow::Scroll, 292 | ) { 293 | View( 294 | width: Size::Percent(100f32), 295 | height: item_count, 296 | ) { 297 | View( 298 | width: Size::Percent(100f32), 299 | position: Position::Absolute, 300 | top: -scroll_offset.get(), 301 | flex_direction: FlexDirection::Column, 302 | align_items: AlignItems::Start, 303 | ) { 304 | #(renamed_elts) 305 | } 306 | } 307 | } 308 | } 309 | View( 310 | width: Size::Percent(100f32), 311 | height: 1, 312 | flex_direction: FlexDirection::Row, 313 | align_items: AlignItems::Stretch, 314 | gap: 1, 315 | ) { 316 | View( 317 | flex_direction: FlexDirection::Row, 318 | align_items: AlignItems::Stretch, 319 | flex_grow: 1f32, 320 | gap: 1, 321 | padding_left: 1, 322 | padding_right: 1, 323 | ) { 324 | View( 325 | flex_shrink: 1f32, 326 | ) { 327 | Text(content: "Match:") 328 | } 329 | View( 330 | flex_grow: 1f32, 331 | background_color: if focused_field.get() == FocusedField::Match { 332 | Color::DarkGrey 333 | } else { 334 | Color::Black 335 | }, 336 | padding_left: 1, 337 | padding_right: 1, 338 | ) { 339 | TextInput( 340 | has_focus: focused_field.get() == FocusedField::Match, 341 | value: match_field.to_string(), 342 | on_change: move |val| match_field.set(val), 343 | ) 344 | } 345 | } 346 | View( 347 | flex_direction: FlexDirection::Row, 348 | align_items: AlignItems::Stretch, 349 | flex_grow: 1f32, 350 | gap: 1, 351 | padding_left: 1, 352 | padding_right: 1, 353 | ) { 354 | View( 355 | flex_shrink: 1f32, 356 | ) { 357 | Text(content: "Rename:") 358 | } 359 | View( 360 | flex_grow: 1f32, 361 | background_color: if focused_field.get() == FocusedField::Rename { 362 | Color::DarkGrey 363 | } else { 364 | Color::Black 365 | }, 366 | padding_left: 1, 367 | padding_right: 1, 368 | ) { 369 | TextInput( 370 | has_focus: focused_field.get() == FocusedField::Rename, 371 | value: rename_field.to_string(), 372 | on_change: move |val| rename_field.set(val), 373 | ) 374 | } 375 | } 376 | } 377 | } 378 | } 379 | } 380 | 381 | fn main() { 382 | let args: Vec = std::env::args().collect(); 383 | let dir = args 384 | .get(1) 385 | .map(|s| match std::path::PathBuf::from(s).canonicalize() { 386 | Ok(path) => path, 387 | Err(err) => { 388 | eprintln!("ERROR: {}", err); 389 | std::process::exit(1); 390 | }, 391 | }) 392 | .or_else(|| match std::env::current_dir() { 393 | Ok(path) => Some(path), 394 | Err(err) => { 395 | eprintln!("ERROR: {}", err); 396 | std::process::exit(1); 397 | }, 398 | }) 399 | .unwrap(); 400 | 401 | let mut elt = element! {App(dir: dir)}; 402 | smol::block_on(elt.fullscreen()).unwrap(); 403 | print!("{}", elt.props.out_buffer); 404 | eprint!("{}", elt.props.err_buffer); 405 | std::process::exit(elt.props.exit_code); 406 | } 407 | --------------------------------------------------------------------------------