generated from PlexSheep/rs-base
initial implement clock-tui
This commit is contained in:
commit
a08b687a87
12 changed files with 1403 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
633
Cargo.lock
generated
Normal file
633
Cargo.lock
generated
Normal file
|
@ -0,0 +1,633 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.6.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cassowary"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa1878c18b5b01b9978d5f130fe366d434022004d12fb87c182e8459b427c4a3"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"parse-zoneinfo",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab8b79fe3946ceb4a0b1c080b4018992b8d27e9ff363644c1c9b6387c854614d"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clock-tui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"clap",
|
||||
"crossterm 0.24.0",
|
||||
"regex 1.6.0",
|
||||
"tui",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.23.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2102ea4f781910f8a5b98dd061f4c2023f479ce7bb1236330099ceb5a93cf17"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab9f7409c70a38a56216480fba371ee460207dd8926ccf5b4160591759559170"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse-zoneinfo"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4ee19a3656dadae35a33467f9714f1228dd34766dbe49e10e656b5296867aea"
|
||||
dependencies = [
|
||||
"regex 0.2.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384"
|
||||
dependencies = [
|
||||
"aho-corasick 0.6.10",
|
||||
"memchr",
|
||||
"regex-syntax 0.5.6",
|
||||
"thread_local",
|
||||
"utf8-ranges",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
dependencies = [
|
||||
"aho-corasick 0.7.18",
|
||||
"memchr",
|
||||
"regex-syntax 0.6.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
|
||||
dependencies = [
|
||||
"ucd-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tui"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96fe69244ec2af261bced1d9046a6fee6c8c2a6b0228e59e5ba39bc8ba4ed729"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cassowary",
|
||||
"crossterm 0.23.2",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ucd-util"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bfcbf611b122f2c10eb1bb6172fbc4c2e25df9970330e4d75ce2b5201c9bfc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "clock-tui"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tui = "0.18.0"
|
||||
crossterm = "0.24"
|
||||
chrono = "0.4"
|
||||
chrono-tz = "0.4"
|
||||
clap = { version = "3.2.12", features = ["derive"] }
|
||||
regex = "1.6.0"
|
148
src/app.rs
Normal file
148
src/app.rs
Normal file
|
@ -0,0 +1,148 @@
|
|||
use chrono::Duration;
|
||||
use crossterm::event::KeyCode;
|
||||
use regex::Regex;
|
||||
use tui::backend::Backend;
|
||||
use tui::style::Color;
|
||||
use tui::style::Style;
|
||||
use tui::Frame;
|
||||
|
||||
use self::modes::Clock;
|
||||
use self::modes::Stopwatch;
|
||||
use self::modes::Timer;
|
||||
|
||||
pub(crate) mod modes;
|
||||
|
||||
#[derive(clap::ArgEnum, Clone)]
|
||||
pub(crate) enum Mode {
|
||||
Clock,
|
||||
Timer,
|
||||
Stopwatch,
|
||||
}
|
||||
|
||||
#[derive(clap::Parser)]
|
||||
pub(crate) struct App {
|
||||
#[clap(short, long, value_parser, arg_enum, default_value = "clock")]
|
||||
pub mode: Mode,
|
||||
#[clap(short, long, value_parser = parse_color, default_value = "green")]
|
||||
pub color: Color,
|
||||
#[clap(short, long, value_parser = parse_duration, default_value = "5m")]
|
||||
pub duration: Duration,
|
||||
#[clap(short, long, value_parser, default_value = "1")]
|
||||
pub size: u16,
|
||||
|
||||
#[clap(skip)]
|
||||
clock: Option<Clock>,
|
||||
#[clap(skip)]
|
||||
timer: Option<Timer>,
|
||||
#[clap(skip)]
|
||||
stopwatch: Option<Stopwatch>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn init_app(&mut self) {
|
||||
let style = Style::default().fg(self.color);
|
||||
match self.mode {
|
||||
Mode::Clock => {
|
||||
self.clock = Some(Clock {
|
||||
size: self.size,
|
||||
style,
|
||||
long: false,
|
||||
});
|
||||
}
|
||||
Mode::Timer => {
|
||||
self.timer = Some(Timer::new(self.duration, self.size, style));
|
||||
}
|
||||
Mode::Stopwatch => {
|
||||
self.stopwatch = Some(Stopwatch::new(self.size, style));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ui<B: Backend>(&self, f: &mut Frame<B>) {
|
||||
if let Some(w) = self.clock.as_ref() {
|
||||
f.render_widget(w, f.size());
|
||||
} else if let Some(w) = self.timer.as_ref() {
|
||||
f.render_widget(w, f.size());
|
||||
} else if let Some(w) = self.stopwatch.as_ref() {
|
||||
f.render_widget(w, f.size());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_key(&mut self, key: KeyCode) {
|
||||
if let Some(_w) = self.clock.as_mut() {
|
||||
} else if let Some(w) = self.timer.as_mut() {
|
||||
match key {
|
||||
KeyCode::Char(' ') => {
|
||||
if w.is_paused() {
|
||||
w.resume();
|
||||
} else {
|
||||
w.pause();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else if let Some(w) = self.stopwatch.as_mut() {
|
||||
match key {
|
||||
KeyCode::Char(' ') => {
|
||||
if w.is_paused() {
|
||||
w.resume();
|
||||
} else {
|
||||
w.pause();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_duration(s: &str) -> Result<Duration, String> {
|
||||
let reg = Regex::new(r"^(\d+)([smhdSMHD])$").unwrap();
|
||||
let cap = reg
|
||||
.captures(s)
|
||||
.ok_or_else(|| format!("{} is not a valid duration", s))?;
|
||||
|
||||
let num = cap.get(1).unwrap().as_str().parse::<i64>().unwrap();
|
||||
let unit = cap.get(2).unwrap().as_str().to_lowercase();
|
||||
|
||||
match unit.as_str() {
|
||||
"s" => Ok(Duration::seconds(num)),
|
||||
"m" => Ok(Duration::minutes(num)),
|
||||
"h" => Ok(Duration::hours(num)),
|
||||
"d" => Ok(Duration::days(num)),
|
||||
_ => Err(format!("Invalid duration: {}", s).into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_color(s: &str) -> Result<Color, String> {
|
||||
let s = s.to_lowercase();
|
||||
let reg = Regex::new(r"^#([0-9a-f]{6})$").unwrap();
|
||||
match s.as_str() {
|
||||
"black" => Ok(Color::Black),
|
||||
"red" => Ok(Color::Red),
|
||||
"green" => Ok(Color::Green),
|
||||
"yellow" => Ok(Color::Yellow),
|
||||
"blue" => Ok(Color::Blue),
|
||||
"magenta" => Ok(Color::Magenta),
|
||||
"cyan" => Ok(Color::Cyan),
|
||||
"gray" => Ok(Color::Gray),
|
||||
"darkgray" => Ok(Color::DarkGray),
|
||||
"lightred" => Ok(Color::LightRed),
|
||||
"lightGreen" => Ok(Color::LightGreen),
|
||||
"lightYellow" => Ok(Color::LightYellow),
|
||||
"lightBlue" => Ok(Color::LightBlue),
|
||||
"lightMagenta" => Ok(Color::LightMagenta),
|
||||
"lightCyan" => Ok(Color::LightCyan),
|
||||
"white" => Ok(Color::White),
|
||||
s => {
|
||||
let cap = reg
|
||||
.captures(s)
|
||||
.ok_or_else(|| format!("Invalid color: {}", s))?;
|
||||
let hex = cap.get(1).unwrap().as_str();
|
||||
let r = hex[1..3].parse::<u8>().unwrap();
|
||||
let g = hex[3..5].parse::<u8>().unwrap();
|
||||
let b = hex[5..7].parse::<u8>().unwrap();
|
||||
Ok(Color::Rgb(r, g, b))
|
||||
}
|
||||
}
|
||||
}
|
42
src/app/modes.rs
Normal file
42
src/app/modes.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
mod clock;
|
||||
mod stopwatch;
|
||||
mod timer;
|
||||
|
||||
use std::cmp::min;
|
||||
|
||||
use chrono::Duration;
|
||||
pub(crate) use clock::Clock;
|
||||
use clock_tui::bricks_text::BricksText;
|
||||
pub(crate) use stopwatch::Stopwatch;
|
||||
pub(crate) use timer::Timer;
|
||||
use tui::{buffer::Buffer, layout::Rect, widgets::Widget};
|
||||
|
||||
fn format_duration(duration: Duration) -> String {
|
||||
let millis = duration.num_milliseconds();
|
||||
let seconds = millis / 1000;
|
||||
let minutes = seconds / 60;
|
||||
let hours = minutes / 60;
|
||||
let days = hours / 24;
|
||||
let mut result = String::new();
|
||||
if days > 0 {
|
||||
result.push_str(&format!("{}:", days));
|
||||
}
|
||||
if hours > 0 {
|
||||
result.push_str(&format!("{}:", hours % 24));
|
||||
}
|
||||
result.push_str(&format!("{}:", minutes % 60));
|
||||
result.push_str(&format!("{:02}.{}", seconds % 60, (millis % 1000) / 100));
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn render_centered(area: Rect, buf: &mut Buffer, text: &BricksText) {
|
||||
let text_size = text.size();
|
||||
let text_area = Rect {
|
||||
x: area.x + (area.width.saturating_sub(text_size.0)) / 2,
|
||||
y: area.y + (area.height.saturating_sub(text_size.1)) / 2,
|
||||
width: min(text_size.0, area.width),
|
||||
height: min(text_size.0, area.height),
|
||||
};
|
||||
text.render(text_area, buf);
|
||||
}
|
50
src/app/modes/clock.rs
Normal file
50
src/app/modes/clock.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use std::cmp::min;
|
||||
|
||||
use chrono::Local;
|
||||
use clock_tui::bricks_text::BricksText;
|
||||
use tui::{
|
||||
layout::Rect,
|
||||
style::Style,
|
||||
text::Span,
|
||||
widgets::{Paragraph, Widget},
|
||||
};
|
||||
|
||||
pub(crate) struct Clock {
|
||||
pub size: u16,
|
||||
pub style: Style,
|
||||
pub long: bool,
|
||||
}
|
||||
|
||||
impl Widget for &Clock {
|
||||
fn render(self, area: Rect, buf: &mut tui::buffer::Buffer) {
|
||||
let now = Local::now();
|
||||
let time_str = if self.long {
|
||||
let mut str = now.format("%H:%M:%S%.3f").to_string();
|
||||
str.truncate(str.len() - 2);
|
||||
str
|
||||
} else {
|
||||
now.format("%H:%M:%S").to_string()
|
||||
};
|
||||
let time_str = time_str.as_str();
|
||||
let text = BricksText::new(time_str, self.size, self.size, self.style);
|
||||
let text_size = text.size();
|
||||
let text_area = Rect {
|
||||
x: area.x + (area.width.saturating_sub(text_size.0)) / 2,
|
||||
y: area.y + (area.height.saturating_sub(text_size.1)) / 2,
|
||||
width: min(text_size.0, area.width),
|
||||
height: min(text_size.0, area.height),
|
||||
};
|
||||
text.render(text_area, buf);
|
||||
let text = now.format("%Y-%m-%d %Z").to_string();
|
||||
let text_len = text.as_str().len() as u16;
|
||||
let paragrahp = Paragraph::new(Span::from(text)).style(Style::default());
|
||||
|
||||
let para_area = Rect {
|
||||
x: area.x + (area.width.saturating_sub(text_len)) / 2,
|
||||
y: text_area.y.saturating_sub(2),
|
||||
width: min(text_len, area.width),
|
||||
height: min(1, area.height),
|
||||
};
|
||||
paragrahp.render(para_area, buf);
|
||||
}
|
||||
}
|
58
src/app/modes/stopwatch.rs
Normal file
58
src/app/modes/stopwatch.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
use chrono::{DateTime, Duration, Local};
|
||||
use clock_tui::bricks_text::BricksText;
|
||||
use tui::{buffer::Buffer, layout::Rect, style::Style, widgets::Widget};
|
||||
|
||||
use super::{format_duration, render_centered};
|
||||
|
||||
pub struct Stopwatch {
|
||||
pub size: u16,
|
||||
pub style: Style,
|
||||
duration: Duration,
|
||||
started_at: Option<DateTime<Local>>,
|
||||
}
|
||||
|
||||
impl Stopwatch {
|
||||
pub(crate) fn new(size: u16, style: Style) -> Self {
|
||||
Self {
|
||||
size,
|
||||
style,
|
||||
duration: Duration::zero(),
|
||||
started_at: Some(Local::now()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_paused(&self) -> bool {
|
||||
self.started_at.is_none()
|
||||
}
|
||||
|
||||
pub(crate) fn pause(&mut self) {
|
||||
if let Some(start_at) = self.started_at {
|
||||
let now = Local::now();
|
||||
self.duration = self.duration + now.signed_duration_since(start_at);
|
||||
self.started_at = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resume(&mut self) {
|
||||
if self.started_at.is_none() {
|
||||
self.started_at = Some(Local::now());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn total_time(&self) -> Duration {
|
||||
if let Some(start_at) = self.started_at {
|
||||
let now = Local::now();
|
||||
self.duration + now.signed_duration_since(start_at)
|
||||
} else {
|
||||
self.duration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for &Stopwatch {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let time_str = format_duration(self.total_time());
|
||||
let text = BricksText::new(time_str.as_str(), self.size, self.size, self.style);
|
||||
render_centered(area, buf, &text);
|
||||
}
|
||||
}
|
66
src/app/modes/timer.rs
Normal file
66
src/app/modes/timer.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
use chrono::{DateTime, Duration, Local};
|
||||
use clock_tui::bricks_text::BricksText;
|
||||
use tui::{buffer::Buffer, layout::Rect, style::Style, widgets::Widget};
|
||||
|
||||
use super::{format_duration, render_centered};
|
||||
|
||||
pub struct Timer {
|
||||
pub size: u16,
|
||||
pub style: Style,
|
||||
duration: Duration,
|
||||
ended_at: Option<DateTime<Local>>,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
pub(crate) fn new(duration: Duration, size: u16, style: Style) -> Self {
|
||||
Self {
|
||||
duration,
|
||||
size,
|
||||
style,
|
||||
ended_at: Some(Local::now() + duration),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_paused(&self) -> bool {
|
||||
self.ended_at.is_none()
|
||||
}
|
||||
|
||||
pub(crate) fn pause(&mut self) {
|
||||
if let Some(end_at) = self.ended_at {
|
||||
if end_at <= Local::now() {
|
||||
self.duration = Duration::zero();
|
||||
} else {
|
||||
self.duration = end_at - Local::now();
|
||||
}
|
||||
self.ended_at = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resume(&mut self) {
|
||||
if self.ended_at.is_none() {
|
||||
self.ended_at = Some(Local::now() + self.duration);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn remaining_time(&self) -> Duration {
|
||||
if let Some(end_at) = self.ended_at {
|
||||
let now = Local::now();
|
||||
if end_at <= now {
|
||||
Duration::zero()
|
||||
} else {
|
||||
end_at.signed_duration_since(now)
|
||||
}
|
||||
} else {
|
||||
self.duration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for &Timer {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let time_str = format_duration(self.remaining_time());
|
||||
// println!("{}", time_str);
|
||||
let text = BricksText::new(time_str.as_str(), self.size, self.size, self.style);
|
||||
render_centered(area, buf, &text);
|
||||
}
|
||||
}
|
46
src/bricks_text.rs
Normal file
46
src/bricks_text.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
use tui::{style::Style, widgets::Widget};
|
||||
|
||||
use self::chars::{BrickChar, Point};
|
||||
|
||||
mod chars;
|
||||
|
||||
pub struct BricksText {
|
||||
text: String,
|
||||
size: u16,
|
||||
space: u16,
|
||||
style: Style,
|
||||
}
|
||||
|
||||
impl BricksText {
|
||||
pub fn new(text: &str, size: u16, space: u16, style: Style) -> BricksText {
|
||||
BricksText {
|
||||
text: text.to_string(),
|
||||
size,
|
||||
space,
|
||||
style,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> (u16, u16) {
|
||||
let Point(w, h) = BrickChar::size(self.size);
|
||||
let n_chars = self.text.chars().count() as u16;
|
||||
(w * n_chars + self.space * (n_chars - 1), h)
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for &BricksText {
|
||||
fn render(self, area: tui::layout::Rect, buf: &mut tui::buffer::Buffer) {
|
||||
let mut area = area.clone();
|
||||
for char in self.text.chars() {
|
||||
let Point(w, _) = BrickChar::size(self.size);
|
||||
let char = BrickChar::from(char);
|
||||
char.render(self.size, self.style, area, buf);
|
||||
let l = w + self.space;
|
||||
area.x += l;
|
||||
area.width = area.width.saturating_sub(l);
|
||||
if area.area() == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
265
src/bricks_text/chars.rs
Normal file
265
src/bricks_text/chars.rs
Normal file
|
@ -0,0 +1,265 @@
|
|||
use std::{cmp::max, ops};
|
||||
use tui::{buffer::Buffer, layout::Rect, style::Style};
|
||||
|
||||
pub(crate) trait BricksChar {
|
||||
fn render(&self, size: u16, style: Style, area: Rect, buf: &mut Buffer);
|
||||
}
|
||||
|
||||
pub struct BrickChar(char);
|
||||
|
||||
impl BrickChar {
|
||||
const H_UNIT: u16 = 2;
|
||||
const V_UNIT: u16 = 1;
|
||||
const UNIT_SIZE: Point = Point(3 * Self::H_UNIT, 5 * Self::V_UNIT);
|
||||
|
||||
pub(crate) fn size(size: u16) -> Point {
|
||||
Self::UNIT_SIZE.clone() * size
|
||||
}
|
||||
|
||||
pub(crate) fn from(char: char) -> BrickChar {
|
||||
BrickChar(char)
|
||||
}
|
||||
|
||||
pub(crate) fn render(&self, size: u16, style: Style, area: Rect, buf: &mut Buffer) {
|
||||
let char_size = BrickChar::size(size);
|
||||
match self.0 {
|
||||
'0'..='9' => Self::draw_digital(self.0, size, style, area, buf),
|
||||
':' => {
|
||||
let start_x = area.x + size * Self::H_UNIT;
|
||||
let end_x = area.x + char_size.0 - size * Self::H_UNIT;
|
||||
let start_y = area.y + size * Self::V_UNIT;
|
||||
let start_y2 = area.y + (char_size.1 + size * Self::V_UNIT) / 2;
|
||||
let len = (char_size.1 - 3 * size * Self::V_UNIT) / 2;
|
||||
for x in (start_x..end_x).step_by((size * Self::H_UNIT) as usize) {
|
||||
Self::draw_line(
|
||||
size,
|
||||
Point(x, start_y),
|
||||
len,
|
||||
LineDir::Vertical,
|
||||
style,
|
||||
&area,
|
||||
buf,
|
||||
);
|
||||
Self::draw_line(
|
||||
size,
|
||||
Point(x, start_y2),
|
||||
len,
|
||||
LineDir::Vertical,
|
||||
style,
|
||||
&area,
|
||||
buf,
|
||||
);
|
||||
}
|
||||
}
|
||||
'-' => {
|
||||
let x = area.x;
|
||||
let y = area.y + (char_size.1 - size * Self::V_UNIT) / 2;
|
||||
Self::draw_line(
|
||||
size,
|
||||
Point(x, y),
|
||||
char_size.1,
|
||||
LineDir::Horizontal,
|
||||
style,
|
||||
&area,
|
||||
buf,
|
||||
);
|
||||
}
|
||||
'.' => {
|
||||
let x = area.x + char_size.0 - size * Self::H_UNIT;
|
||||
let y = area.y + char_size.1 - size * Self::V_UNIT;
|
||||
Self::draw_line(
|
||||
size,
|
||||
Point(x, y),
|
||||
size * Self::H_UNIT,
|
||||
LineDir::Horizontal,
|
||||
style,
|
||||
&area,
|
||||
buf,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_digital(d: char, size: u16, style: Style, area: Rect, buf: &mut Buffer) {
|
||||
let char_size = BrickChar::size(size);
|
||||
let mut draw_line =
|
||||
|x, y, len, dir| Self::draw_line(size, Point(x, y), len, dir, style, &area, buf);
|
||||
let x_start = area.x;
|
||||
let x_end = area.x + char_size.0 - size * Self::H_UNIT;
|
||||
let y_start = area.y;
|
||||
let y_end = area.y + char_size.1 - size * Self::V_UNIT;
|
||||
let y_center = area.y + (char_size.1 - size * Self::V_UNIT) / 2;
|
||||
let half_h = (char_size.1 + size * Self::V_UNIT) / 2;
|
||||
match d {
|
||||
'0' => {
|
||||
draw_line(x_start, y_start, half_h, LineDir::Vertical);
|
||||
draw_line(x_start, y_center, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_start, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_center, half_h, LineDir::Vertical);
|
||||
|
||||
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
|
||||
// draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
|
||||
draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
|
||||
}
|
||||
'1' => {
|
||||
// draw_line(x_start, y_start, half_h, LineDir::Vertical);
|
||||
// draw_line(x_start, y_center, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_start, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_center, half_h, LineDir::Vertical);
|
||||
|
||||
// draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
|
||||
// draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
|
||||
// draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
|
||||
}
|
||||
'2' => {
|
||||
// draw_line(x_start, y_start, half_h, LineDir::Vertical);
|
||||
draw_line(x_start, y_center, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_start, half_h, LineDir::Vertical);
|
||||
// draw_line(x_end, y_center, half_h, LineDir::Vertical);
|
||||
|
||||
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
|
||||
draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
|
||||
draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
|
||||
}
|
||||
'3' => {
|
||||
// draw_line(x_start, y_start, half_h, LineDir::Vertical);
|
||||
// draw_line(x_start, y_center, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_start, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_center, half_h, LineDir::Vertical);
|
||||
|
||||
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
|
||||
draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
|
||||
draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
|
||||
}
|
||||
'4' => {
|
||||
draw_line(x_start, y_start, half_h, LineDir::Vertical);
|
||||
// draw_line(x_start, y_center, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_start, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_center, half_h, LineDir::Vertical);
|
||||
|
||||
// draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
|
||||
draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
|
||||
// draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
|
||||
}
|
||||
'5' => {
|
||||
draw_line(x_start, y_start, half_h, LineDir::Vertical);
|
||||
// draw_line(x_start, y_center, half_h, LineDir::Vertical);
|
||||
// draw_line(x_end, y_start, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_center, half_h, LineDir::Vertical);
|
||||
|
||||
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
|
||||
draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
|
||||
draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
|
||||
}
|
||||
'6' => {
|
||||
draw_line(x_start, y_start, half_h, LineDir::Vertical);
|
||||
draw_line(x_start, y_center, half_h, LineDir::Vertical);
|
||||
// draw_line(x_end, y_start, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_center, half_h, LineDir::Vertical);
|
||||
|
||||
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
|
||||
draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
|
||||
draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
|
||||
}
|
||||
'7' => {
|
||||
// draw_line(x_start, y_start, half_h, LineDir::Vertical);
|
||||
// draw_line(x_start, y_center, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_start, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_center, half_h, LineDir::Vertical);
|
||||
|
||||
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
|
||||
// draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
|
||||
// draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
|
||||
}
|
||||
'8' => {
|
||||
draw_line(x_start, y_start, half_h, LineDir::Vertical);
|
||||
draw_line(x_start, y_center, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_start, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_center, half_h, LineDir::Vertical);
|
||||
|
||||
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
|
||||
draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
|
||||
draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
|
||||
}
|
||||
'9' => {
|
||||
draw_line(x_start, y_start, half_h, LineDir::Vertical);
|
||||
// draw_line(x_start, y_center, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_start, half_h, LineDir::Vertical);
|
||||
draw_line(x_end, y_center, half_h, LineDir::Vertical);
|
||||
|
||||
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
|
||||
draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
|
||||
draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_line(
|
||||
size: u16,
|
||||
start: Point,
|
||||
len: u16,
|
||||
dir: LineDir,
|
||||
style: Style,
|
||||
area: &Rect,
|
||||
buf: &mut Buffer,
|
||||
) {
|
||||
let step = match dir {
|
||||
LineDir::Horizontal => Point(Self::H_UNIT, 0),
|
||||
LineDir::Vertical => Point(0, Self::V_UNIT),
|
||||
};
|
||||
|
||||
let line = match dir {
|
||||
LineDir::Horizontal => Point(0, Self::V_UNIT),
|
||||
LineDir::Vertical => Point(Self::H_UNIT, 0),
|
||||
};
|
||||
|
||||
let mut from = start;
|
||||
for _ in 0..size {
|
||||
let mut p = from;
|
||||
for _ in (0..len).step_by(max(step.0, step.1).into()) {
|
||||
if !p.in_area(&area) {
|
||||
break;
|
||||
}
|
||||
// println!("p = {:?} area = {:?}", p, area);
|
||||
buf.get_mut(p.0, p.1).set_symbol("██").set_style(style);
|
||||
p = p + &step;
|
||||
}
|
||||
from = from + &line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum LineDir {
|
||||
Horizontal,
|
||||
Vertical,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) struct Point(pub u16, pub u16);
|
||||
|
||||
impl Point {
|
||||
pub(crate) fn in_area(&self, area: &Rect) -> bool {
|
||||
area.left() <= self.0
|
||||
&& self.0 < area.right()
|
||||
&& area.top() <= self.1
|
||||
&& self.1 < area.bottom()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Add<&Point> for Point {
|
||||
type Output = Point;
|
||||
|
||||
fn add(self, other: &Point) -> Point {
|
||||
Point(self.0 + other.0, self.1 + other.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Mul<u16> for Point {
|
||||
type Output = Point;
|
||||
|
||||
fn mul(self, other: u16) -> Point {
|
||||
Point(self.0 * other, self.1 * other)
|
||||
}
|
||||
}
|
1
src/lib.rs
Normal file
1
src/lib.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod bricks_text;
|
79
src/main.rs
Normal file
79
src/main.rs
Normal file
|
@ -0,0 +1,79 @@
|
|||
use std::{
|
||||
error::Error,
|
||||
io,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
use crossterm::{
|
||||
event::{self, Event, KeyCode},
|
||||
execute,
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
use tui::{
|
||||
backend::{Backend, CrosstermBackend},
|
||||
Terminal,
|
||||
};
|
||||
|
||||
mod app;
|
||||
|
||||
fn run_app<B: Backend>(
|
||||
terminal: &mut Terminal<B>,
|
||||
app: &mut app::App,
|
||||
tick_rate: Duration,
|
||||
) -> io::Result<()> {
|
||||
let mut last_tick = Instant::now();
|
||||
|
||||
loop {
|
||||
terminal.draw(|f| app.ui(f))?;
|
||||
|
||||
let timeout = tick_rate
|
||||
.checked_sub(last_tick.elapsed())
|
||||
.unwrap_or(Duration::from_secs(0));
|
||||
if event::poll(timeout)? {
|
||||
if let Event::Key(key) = event::read()? {
|
||||
match key.code {
|
||||
KeyCode::Char('q') => {
|
||||
return Ok(());
|
||||
}
|
||||
key => app.on_key(key),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if last_tick.elapsed() >= tick_rate {
|
||||
last_tick = Instant::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let mut app = app::App::parse();
|
||||
app.init_app();
|
||||
|
||||
// setup terminal
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen /*EnableMouseCapture*/,)?;
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
// create app and run it
|
||||
let tick_rate = Duration::from_millis(100);
|
||||
let res = run_app(&mut terminal, &mut app, tick_rate);
|
||||
|
||||
// restore terminal
|
||||
disable_raw_mode()?;
|
||||
execute!(
|
||||
terminal.backend_mut(),
|
||||
LeaveAlternateScreen,
|
||||
// DisableMouseCapture
|
||||
)?;
|
||||
terminal.show_cursor()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
eprintln!("{:?}", err)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
Reference in a new issue