├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── files ├── screenshot.png └── screenshot_big_font.png └── src ├── arg_resolver.rs ├── fmt.rs ├── main.rs └── timer.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 = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.8.9" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" 10 | dependencies = [ 11 | "cfg-if", 12 | "once_cell", 13 | "version_check", 14 | "zerocopy", 15 | ] 16 | 17 | [[package]] 18 | name = "aho-corasick" 19 | version = "1.1.2" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 22 | dependencies = [ 23 | "memchr", 24 | ] 25 | 26 | [[package]] 27 | name = "allocator-api2" 28 | version = "0.2.16" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" 31 | 32 | [[package]] 33 | name = "android-tzdata" 34 | version = "0.1.1" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 37 | 38 | [[package]] 39 | name = "android_system_properties" 40 | version = "0.1.5" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 43 | dependencies = [ 44 | "libc", 45 | ] 46 | 47 | [[package]] 48 | name = "autocfg" 49 | version = "1.1.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 52 | 53 | [[package]] 54 | name = "bitflags" 55 | version = "1.3.2" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 58 | 59 | [[package]] 60 | name = "bitflags" 61 | version = "2.4.2" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" 64 | 65 | [[package]] 66 | name = "bumpalo" 67 | version = "3.15.3" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" 70 | 71 | [[package]] 72 | name = "cassowary" 73 | version = "0.3.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" 76 | 77 | [[package]] 78 | name = "castaway" 79 | version = "0.2.2" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" 82 | dependencies = [ 83 | "rustversion", 84 | ] 85 | 86 | [[package]] 87 | name = "cc" 88 | version = "1.0.87" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "3286b845d0fccbdd15af433f61c5970e711987036cb468f437ff6badd70f4e24" 91 | 92 | [[package]] 93 | name = "cfg-if" 94 | version = "1.0.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 97 | 98 | [[package]] 99 | name = "chrono" 100 | version = "0.4.34" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" 103 | dependencies = [ 104 | "android-tzdata", 105 | "iana-time-zone", 106 | "js-sys", 107 | "num-traits", 108 | "wasm-bindgen", 109 | "windows-targets 0.52.3", 110 | ] 111 | 112 | [[package]] 113 | name = "compact_str" 114 | version = "0.7.1" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" 117 | dependencies = [ 118 | "castaway", 119 | "cfg-if", 120 | "itoa", 121 | "ryu", 122 | "static_assertions", 123 | ] 124 | 125 | [[package]] 126 | name = "core-foundation-sys" 127 | version = "0.8.6" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 130 | 131 | [[package]] 132 | name = "crossterm" 133 | version = "0.27.0" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" 136 | dependencies = [ 137 | "bitflags 2.4.2", 138 | "crossterm_winapi", 139 | "libc", 140 | "mio", 141 | "parking_lot", 142 | "signal-hook", 143 | "signal-hook-mio", 144 | "winapi", 145 | ] 146 | 147 | [[package]] 148 | name = "crossterm_winapi" 149 | version = "0.9.1" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 152 | dependencies = [ 153 | "winapi", 154 | ] 155 | 156 | [[package]] 157 | name = "either" 158 | version = "1.10.0" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" 161 | 162 | [[package]] 163 | name = "hashbrown" 164 | version = "0.14.3" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 167 | dependencies = [ 168 | "ahash", 169 | "allocator-api2", 170 | ] 171 | 172 | [[package]] 173 | name = "heck" 174 | version = "0.4.1" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 177 | 178 | [[package]] 179 | name = "iana-time-zone" 180 | version = "0.1.60" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" 183 | dependencies = [ 184 | "android_system_properties", 185 | "core-foundation-sys", 186 | "iana-time-zone-haiku", 187 | "js-sys", 188 | "wasm-bindgen", 189 | "windows-core", 190 | ] 191 | 192 | [[package]] 193 | name = "iana-time-zone-haiku" 194 | version = "0.1.2" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 197 | dependencies = [ 198 | "cc", 199 | ] 200 | 201 | [[package]] 202 | name = "indoc" 203 | version = "2.0.4" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" 206 | 207 | [[package]] 208 | name = "itertools" 209 | version = "0.12.1" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 212 | dependencies = [ 213 | "either", 214 | ] 215 | 216 | [[package]] 217 | name = "itoa" 218 | version = "1.0.10" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" 221 | 222 | [[package]] 223 | name = "js-sys" 224 | version = "0.3.68" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" 227 | dependencies = [ 228 | "wasm-bindgen", 229 | ] 230 | 231 | [[package]] 232 | name = "libc" 233 | version = "0.2.153" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 236 | 237 | [[package]] 238 | name = "lock_api" 239 | version = "0.4.11" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" 242 | dependencies = [ 243 | "autocfg", 244 | "scopeguard", 245 | ] 246 | 247 | [[package]] 248 | name = "log" 249 | version = "0.4.20" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 252 | 253 | [[package]] 254 | name = "lru" 255 | version = "0.12.2" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "db2c024b41519440580066ba82aab04092b333e09066a5eb86c7c4890df31f22" 258 | dependencies = [ 259 | "hashbrown", 260 | ] 261 | 262 | [[package]] 263 | name = "memchr" 264 | version = "2.7.1" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 267 | 268 | [[package]] 269 | name = "mio" 270 | version = "0.8.10" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" 273 | dependencies = [ 274 | "libc", 275 | "log", 276 | "wasi", 277 | "windows-sys", 278 | ] 279 | 280 | [[package]] 281 | name = "num-traits" 282 | version = "0.2.18" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" 285 | dependencies = [ 286 | "autocfg", 287 | ] 288 | 289 | [[package]] 290 | name = "once_cell" 291 | version = "1.19.0" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 294 | 295 | [[package]] 296 | name = "parking_lot" 297 | version = "0.12.1" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 300 | dependencies = [ 301 | "lock_api", 302 | "parking_lot_core", 303 | ] 304 | 305 | [[package]] 306 | name = "parking_lot_core" 307 | version = "0.9.9" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" 310 | dependencies = [ 311 | "cfg-if", 312 | "libc", 313 | "redox_syscall", 314 | "smallvec", 315 | "windows-targets 0.48.5", 316 | ] 317 | 318 | [[package]] 319 | name = "paste" 320 | version = "1.0.14" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" 323 | 324 | [[package]] 325 | name = "proc-macro2" 326 | version = "1.0.78" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" 329 | dependencies = [ 330 | "unicode-ident", 331 | ] 332 | 333 | [[package]] 334 | name = "quote" 335 | version = "1.0.35" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 338 | dependencies = [ 339 | "proc-macro2", 340 | ] 341 | 342 | [[package]] 343 | name = "ratatui" 344 | version = "0.26.1" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8" 347 | dependencies = [ 348 | "bitflags 2.4.2", 349 | "cassowary", 350 | "compact_str", 351 | "crossterm", 352 | "indoc", 353 | "itertools", 354 | "lru", 355 | "paste", 356 | "stability", 357 | "strum", 358 | "unicode-segmentation", 359 | "unicode-width", 360 | ] 361 | 362 | [[package]] 363 | name = "redox_syscall" 364 | version = "0.4.1" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 367 | dependencies = [ 368 | "bitflags 1.3.2", 369 | ] 370 | 371 | [[package]] 372 | name = "regex" 373 | version = "1.10.3" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" 376 | dependencies = [ 377 | "aho-corasick", 378 | "memchr", 379 | "regex-automata", 380 | "regex-syntax", 381 | ] 382 | 383 | [[package]] 384 | name = "regex-automata" 385 | version = "0.4.5" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" 388 | dependencies = [ 389 | "aho-corasick", 390 | "memchr", 391 | "regex-syntax", 392 | ] 393 | 394 | [[package]] 395 | name = "regex-syntax" 396 | version = "0.8.2" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 399 | 400 | [[package]] 401 | name = "rustversion" 402 | version = "1.0.14" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" 405 | 406 | [[package]] 407 | name = "ryu" 408 | version = "1.0.17" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" 411 | 412 | [[package]] 413 | name = "scopeguard" 414 | version = "1.2.0" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 417 | 418 | [[package]] 419 | name = "signal-hook" 420 | version = "0.3.17" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" 423 | dependencies = [ 424 | "libc", 425 | "signal-hook-registry", 426 | ] 427 | 428 | [[package]] 429 | name = "signal-hook-mio" 430 | version = "0.2.3" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" 433 | dependencies = [ 434 | "libc", 435 | "mio", 436 | "signal-hook", 437 | ] 438 | 439 | [[package]] 440 | name = "signal-hook-registry" 441 | version = "1.4.1" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 444 | dependencies = [ 445 | "libc", 446 | ] 447 | 448 | [[package]] 449 | name = "smallvec" 450 | version = "1.13.1" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" 453 | 454 | [[package]] 455 | name = "stability" 456 | version = "0.1.1" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce" 459 | dependencies = [ 460 | "quote", 461 | "syn 1.0.109", 462 | ] 463 | 464 | [[package]] 465 | name = "static_assertions" 466 | version = "1.1.0" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 469 | 470 | [[package]] 471 | name = "strum" 472 | version = "0.26.1" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" 475 | dependencies = [ 476 | "strum_macros", 477 | ] 478 | 479 | [[package]] 480 | name = "strum_macros" 481 | version = "0.26.1" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" 484 | dependencies = [ 485 | "heck", 486 | "proc-macro2", 487 | "quote", 488 | "rustversion", 489 | "syn 2.0.50", 490 | ] 491 | 492 | [[package]] 493 | name = "syn" 494 | version = "1.0.109" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 497 | dependencies = [ 498 | "proc-macro2", 499 | "quote", 500 | "unicode-ident", 501 | ] 502 | 503 | [[package]] 504 | name = "syn" 505 | version = "2.0.50" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" 508 | dependencies = [ 509 | "proc-macro2", 510 | "quote", 511 | "unicode-ident", 512 | ] 513 | 514 | [[package]] 515 | name = "timer-tui" 516 | version = "0.1.0" 517 | dependencies = [ 518 | "chrono", 519 | "crossterm", 520 | "ratatui", 521 | "regex", 522 | ] 523 | 524 | [[package]] 525 | name = "unicode-ident" 526 | version = "1.0.12" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 529 | 530 | [[package]] 531 | name = "unicode-segmentation" 532 | version = "1.11.0" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" 535 | 536 | [[package]] 537 | name = "unicode-width" 538 | version = "0.1.11" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 541 | 542 | [[package]] 543 | name = "version_check" 544 | version = "0.9.4" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 547 | 548 | [[package]] 549 | name = "wasi" 550 | version = "0.11.0+wasi-snapshot-preview1" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 553 | 554 | [[package]] 555 | name = "wasm-bindgen" 556 | version = "0.2.91" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" 559 | dependencies = [ 560 | "cfg-if", 561 | "wasm-bindgen-macro", 562 | ] 563 | 564 | [[package]] 565 | name = "wasm-bindgen-backend" 566 | version = "0.2.91" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" 569 | dependencies = [ 570 | "bumpalo", 571 | "log", 572 | "once_cell", 573 | "proc-macro2", 574 | "quote", 575 | "syn 2.0.50", 576 | "wasm-bindgen-shared", 577 | ] 578 | 579 | [[package]] 580 | name = "wasm-bindgen-macro" 581 | version = "0.2.91" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" 584 | dependencies = [ 585 | "quote", 586 | "wasm-bindgen-macro-support", 587 | ] 588 | 589 | [[package]] 590 | name = "wasm-bindgen-macro-support" 591 | version = "0.2.91" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" 594 | dependencies = [ 595 | "proc-macro2", 596 | "quote", 597 | "syn 2.0.50", 598 | "wasm-bindgen-backend", 599 | "wasm-bindgen-shared", 600 | ] 601 | 602 | [[package]] 603 | name = "wasm-bindgen-shared" 604 | version = "0.2.91" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" 607 | 608 | [[package]] 609 | name = "winapi" 610 | version = "0.3.9" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 613 | dependencies = [ 614 | "winapi-i686-pc-windows-gnu", 615 | "winapi-x86_64-pc-windows-gnu", 616 | ] 617 | 618 | [[package]] 619 | name = "winapi-i686-pc-windows-gnu" 620 | version = "0.4.0" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 623 | 624 | [[package]] 625 | name = "winapi-x86_64-pc-windows-gnu" 626 | version = "0.4.0" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 629 | 630 | [[package]] 631 | name = "windows-core" 632 | version = "0.52.0" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 635 | dependencies = [ 636 | "windows-targets 0.52.3", 637 | ] 638 | 639 | [[package]] 640 | name = "windows-sys" 641 | version = "0.48.0" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 644 | dependencies = [ 645 | "windows-targets 0.48.5", 646 | ] 647 | 648 | [[package]] 649 | name = "windows-targets" 650 | version = "0.48.5" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 653 | dependencies = [ 654 | "windows_aarch64_gnullvm 0.48.5", 655 | "windows_aarch64_msvc 0.48.5", 656 | "windows_i686_gnu 0.48.5", 657 | "windows_i686_msvc 0.48.5", 658 | "windows_x86_64_gnu 0.48.5", 659 | "windows_x86_64_gnullvm 0.48.5", 660 | "windows_x86_64_msvc 0.48.5", 661 | ] 662 | 663 | [[package]] 664 | name = "windows-targets" 665 | version = "0.52.3" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" 668 | dependencies = [ 669 | "windows_aarch64_gnullvm 0.52.3", 670 | "windows_aarch64_msvc 0.52.3", 671 | "windows_i686_gnu 0.52.3", 672 | "windows_i686_msvc 0.52.3", 673 | "windows_x86_64_gnu 0.52.3", 674 | "windows_x86_64_gnullvm 0.52.3", 675 | "windows_x86_64_msvc 0.52.3", 676 | ] 677 | 678 | [[package]] 679 | name = "windows_aarch64_gnullvm" 680 | version = "0.48.5" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 683 | 684 | [[package]] 685 | name = "windows_aarch64_gnullvm" 686 | version = "0.52.3" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" 689 | 690 | [[package]] 691 | name = "windows_aarch64_msvc" 692 | version = "0.48.5" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 695 | 696 | [[package]] 697 | name = "windows_aarch64_msvc" 698 | version = "0.52.3" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" 701 | 702 | [[package]] 703 | name = "windows_i686_gnu" 704 | version = "0.48.5" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 707 | 708 | [[package]] 709 | name = "windows_i686_gnu" 710 | version = "0.52.3" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" 713 | 714 | [[package]] 715 | name = "windows_i686_msvc" 716 | version = "0.48.5" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 719 | 720 | [[package]] 721 | name = "windows_i686_msvc" 722 | version = "0.52.3" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" 725 | 726 | [[package]] 727 | name = "windows_x86_64_gnu" 728 | version = "0.48.5" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 731 | 732 | [[package]] 733 | name = "windows_x86_64_gnu" 734 | version = "0.52.3" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" 737 | 738 | [[package]] 739 | name = "windows_x86_64_gnullvm" 740 | version = "0.48.5" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 743 | 744 | [[package]] 745 | name = "windows_x86_64_gnullvm" 746 | version = "0.52.3" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" 749 | 750 | [[package]] 751 | name = "windows_x86_64_msvc" 752 | version = "0.48.5" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 755 | 756 | [[package]] 757 | name = "windows_x86_64_msvc" 758 | version = "0.52.3" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" 761 | 762 | [[package]] 763 | name = "zerocopy" 764 | version = "0.7.32" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" 767 | dependencies = [ 768 | "zerocopy-derive", 769 | ] 770 | 771 | [[package]] 772 | name = "zerocopy-derive" 773 | version = "0.7.32" 774 | source = "registry+https://github.com/rust-lang/crates.io-index" 775 | checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" 776 | dependencies = [ 777 | "proc-macro2", 778 | "quote", 779 | "syn 2.0.50", 780 | ] 781 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "timer-tui" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | chrono = "0.4.34" 10 | crossterm = "0.27.0" 11 | ratatui = "0.26.1" 12 | regex = "1.10.3" 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Timer TUI 2 | 3 | ## about 4 | It's a timer 5 | ![Screenshot of tui](/files/screenshot_big_font.png "Screenshot") 6 | 7 | Write in rust with [Ratatui](https://github.com/ratatui-org/ratatui) 8 | 9 | ## install 10 | ``` 11 | git clone https://github.com/maraloon/timer-tui.git 12 | cd timer-tui 13 | cargo install --path=. 14 | ``` 15 | 16 | ## usage 17 | ``` 18 | timer-tui 5 19 | timer-tui 5s 20 | timer-tui 5m 21 | timer-tui 5h 22 | ``` 23 | 24 | ## Keys 25 | `q` - Quite 26 | 27 | `p` - Pause 28 | 29 | ## tweaking 30 | add alias for sh so u can use `t tea` or `t 5m` 31 | ```bash 32 | t() { 33 | local template=$1 34 | local message=$2 35 | 36 | if [ $template = "tea" ]; then 37 | timer-tui 10m 38 | message="Tea" 39 | elif [ $template = "bath" ]; then 40 | timer-tui 6m 41 | message="Bath" 42 | else 43 | timer-tui $1 44 | if [ -z $message ]; then 45 | message="It's time to stop!" 46 | fi 47 | fi 48 | 49 | notify-send "Timer" $message 50 | } 51 | ``` 52 | 53 | ## credentials 54 | 55 | Inspired by [timer-bin](https://github.com/caarlos0/timer) 56 | -------------------------------------------------------------------------------- /files/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maraloon/timer-tui/827320c461d467997738b0e253ca3ee3d531cb44/files/screenshot.png -------------------------------------------------------------------------------- /files/screenshot_big_font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maraloon/timer-tui/827320c461d467997738b0e253ca3ee3d531cb44/files/screenshot_big_font.png -------------------------------------------------------------------------------- /src/arg_resolver.rs: -------------------------------------------------------------------------------- 1 | use std::i64; 2 | 3 | use regex::{self, Regex}; 4 | 5 | // pub fn parse_time_argument(time_string: String) -> i64 { 6 | pub fn parse_time_argument(time_string: String) -> Result { 7 | let re = Regex::new(r"(\d+)([^0-9]+)").unwrap(); 8 | let error: &str = "time argument must be NUMBER[s|m|h] format"; 9 | 10 | if let Some(captures) = re.captures(&time_string) { 11 | let value: i64 = captures[1].parse().unwrap(); 12 | match &captures[2] { 13 | "s" => Ok(value * 1000), 14 | "m" => Ok(value * 1000 * 60), 15 | "h" => Ok(value * 1000 * 60 * 60), 16 | // _ => panic!("{}", error), 17 | _ => Err(error.to_string()), 18 | } 19 | } else { 20 | let result: Result = time_string.parse(); 21 | match result { 22 | Ok(parsed_number) => Ok(parsed_number * 1000), 23 | Err(_) => { 24 | Err(error.to_string()) 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/fmt.rs: -------------------------------------------------------------------------------- 1 | use chrono::{Duration, Timelike}; 2 | 3 | use crate::timer::Timer; 4 | 5 | pub fn remain_time_string(timer: &mut Timer) -> String { 6 | // when we start timer at 15s, achualy it's 14,99999 right after starting 7 | // and it's look like a bug, when we show 14, not 15 8 | // so i add 1 second 9 | // to fake reality and don't fear users with horrible truth 10 | let remain_ms_fixuped = timer.remain_ms + 1000; 11 | 12 | let remain = Duration::milliseconds(remain_ms_fixuped); 13 | 14 | let hours = remain.num_hours(); 15 | let minutes = remain.num_minutes() % 60; 16 | let seconds = remain.num_seconds() % 60; 17 | 18 | if hours == 0 { 19 | return format!("{:02}:{:02}", minutes, seconds); 20 | } 21 | format!("{:02}:{:02}:{:02}", hours, minutes, seconds) 22 | } 23 | 24 | pub fn finish_time_string(timer: &mut Timer) -> String { 25 | let hour = timer.finish_at.hour(); 26 | let minute = timer.finish_at.minute(); 27 | let second = timer.finish_at.second(); 28 | format!("{:02}:{:02}:{:02}", hour, minute, second) 29 | } 30 | 31 | pub fn ratio(timer: &mut Timer) -> f64 { 32 | timer.passed_ms as f64 / timer.full_ms as f64 33 | } 34 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod arg_resolver; 2 | mod fmt; 3 | mod timer; 4 | 5 | use crossterm::{ 6 | event::{self, KeyCode, KeyEventKind}, 7 | terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, 8 | ExecutableCommand, 9 | }; 10 | use ratatui::{prelude::*, widgets::*}; 11 | use ratatui::{ 12 | prelude::{CrosstermBackend, Terminal}, 13 | widgets::Paragraph, 14 | }; 15 | use std::io::{stdout, Result}; 16 | use timer::Timer; 17 | 18 | fn main() -> Result<()> { 19 | let first_arg = std::env::args().nth(1).expect("no pattern given"); 20 | 21 | let duration_ms = match arg_resolver::parse_time_argument(first_arg) { 22 | Ok(d) => d, 23 | Err(err) => { 24 | eprint!("Error: {}", err); 25 | std::process::exit(1); 26 | } 27 | }; 28 | 29 | stdout().execute(EnterAlternateScreen)?; 30 | enable_raw_mode()?; 31 | let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?; 32 | terminal.clear()?; 33 | 34 | let mut timer = Timer::new(duration_ms); 35 | 36 | loop { 37 | timer.tick(); 38 | if timer.remain_ms < 0 { 39 | break; 40 | } 41 | terminal.draw(|frame| { 42 | let layout = Layout::default() 43 | .direction(Direction::Vertical) 44 | .constraints(vec![ 45 | Constraint::Percentage(48), 46 | Constraint::Max(1), 47 | Constraint::Max(1), 48 | ]) 49 | .split(frame.size()); 50 | 51 | let text = format!( 52 | "{} - 󰂚 {}", 53 | fmt::remain_time_string(&mut timer), 54 | fmt::finish_time_string(&mut timer) 55 | ); 56 | 57 | let text_color = match timer.paused { 58 | true => Color::Rgb(44, 56, 54), 59 | false => Color::Rgb(255, 0, 0), 60 | }; 61 | let progress_color = match timer.paused { 62 | true => Color::Rgb(44, 56, 54), 63 | false => Color::Rgb(0, 208, 152) 64 | }; 65 | 66 | frame.render_widget( 67 | Paragraph::new(text) 68 | .block( 69 | Block::default() 70 | .borders(Borders::NONE) 71 | .padding(Padding::new(1, 1, 0, 0)), 72 | ) 73 | .style(Style::default().fg(text_color)), 74 | layout[1], 75 | ); 76 | 77 | frame.render_widget( 78 | Gauge::default() 79 | .label("") 80 | .use_unicode(true) 81 | .block( 82 | Block::default() 83 | .borders(Borders::NONE) 84 | .padding(Padding::new(1, 1, 0, 0)), 85 | ) 86 | .gauge_style( 87 | Style::default() 88 | .fg(progress_color) 89 | .bg(Color::Black), 90 | ) 91 | .ratio(fmt::ratio(&mut timer)), 92 | layout[2], 93 | ); 94 | })?; 95 | 96 | if event::poll(std::time::Duration::from_millis(10))? { 97 | if let event::Event::Key(key) = event::read()? { 98 | if key.kind == KeyEventKind::Press { 99 | match key.code { 100 | KeyCode::Char('p') => timer.toggle_pause(), 101 | KeyCode::Char('q') => break, 102 | _ => todo!(), 103 | } 104 | } 105 | } 106 | } 107 | } 108 | 109 | stdout().execute(LeaveAlternateScreen)?; 110 | disable_raw_mode()?; 111 | Ok(()) 112 | } 113 | -------------------------------------------------------------------------------- /src/timer.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Duration, Local}; 2 | 3 | pub struct Timer { 4 | pub passed_ms: i64, 5 | pub remain_ms: i64, 6 | pub full_ms: i64, 7 | pub finish_at: DateTime, 8 | pub paused: bool, 9 | paused_at: DateTime, // TODO: Option 10 | } 11 | 12 | impl Timer { 13 | pub fn new(timer_duration: i64) -> Timer { 14 | let now = Local::now(); 15 | let finish_at = now + Duration::milliseconds(timer_duration); 16 | let full_ms = finish_at.timestamp_millis() - now.timestamp_millis(); 17 | let passed_ms = 0; 18 | let remain_ms = full_ms - passed_ms; 19 | Timer { 20 | passed_ms, 21 | remain_ms, 22 | full_ms, 23 | finish_at, 24 | paused: false, 25 | paused_at: now, 26 | } 27 | } 28 | 29 | pub fn tick(&mut self) { 30 | if self.paused { 31 | self.finish_at = Local::now() + Duration::milliseconds(self.remain_ms); 32 | } else { 33 | let now = Local::now(); 34 | self.remain_ms = self.finish_at.timestamp_millis() - now.timestamp_millis(); 35 | self.passed_ms = self.full_ms - self.remain_ms; 36 | } 37 | } 38 | 39 | pub fn toggle_pause(&mut self) { 40 | self.paused = !self.paused; 41 | if self.paused { 42 | self.paused_at = Local::now() 43 | } 44 | } 45 | } 46 | --------------------------------------------------------------------------------