mirror of
https://github.com/ricoriedel/wipe.git
synced 2024-12-23 06:36:42 +00:00
Reset repository
This commit is contained in:
parent
7500e5af1b
commit
a92cf27664
633
Cargo.lock
generated
633
Cargo.lock
generated
@ -2,637 +2,6 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
|
||||
|
||||
[[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 = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83"
|
||||
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 = "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_winapi"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "difflib"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||
|
||||
[[package]]
|
||||
name = "downcast"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be"
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fragile"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85dcb89d2b10c5f6133de2efd8c11959ce9dbb46a2f7a4cab208c4eeda6ce1ab"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
|
||||
|
||||
[[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 = "itertools"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[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",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mockall"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5641e476bbaf592a3939a7485fa079f427b4db21407d5ebfd5bba4e07a1f6f4c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"downcast",
|
||||
"fragile",
|
||||
"lazy_static",
|
||||
"mockall_derive",
|
||||
"predicates",
|
||||
"predicates-tree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mockall_derive"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "262d56735932ee0240d515656e5a7667af3af2a5b0af4da558c4cff2b2aeb0c7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "normalize-line-endings"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
|
||||
|
||||
[[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.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac8b1a9b2518dc799a2271eff1688707eb315f0d4697aa6b0871369ca4c4da55"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
|
||||
|
||||
[[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 = "ppv-lite86"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c"
|
||||
dependencies = [
|
||||
"difflib",
|
||||
"float-cmp",
|
||||
"itertools",
|
||||
"normalize-line-endings",
|
||||
"predicates-core",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "predicates-core"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb"
|
||||
|
||||
[[package]]
|
||||
name = "predicates-tree"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032"
|
||||
dependencies = [
|
||||
"predicates-core",
|
||||
"termtree",
|
||||
]
|
||||
|
||||
[[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 = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[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 = "1.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
|
||||
|
||||
[[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 = "termtree"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b"
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
||||
|
||||
[[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.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"
|
||||
|
||||
[[package]]
|
||||
name = "wipe"
|
||||
version = "1.0.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"crossterm",
|
||||
"mockall",
|
||||
"rand",
|
||||
]
|
||||
version = "2.0.0"
|
||||
|
16
Cargo.toml
16
Cargo.toml
@ -1,21 +1,7 @@
|
||||
# Don't forget to update
|
||||
# - dist/arch/PKGBUILD
|
||||
# - dist/deb/build
|
||||
|
||||
[package]
|
||||
name = "wipe"
|
||||
version = "1.0.1"
|
||||
version = "2.0.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
description = "Wipe your terminal with a random animation."
|
||||
repository = "https://github.com/ricoriedel/wipe"
|
||||
authors = ["Rico Riedel"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
clap = { version = "3.2", features = ["derive"]}
|
||||
crossterm = "0.23"
|
||||
rand = "0.8"
|
||||
|
||||
[dev-dependencies]
|
||||
mockall = "0.11"
|
36
README.md
36
README.md
@ -1,36 +0,0 @@
|
||||
# Wipe
|
||||
Wipe your terminal with a smooth animation.
|
||||
|
||||
This is the perfect program for you, if you like `clear` but want to add an unnecessary animation.
|
||||
|
||||
Download options can be found in the [release tab](https://github.com/ricoriedel/wipe/releases).
|
||||
|
||||
## Configuration
|
||||
All configuration is done using command line parameters.
|
||||
For a list of parameters, execute `wipe -h`.
|
||||
Note that some parameters like `--color` can be specified multiple times with different values.
|
||||
|
||||
If you want a persistent solution, you can add an alias to your `.bashrc` equivalent.
|
||||
```shell
|
||||
# Persistent config
|
||||
alias wipe='wipe -c red'
|
||||
|
||||
# Replace clear with wipe
|
||||
alias clear='wipe'
|
||||
```
|
||||
|
||||
If you are using `ZSH` as your shell, you can add a keyboard shortcut like this:
|
||||
```shell
|
||||
# Bind wipe to CTRL+W
|
||||
wipe-zle() {
|
||||
wipe
|
||||
zle reset-prompt
|
||||
}
|
||||
zle -N wipe-zle
|
||||
bindkey '^w' wipe-zle
|
||||
```
|
||||
|
||||
## Showcase
|
||||
[![Circle](doc/circle.gif)]()
|
||||
[![Rhombus](doc/rhombus.gif)]()
|
||||
[![Rotation](doc/sonar.gif)]()
|
37
dist/arch/PKGBUILD
vendored
37
dist/arch/PKGBUILD
vendored
@ -1,37 +0,0 @@
|
||||
# Maintainer: Rico Riedel <rico.riedel@protonmail.ch>
|
||||
|
||||
pkgname='wipe-terminal-git'
|
||||
pkgver='1.0.1'
|
||||
pkgrel='4'
|
||||
pkgdesc='Wipe your terminal with a random animation.'
|
||||
arch=('x86_64')
|
||||
url='https://github.com/ricoriedel/wipe'
|
||||
license=('MIT')
|
||||
makedepends=('cargo')
|
||||
conflicts=('wipe')
|
||||
source=('git+https://www.github.com/ricoriedel/wipe.git')
|
||||
sha256sums=('SKIP')
|
||||
|
||||
prepare() {
|
||||
cd wipe
|
||||
cargo fetch --locked --target "$CARCH-unknown-linux-gnu"
|
||||
}
|
||||
|
||||
build() {
|
||||
cd wipe
|
||||
export RUSTUP_TOOLCHAIN=stable
|
||||
export CARGO_TARGET_DIR=target
|
||||
cargo build --frozen --release --all-features
|
||||
}
|
||||
|
||||
check() {
|
||||
cd wipe
|
||||
export RUSTUP_TOOLCHAIN=stable
|
||||
cargo test --frozen --all-features
|
||||
}
|
||||
|
||||
package() {
|
||||
cd wipe
|
||||
install -Dm0644 -t "$pkgdir/usr/share/licenses/$pkgname/" 'LICENSE'
|
||||
install -Dm0755 -t "$pkgdir/usr/bin/" "target/release/wipe"
|
||||
}
|
45
dist/deb/build
vendored
45
dist/deb/build
vendored
@ -1,45 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
pkgname='wipe-terminal'
|
||||
version='1.0.1-4'
|
||||
maintainer='Rico Riedel <rico.riedel@protonmail.ch>'
|
||||
description='Wipe your terminal with a random animation.'
|
||||
arch='x86_64'
|
||||
debarch='amd64'
|
||||
pkgdir="$PWD/$pkgname"
|
||||
srcdir="$PWD/../.."
|
||||
|
||||
build() {
|
||||
cd "$srcdir"
|
||||
|
||||
export RUSTUP_TOOLCHAIN=stable
|
||||
export CARGO_TARGET_DIR=target
|
||||
|
||||
cargo fetch --locked --target "$arch-unknown-linux-gnu"
|
||||
cargo build --frozen --release --all-features
|
||||
cargo test --frozen --all-features
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$srcdir"
|
||||
|
||||
install -Dm0644 'LICENSE' "$pkgdir/usr/share/doc/$pkgname/copyright"
|
||||
install -Dm0755 -t "$pkgdir/usr/bin/" "target/release/wipe"
|
||||
|
||||
mkdir -p "$pkgdir/DEBIAN"
|
||||
echo "Package: $pkgname" > "$pkgdir/DEBIAN/control"
|
||||
echo "Version: $version" >> "$pkgdir/DEBIAN/control"
|
||||
echo "Section: utils" >> "$pkgdir/DEBIAN/control"
|
||||
echo "Priority: optional" >> "$pkgdir/DEBIAN/control"
|
||||
echo "Architecture: $debarch" >> "$pkgdir/DEBIAN/control"
|
||||
echo "Maintainer: $maintainer" >> "$pkgdir/DEBIAN/control"
|
||||
echo "Description: $description" >> "$pkgdir/DEBIAN/control"
|
||||
|
||||
dpkg-deb --root-owner-group --build "$pkgdir"
|
||||
}
|
||||
|
||||
mkdir -p "$pkgdir"
|
||||
|
||||
build
|
||||
package
|
30
dist/win/build
vendored
30
dist/win/build
vendored
@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
target='x86_64-pc-windows-gnu'
|
||||
pkgdir="$PWD"
|
||||
pkgfile="$pkgdir/win.zip"
|
||||
srcdir="$PWD/../.."
|
||||
|
||||
build() {
|
||||
cd "$srcdir"
|
||||
|
||||
export RUSTUP_TOOLCHAIN=stable
|
||||
export CARGO_TARGET_DIR=target
|
||||
|
||||
cargo fetch --locked --target "$target"
|
||||
cargo build --frozen --release --all-features --target "$target"
|
||||
cargo test --frozen --all-features
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$srcdir"
|
||||
cp "target/$target/release/wipe.exe" "$pkgdir"
|
||||
cp 'LICENSE' "$pkgdir"
|
||||
|
||||
cd "$pkgdir"
|
||||
zip -m "$pkgfile" 'wipe.exe' 'LICENSE'
|
||||
}
|
||||
|
||||
build
|
||||
package
|
BIN
doc/circle.gif
BIN
doc/circle.gif
Binary file not shown.
Before Width: | Height: | Size: 275 KiB |
BIN
doc/rhombus.gif
BIN
doc/rhombus.gif
Binary file not shown.
Before Width: | Height: | Size: 338 KiB |
BIN
doc/sonar.gif
BIN
doc/sonar.gif
Binary file not shown.
Before Width: | Height: | Size: 223 KiB |
@ -1,52 +0,0 @@
|
||||
use crate::animation::Animation;
|
||||
use crate::vec::Vector;
|
||||
|
||||
const THICKNESS: f32 = 0.2;
|
||||
const FINAL_RADIUS: f32 = 1.0 + THICKNESS * 2.0;
|
||||
|
||||
/// An animation of an expanding circle.
|
||||
pub struct CircleAnimation {
|
||||
center: Vector,
|
||||
thickness: f32,
|
||||
final_radius: f32,
|
||||
}
|
||||
|
||||
impl CircleAnimation {
|
||||
pub fn new(size: Vector) -> Self {
|
||||
let center = size.center();
|
||||
let distance = center.length();
|
||||
|
||||
Self {
|
||||
center,
|
||||
thickness: distance * THICKNESS,
|
||||
final_radius: distance * FINAL_RADIUS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Animation for CircleAnimation {
|
||||
fn sample(&self, step: f32, pos: Vector) -> f32 {
|
||||
let radius = self.final_radius * step - self.thickness;
|
||||
let distance = (pos - self.center).length();
|
||||
|
||||
(distance - radius) / self.thickness
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sample() {
|
||||
let anim = CircleAnimation::new(Vector::new(10.0, 20.0));
|
||||
|
||||
let sample_1 = anim.sample(0.5, Vector::new(17.0, 5.0));
|
||||
let sample_2 = anim.sample(0.8, Vector::new(11.0, 8.0));
|
||||
let sample_3 = anim.sample(0.2, Vector::new(7.0, 10.0));
|
||||
|
||||
assert!(3.3 < sample_1 && sample_1 < 3.4);
|
||||
assert!(-1.8 < sample_2 && sample_2 < -1.7);
|
||||
assert!(0.4 < sample_3 && sample_3 < 0.5);
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
pub mod circle;
|
||||
pub mod sonar;
|
||||
pub mod rhombus;
|
||||
|
||||
use crate::vec::Vector;
|
||||
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
/// A sampler for an animation.
|
||||
#[cfg_attr(test, automock)]
|
||||
pub trait Animation {
|
||||
/// Returns the level (of brightness) for the
|
||||
/// given step of the animation an position on screen.
|
||||
/// # Arguments
|
||||
/// * `step`: `0 <= step` and `step <= 1`
|
||||
///
|
||||
/// # Return values
|
||||
/// * `1 < n` => Keep current character
|
||||
/// * `0 <= n` and `n < 1` => Draw some character
|
||||
/// * `n < 0` => Clear character
|
||||
fn sample(&self, step: f32, pos: Vector) -> f32;
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
use crate::animation::Animation;
|
||||
use crate::vec::Vector;
|
||||
|
||||
const THICKNESS: f32 = 0.2;
|
||||
const FINAL_DISTANCE: f32 = 1.0 + THICKNESS * 2.0;
|
||||
|
||||
/// An animation of an expanding rhombus.
|
||||
pub struct RhombusAnimation {
|
||||
center: Vector,
|
||||
thickness: f32,
|
||||
final_distance: f32,
|
||||
}
|
||||
|
||||
impl RhombusAnimation {
|
||||
pub fn new(size: Vector) -> Self {
|
||||
let center = size.center();
|
||||
let distance = center.sum();
|
||||
|
||||
Self {
|
||||
center,
|
||||
thickness: distance * THICKNESS,
|
||||
final_distance: distance * FINAL_DISTANCE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Animation for RhombusAnimation {
|
||||
fn sample(&self, step: f32, pos: Vector) -> f32 {
|
||||
let dist = self.final_distance * step - self.thickness;
|
||||
let pos_dist = (self.center - pos).abs().sum();
|
||||
|
||||
(pos_dist - dist) / self.thickness
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sample() {
|
||||
let anim = RhombusAnimation::new(Vector::new(30.0, 10.0));
|
||||
|
||||
let sample_1 = anim.sample(0.2, Vector::new(5.0, 16.0));
|
||||
let sample_2 = anim.sample(0.7, Vector::new(22.0, 2.0));
|
||||
let sample_3 = anim.sample(0.5, Vector::new(4.0, 7.0));
|
||||
|
||||
assert!(4.8 < sample_1 && sample_1 < 4.9);
|
||||
assert!(-1.5 < sample_2 && sample_2 < -1.4);
|
||||
assert!(0.7 < sample_3 && sample_3 < 0.8);
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
use std::f32::consts::PI;
|
||||
use crate::animation::Animation;
|
||||
use crate::vec::Vector;
|
||||
|
||||
const TWO_PI: f32 = PI * 2.0;
|
||||
const THICKNESS: f32 = TWO_PI * 0.1;
|
||||
const FULL_ROTATION: f32 = TWO_PI + THICKNESS * 2.0;
|
||||
|
||||
/// A sonar like animation.
|
||||
pub struct SonarAnimation {
|
||||
center: Vector
|
||||
}
|
||||
|
||||
impl SonarAnimation {
|
||||
pub fn new(size: Vector) -> Self {
|
||||
Self {
|
||||
center: size.center()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Animation for SonarAnimation {
|
||||
fn sample(&self, step: f32, pos: Vector) -> f32 {
|
||||
let angle = FULL_ROTATION * step - PI - THICKNESS;
|
||||
let pos_angle = (pos - self.center).angle();
|
||||
|
||||
(pos_angle - angle) / THICKNESS
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sample() {
|
||||
let anim = SonarAnimation::new(Vector::new(30.0, 10.0));
|
||||
|
||||
let sample_1 = anim.sample(0.3, Vector::new(16.0, 3.0));
|
||||
let sample_2 = anim.sample(0.7, Vector::new(22.0, 2.0));
|
||||
let sample_3 = anim.sample(0.5, Vector::new(4.0, 7.0));
|
||||
|
||||
assert!(0.6 < sample_1 && sample_1 < 0.7);
|
||||
assert!(-3.1 < sample_2 && sample_2 < -3.0);
|
||||
assert!(4.7 < sample_3 && sample_3 < 4.8);
|
||||
}
|
||||
}
|
98
src/array.rs
98
src/array.rs
@ -1,98 +0,0 @@
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
/// A two dimensional statically size array.
|
||||
pub struct Array2D<T> {
|
||||
width: usize,
|
||||
height: usize,
|
||||
values: Vec<T>
|
||||
}
|
||||
|
||||
impl<T: Default + Copy> Array2D<T> {
|
||||
pub fn new(width: usize, height: usize) -> Self {
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
values: vec![T::default(); width * height]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn width(&self) -> usize {
|
||||
self.width
|
||||
}
|
||||
|
||||
pub fn height(&self) -> usize {
|
||||
self.height
|
||||
}
|
||||
|
||||
/// Calculates the physical index of the given position.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if the position is out of bounds.
|
||||
fn index_of(&self, pos: (usize, usize)) -> usize {
|
||||
assert!(pos.0 < self.width);
|
||||
assert!(pos.1 < self.height);
|
||||
pos.0 + pos.1 * self.width
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default + Copy> Index<(usize, usize)> for Array2D<T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, pos: (usize, usize)) -> &Self::Output {
|
||||
unsafe { self.values.get_unchecked(self.index_of(pos)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default + Copy> IndexMut<(usize, usize)> for Array2D<T> {
|
||||
fn index_mut(&mut self, pos: (usize, usize)) -> &mut Self::Output {
|
||||
let i = self.index_of(pos);
|
||||
|
||||
unsafe { self.values.get_unchecked_mut(i) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::array::Array2D;
|
||||
|
||||
#[test]
|
||||
fn width() {
|
||||
let array = Array2D::<()>::new(10, 4);
|
||||
|
||||
assert_eq!(10, array.width());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn height() {
|
||||
let array = Array2D::<()>::new(2, 5);
|
||||
|
||||
assert_eq!(5, array.height());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn index() {
|
||||
let mut array = Array2D::new(4, 4);
|
||||
|
||||
array[(1, 2)] = 3;
|
||||
array[(3, 3)] = 7;
|
||||
|
||||
assert_eq!(3, array[(1, 2)]);
|
||||
assert_eq!(7, array[(3, 3)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn index_oob_width() {
|
||||
let array = Array2D::<()>::new(5, 10);
|
||||
|
||||
array[(8, 2)];
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn index_oob_height() {
|
||||
let array = Array2D::<()>::new(10, 5);
|
||||
|
||||
array[(3, 7)];
|
||||
}
|
||||
}
|
62
src/char.rs
62
src/char.rs
@ -1,62 +0,0 @@
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
/// Used to get a character with a given brightness.
|
||||
#[cfg_attr(test, automock)]
|
||||
pub trait CharSampler {
|
||||
/// Gets a character with the given brightness.
|
||||
/// # Arguments
|
||||
/// * `level`: `0 <= level` and `level < 1`
|
||||
fn sample(&self, level: f32) -> char;
|
||||
}
|
||||
|
||||
pub struct SimpleCharSampler {
|
||||
len: f32,
|
||||
chars: String
|
||||
}
|
||||
|
||||
impl SimpleCharSampler {
|
||||
/// # Arguments
|
||||
/// * `chars`: The characters ordered by brightness.
|
||||
pub fn new(chars: String) -> Self {
|
||||
let len = chars.chars().count() as f32;
|
||||
|
||||
Self { chars, len }
|
||||
}
|
||||
}
|
||||
|
||||
impl CharSampler for SimpleCharSampler {
|
||||
fn sample(&self, level: f32) -> char {
|
||||
assert!(0.0 <= level && level < 1.0);
|
||||
|
||||
let index = level * self.len;
|
||||
|
||||
self.chars.chars().nth(index as usize).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sample() {
|
||||
let sampler = SimpleCharSampler::new("abc".to_string());
|
||||
|
||||
assert_eq!('a', sampler.sample(0.1));
|
||||
assert_eq!('b', sampler.sample(0.4));
|
||||
assert_eq!('c', sampler.sample(0.7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn sample_index_negative() {
|
||||
SimpleCharSampler::new("abc".to_string()).sample(-0.1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn sample_index_equals_one() {
|
||||
SimpleCharSampler::new("abc".to_string()).sample(1.0);
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
use rand::prelude::IteratorRandom;
|
||||
use rand::Rng;
|
||||
|
||||
/// A trait to get all values of an enum.
|
||||
pub trait Collection {
|
||||
/// Returns a list of all enum values.
|
||||
fn all() -> Vec<Self> where Self: Sized;
|
||||
}
|
||||
|
||||
/// Choose a enum from a list of options.
|
||||
pub struct Chooser<TRng> {
|
||||
rng: TRng
|
||||
}
|
||||
|
||||
impl<TRng: Rng> Chooser<TRng> {
|
||||
pub fn new(rng: TRng) -> Self {
|
||||
Self { rng }
|
||||
}
|
||||
|
||||
/// Choose an enum item from the provided [Vec].
|
||||
/// If none are provided, a random one of all enum values is chosen.
|
||||
pub fn choose<TValue: Collection>(&mut self, selection: Vec<TValue>) -> TValue {
|
||||
let options = if selection.is_empty() {
|
||||
TValue::all()
|
||||
} else {
|
||||
selection
|
||||
};
|
||||
options.into_iter().choose_stable(&mut self.rng).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rand::rngs::mock::StepRng;
|
||||
use crate::{Chooser, Collection};
|
||||
|
||||
enum MockOptions {
|
||||
First,
|
||||
Second,
|
||||
Third
|
||||
}
|
||||
|
||||
impl Collection for MockOptions {
|
||||
fn all() -> Vec<Self> where Self: Sized {
|
||||
use MockOptions::*;
|
||||
|
||||
vec![First, Second, Third]
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn choose() {
|
||||
let rng = StepRng::new(0, 1);
|
||||
let mut chooser = Chooser::new(rng);
|
||||
|
||||
assert!(matches!(chooser.choose(vec![MockOptions::First, MockOptions::Second]), MockOptions::Second));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn choose_empty() {
|
||||
let rng = StepRng::new(0, 1);
|
||||
let mut chooser = Chooser::new(rng);
|
||||
|
||||
assert!(matches!(chooser.choose(Vec::new()), MockOptions::Third));
|
||||
}
|
||||
}
|
61
src/color.rs
61
src/color.rs
@ -1,61 +0,0 @@
|
||||
use crossterm::style::Color;
|
||||
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
/// A collection of colors.
|
||||
#[cfg_attr(test, automock)]
|
||||
pub trait ColorSampler {
|
||||
/// Gets a color for the given fill.
|
||||
/// # Arguments
|
||||
/// * `fill`: `0 <= fill` and `fill < 1`
|
||||
fn sample(&self, fill: f32) -> Color;
|
||||
}
|
||||
|
||||
/// A simple color sampler which interpolates the color from a [Vec].
|
||||
pub struct SimpleColorSampler {
|
||||
values: Vec<Color>
|
||||
}
|
||||
|
||||
impl SimpleColorSampler {
|
||||
pub fn new(values: Vec<Color>) -> Self {
|
||||
Self { values }
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorSampler for SimpleColorSampler {
|
||||
fn sample(&self, fill: f32) -> Color {
|
||||
assert!(0.0 <= fill && fill < 1.0);
|
||||
|
||||
let index = self.values.len() as f32 * fill;
|
||||
|
||||
self.values[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crossterm::style::Color::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sample() {
|
||||
let sampler = SimpleColorSampler::new(vec![Red, Yellow, Green]);
|
||||
|
||||
assert_eq!(Red, sampler.sample(0.1));
|
||||
assert_eq!(Yellow, sampler.sample(0.4));
|
||||
assert_eq!(Green, sampler.sample(0.7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn sample_index_negative() {
|
||||
SimpleColorSampler::new(Vec::new()).sample(-0.1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn sample_index_equals_one() {
|
||||
SimpleColorSampler::new(Vec::new()).sample(1.0);
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
use crate::fill::FillMode;
|
||||
use crate::vec::Vector;
|
||||
|
||||
const INTERVAL: f32 = 4.0;
|
||||
|
||||
/// Fill based on rings of a circle.
|
||||
pub struct CircleFillMode {
|
||||
center: Vector,
|
||||
interval: f32
|
||||
}
|
||||
|
||||
impl CircleFillMode {
|
||||
pub fn new(size: Vector) -> Self {
|
||||
Self {
|
||||
center: size.center(),
|
||||
interval: size.smaller() / INTERVAL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FillMode for CircleFillMode {
|
||||
fn sample(&self, _: f32, pos: Vector) -> f32 {
|
||||
((pos - self.center).length() % self.interval) / self.interval
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sample() {
|
||||
let fill = CircleFillMode::new(Vector::new(10.0, 8.0));
|
||||
|
||||
let sample_1 = fill.sample(0.0, Vector::new(5.0, 3.0));
|
||||
let sample_2 = fill.sample(0.0, Vector::new(8.5, 4.0));
|
||||
|
||||
assert!(0.4 < sample_1 && sample_1 < 0.6);
|
||||
assert!(0.7 < sample_2 && sample_2 < 0.8);
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
use crate::fill::FillMode;
|
||||
use crate::vec::Vector;
|
||||
|
||||
/// Fill based on the level of brightness.
|
||||
pub struct LevelFillMode;
|
||||
|
||||
impl LevelFillMode {
|
||||
pub fn new() -> Self {
|
||||
Self { }
|
||||
}
|
||||
}
|
||||
|
||||
impl FillMode for LevelFillMode {
|
||||
fn sample(&self, level: f32, _: Vector) -> f32 {
|
||||
level
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sample() {
|
||||
let mode = LevelFillMode::new();
|
||||
|
||||
assert_eq!(0.3, mode.sample(0.3, Vector::new(0.0, 0.0)));
|
||||
assert_eq!(0.7, mode.sample(0.7, Vector::new(0.1, 0.2)));
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
pub mod level;
|
||||
pub mod circle;
|
||||
pub mod stripes;
|
||||
|
||||
use crate::vec::Vector;
|
||||
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
/// Used to choose the colors of characters.
|
||||
#[cfg_attr(test, automock)]
|
||||
pub trait FillMode {
|
||||
/// Gets the color for this character.
|
||||
/// # Arguments
|
||||
/// * `step`: `0 <= step` and `step <= 1`
|
||||
fn sample(&self, level: f32, pos: Vector) -> f32;
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
use crate::FillMode;
|
||||
use crate::vec::Vector;
|
||||
|
||||
const INTERVAL: f32 = 4.0;
|
||||
|
||||
/// Fill based on diagonal stripes.
|
||||
pub struct StripesFillMode {
|
||||
interval: f32
|
||||
}
|
||||
|
||||
impl StripesFillMode {
|
||||
pub fn new(size: Vector) -> Self {
|
||||
Self {
|
||||
interval: size.smaller() / INTERVAL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FillMode for StripesFillMode {
|
||||
fn sample(&self, _: f32, pos: Vector) -> f32 {
|
||||
(pos.sum() % self.interval) / self.interval
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sample() {
|
||||
let mode = StripesFillMode::new(Vector::new(8.0, 4.0));
|
||||
|
||||
assert_eq!(0.25, mode.sample(0.0, Vector::new(1.5, 0.75)));
|
||||
assert_eq!(0.5, mode.sample(0.0, Vector::new(4.0, 2.5)));
|
||||
}
|
||||
}
|
289
src/main.rs
289
src/main.rs
@ -1,288 +1,3 @@
|
||||
use std::io::stdout;
|
||||
use std::time::Duration;
|
||||
use anyhow::{anyhow, Error};
|
||||
use clap::Parser;
|
||||
use clap::ArgEnum;
|
||||
use rand::rngs::OsRng;
|
||||
use crate::animation::Animation;
|
||||
use crate::animation::circle::CircleAnimation;
|
||||
use crate::animation::rhombus::RhombusAnimation;
|
||||
use crate::animation::sonar::SonarAnimation;
|
||||
use crate::char::SimpleCharSampler;
|
||||
use crate::choose::{Chooser, Collection};
|
||||
use crate::color::{ColorSampler, SimpleColorSampler};
|
||||
use crate::fill::circle::CircleFillMode;
|
||||
use crate::fill::FillMode;
|
||||
use crate::fill::level::LevelFillMode;
|
||||
use crate::fill::stripes::StripesFillMode;
|
||||
use crate::render::{Renderer, SamplerRenderer};
|
||||
use crate::runner::Runner;
|
||||
use crate::sampler::ComposedSampler;
|
||||
use crate::surface::WriteSurface;
|
||||
use crate::timer::SimpleTimer;
|
||||
use crate::vec::Vector;
|
||||
|
||||
mod color;
|
||||
mod char;
|
||||
mod fill;
|
||||
mod vec;
|
||||
mod array;
|
||||
mod surface;
|
||||
mod animation;
|
||||
mod sampler;
|
||||
mod render;
|
||||
mod timer;
|
||||
mod runner;
|
||||
mod choose;
|
||||
|
||||
/// Defines an enum and implements the [Collection] trait.
|
||||
macro_rules! options {
|
||||
($name:ident { $($opt:ident,)* }) => {
|
||||
#[derive(Copy, Clone, ArgEnum)]
|
||||
enum $name {
|
||||
$($opt,)*
|
||||
}
|
||||
impl Collection for $name {
|
||||
fn all() -> Vec<Self> {
|
||||
vec![$($name::$opt,)*]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
options!(AnimationType {
|
||||
Circle,
|
||||
Rhombus,
|
||||
Sonar,
|
||||
});
|
||||
|
||||
options!(ColorType {
|
||||
Red,
|
||||
Green,
|
||||
Blue,
|
||||
LightRed,
|
||||
LightGreen,
|
||||
LightBlue,
|
||||
Grey,
|
||||
Rainbow,
|
||||
});
|
||||
|
||||
options!(FillModeType {
|
||||
Circle,
|
||||
Level,
|
||||
Stripes,
|
||||
});
|
||||
|
||||
const MAX_FPS: u64 = 480;
|
||||
|
||||
/// The program arguments.
|
||||
#[derive(Parser)]
|
||||
#[clap(author = env ! ("CARGO_PKG_AUTHORS"), version = env ! ("CARGO_PKG_VERSION"), about = env ! ("CARGO_PKG_DESCRIPTION"))]
|
||||
struct Args {
|
||||
#[clap(short, long, help = "Add animation", arg_enum)]
|
||||
animation: Vec<AnimationType>,
|
||||
#[clap(short, long, help = "Add fill mode", arg_enum)]
|
||||
fill: Vec<FillModeType>,
|
||||
#[clap(short, long, help = "Add color pallet", arg_enum)]
|
||||
color: Vec<ColorType>,
|
||||
#[clap(long, default_value = ".-+%#", parse(try_from_str = validate_chars), help = "Set chars")]
|
||||
chars: String,
|
||||
#[clap(long, default_value = "30", parse(try_from_str = validate_fps), help = "Set frames per second [max: 480]")]
|
||||
fps: u64,
|
||||
#[clap(long, default_value = "1000", parse(try_from_str = validate_duration), help = "Set duration [milliseconds]")]
|
||||
duration: u64,
|
||||
#[clap(long, help = "Set width [default: terminal width]")]
|
||||
width: Option<usize>,
|
||||
#[clap(long, help = "Set height [default: terminal height]")]
|
||||
height: Option<usize>,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let args = Args::parse();
|
||||
let mut chooser = Chooser::new(OsRng::default());
|
||||
|
||||
let (width, height) = size(crossterm::terminal::size()?, args.width, args.height);
|
||||
let size = Vector::from_terminal(width, height);
|
||||
let delay = delay_of_fps(args.fps);
|
||||
let duration = Duration::from_millis(args.duration);
|
||||
|
||||
let animation = create_animation(chooser.choose(args.animation), size);
|
||||
let fill = create_fill(chooser.choose(args.fill), size);
|
||||
let color = create_color(chooser.choose(args.color));
|
||||
let char = Box::new(SimpleCharSampler::new(args.chars));
|
||||
|
||||
let sampler = ComposedSampler::new(animation, fill, color, char);
|
||||
let surface = WriteSurface::new(stdout(), width, height)?;
|
||||
|
||||
let renderer = SamplerRenderer::new(surface, sampler);
|
||||
let timer = SimpleTimer::new(delay);
|
||||
let runner = Runner::new(duration, timer, renderer);
|
||||
|
||||
runner.run()
|
||||
}
|
||||
|
||||
/// Validates the chars argument.
|
||||
fn validate_chars(text: &str) -> Result<String, Error> {
|
||||
if text.is_empty() {
|
||||
Err(anyhow!("can't be empty."))
|
||||
} else {
|
||||
Ok(text.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates the fps argument.
|
||||
fn validate_fps(text: &str) -> Result<u64, Error> {
|
||||
let value = text.parse()?;
|
||||
|
||||
if value > MAX_FPS {
|
||||
Err(anyhow!("value is above limit of {}.", MAX_FPS))
|
||||
} else if value == 0 {
|
||||
Err(anyhow!("value is zero."))
|
||||
} else {
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates the duration argument.
|
||||
fn validate_duration(text: &str) -> Result<u64, Error> {
|
||||
let value = text.parse()?;
|
||||
|
||||
if value == 0 {
|
||||
Err(anyhow!("value is zero."))
|
||||
} else {
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the size to use based on the terminal size and width and height arguments.
|
||||
fn size(terminal: (u16, u16), width: Option<usize>, height: Option<usize>) -> (usize, usize) {
|
||||
let width = width.unwrap_or(terminal.0 as usize);
|
||||
let height = height.unwrap_or(terminal.1 as usize);
|
||||
(width, height)
|
||||
}
|
||||
|
||||
/// Calculates the delay between frames based on the fps.
|
||||
fn delay_of_fps(fps: u64) -> Duration {
|
||||
Duration::from_nanos(1_000_000_000 / fps)
|
||||
}
|
||||
|
||||
fn create_animation(animation: AnimationType, size: Vector) -> Box<dyn Animation> {
|
||||
match animation {
|
||||
AnimationType::Circle => Box::new(CircleAnimation::new(size)),
|
||||
AnimationType::Rhombus => Box::new(RhombusAnimation::new(size)),
|
||||
AnimationType::Sonar => Box::new(SonarAnimation::new(size)),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_fill(fill: FillModeType, size: Vector) -> Box<dyn FillMode> {
|
||||
match fill {
|
||||
FillModeType::Circle => Box::new(CircleFillMode::new(size)),
|
||||
FillModeType::Level => Box::new(LevelFillMode::new()),
|
||||
FillModeType::Stripes => Box::new(StripesFillMode::new(size))
|
||||
}
|
||||
}
|
||||
|
||||
fn create_color(color: ColorType) -> Box<dyn ColorSampler> {
|
||||
use crossterm::style::Color::*;
|
||||
|
||||
match color {
|
||||
ColorType::Red => Box::new(SimpleColorSampler::new(vec![Yellow, DarkYellow, Red])),
|
||||
ColorType::Green => Box::new(SimpleColorSampler::new(vec![Cyan, DarkGreen, Green])),
|
||||
ColorType::Blue => Box::new(SimpleColorSampler::new(vec![Magenta, DarkBlue, Blue])),
|
||||
ColorType::LightRed => Box::new(SimpleColorSampler::new(vec![White, Yellow, Red])),
|
||||
ColorType::LightGreen => Box::new(SimpleColorSampler::new(vec![White, Cyan, Green])),
|
||||
ColorType::LightBlue => Box::new(SimpleColorSampler::new(vec![White, Blue, Magenta])),
|
||||
ColorType::Grey => Box::new(SimpleColorSampler::new(vec![Black, Grey, White])),
|
||||
ColorType::Rainbow => Box::new(SimpleColorSampler::new(vec![Magenta, Blue, Green, Yellow, Red]))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn validate_chars_some_string() {
|
||||
assert_eq!("abc", &validate_chars("abc").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_chars_empty() {
|
||||
assert!(validate_chars("").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_fps_some_string() {
|
||||
assert_eq!(35, validate_fps("35").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_fps_zero() {
|
||||
assert!(validate_fps("0").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_fps_above_max() {
|
||||
assert!(validate_fps("500").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_duration_some_string() {
|
||||
assert_eq!(500, validate_duration("500").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_duration_zero() {
|
||||
assert!(validate_duration("0").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_not_set() {
|
||||
assert_eq!((4, 6), size((4, 6), None, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_width_set() {
|
||||
assert_eq!((8, 3), size((2, 3), Some(8), None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_height_set() {
|
||||
assert_eq!((1, 6), size((1, 7), None, Some(6)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delay_of_fps_some_number() {
|
||||
assert_eq!(Duration::from_nanos(10_526_315), delay_of_fps(95));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_animation_all_implemented() {
|
||||
let size = Vector::new(0.0, 0.0);
|
||||
|
||||
create_animation(AnimationType::Circle, size);
|
||||
create_animation(AnimationType::Rhombus, size);
|
||||
create_animation(AnimationType::Sonar, size);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_fill_all_implemented() {
|
||||
let size = Vector::new(0.0, 0.0);
|
||||
|
||||
create_fill(FillModeType::Circle, size);
|
||||
create_fill(FillModeType::Level, size);
|
||||
create_fill(FillModeType::Stripes, size);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_color_all_implemented() {
|
||||
create_color(ColorType::Red);
|
||||
create_color(ColorType::Green);
|
||||
create_color(ColorType::Blue);
|
||||
create_color(ColorType::LightRed);
|
||||
create_color(ColorType::LightGreen);
|
||||
create_color(ColorType::LightBlue);
|
||||
create_color(ColorType::Grey);
|
||||
create_color(ColorType::Rainbow);
|
||||
}
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
use anyhow::Error;
|
||||
use crate::sampler::{Sample, Sampler};
|
||||
use crate::surface::Surface;
|
||||
use crate::Vector;
|
||||
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
/// A trait for anything which performs some rendering.
|
||||
#[cfg_attr(test, automock)]
|
||||
pub trait Renderer {
|
||||
/// Render the frame.
|
||||
fn render(&mut self, step: f32);
|
||||
|
||||
/// Present the finished frame.
|
||||
fn present(&mut self) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Fills its [Surface] with the values received from a [Sampler].
|
||||
pub struct SamplerRenderer<TSurface, TSampler> {
|
||||
surface: TSurface,
|
||||
sampler: TSampler,
|
||||
}
|
||||
|
||||
impl<T1, T2> SamplerRenderer<T1, T2> {
|
||||
pub fn new(surface: T1, sampler: T2) -> Self {
|
||||
Self { surface, sampler }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T1: Surface, T2: Sampler> Renderer for SamplerRenderer<T1, T2> {
|
||||
fn render(&mut self, step: f32) {
|
||||
for x in 0..self.surface.width() {
|
||||
for y in 0..self.surface.height() {
|
||||
let pos = Vector::from_terminal(x, y);
|
||||
let sample = self.sampler.sample(step, pos);
|
||||
|
||||
match sample {
|
||||
Sample::Keep => (),
|
||||
Sample::Draw { char, color } => self.surface.draw(x, y, char, color),
|
||||
Sample::Clear => self.surface.clear(x, y),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn present(&mut self) -> Result<(), Error> {
|
||||
self.surface.present()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crossterm::style::*;
|
||||
use mockall::predicate::*;
|
||||
use super::*;
|
||||
use crate::surface::MockSurface;
|
||||
use crate::sampler::MockSampler;
|
||||
|
||||
#[test]
|
||||
fn render() {
|
||||
let mut surface = MockSurface::new();
|
||||
let mut sampler = MockSampler::new();
|
||||
|
||||
sampler.expect_sample().withf(|_, pos| pos.x == 0.0 && pos.y == 0.0).returning(|_,_| Sample::Clear);
|
||||
sampler.expect_sample().withf(|_, pos| pos.x == 1.0 && pos.y == 0.0).returning(|_,_| Sample::Keep);
|
||||
sampler.expect_sample().withf(|_, pos| pos.x == 0.0 && pos.y == 2.0).returning(|_,_| Sample::Draw { char: 'a', color: Color::Red });
|
||||
sampler.expect_sample().withf(|_, pos| pos.x == 1.0 && pos.y == 2.0).returning(|_,_| Sample::Keep);
|
||||
sampler.expect_sample().withf(|_, pos| pos.x == 0.0 && pos.y == 4.0).returning(|_,_| Sample::Draw { char: 'x', color: Color::Yellow });
|
||||
sampler.expect_sample().withf(|_, pos| pos.x == 1.0 && pos.y == 4.0).returning(|_,_| Sample::Clear);
|
||||
|
||||
surface.expect_width().return_const(2 as usize);
|
||||
surface.expect_height().return_const(3 as usize);
|
||||
surface.expect_clear().once().with(eq(0), eq(0)).return_const(());
|
||||
surface.expect_draw().once().with(eq(0), eq(1), eq('a'), eq(Color::Red)).return_const(());
|
||||
surface.expect_draw().once().with(eq(0), eq(2), eq('x'), eq(Color::Yellow)).return_const(());
|
||||
surface.expect_clear().once().with(eq(1), eq(2)).return_const(());
|
||||
|
||||
let mut renderer = SamplerRenderer::new(surface, sampler);
|
||||
|
||||
renderer.render(0.5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn present() {
|
||||
let mut surface = MockSurface::new();
|
||||
let sampler = MockSampler::new();
|
||||
|
||||
surface.expect_present().once().returning(|| Ok(()));
|
||||
|
||||
let mut renderer = SamplerRenderer::new(surface, sampler);
|
||||
|
||||
renderer.present().unwrap();
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
use std::time::Duration;
|
||||
use anyhow::Error;
|
||||
use crate::Renderer;
|
||||
use crate::timer::Timer;
|
||||
|
||||
/// Periodically calls [Renderer::render] and [Renderer::present].
|
||||
pub struct Runner<TTimer, TRenderer> {
|
||||
duration: Duration,
|
||||
timer: TTimer,
|
||||
renderer: TRenderer,
|
||||
}
|
||||
|
||||
impl<T1: Timer, T2: Renderer> Runner<T1, T2> {
|
||||
pub fn new(duration: Duration,
|
||||
timer: T1,
|
||||
renderer: T2) -> Self {
|
||||
|
||||
Self { duration, timer, renderer }
|
||||
}
|
||||
|
||||
pub fn run(mut self) -> Result<(), Error> {
|
||||
self.timer.set();
|
||||
|
||||
while self.timer.elapsed() < self.duration {
|
||||
let step = self.timer.elapsed().as_secs_f32() / self.duration.as_secs_f32();
|
||||
|
||||
self.renderer.render(step);
|
||||
self.renderer.present()?;
|
||||
self.timer.sleep();
|
||||
}
|
||||
self.renderer.render(1.0);
|
||||
self.renderer.present()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::time::Duration;
|
||||
use mockall::predicate::*;
|
||||
use mockall::Sequence;
|
||||
use crate::timer::MockTimer;
|
||||
use crate::render::MockRenderer;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn run() {
|
||||
let mut timer = MockTimer::new();
|
||||
let mut renderer = MockRenderer::new();
|
||||
let seq = &mut Sequence::new();
|
||||
|
||||
timer.expect_set().once().in_sequence(seq).return_const(());
|
||||
|
||||
timer.expect_elapsed().times(2).in_sequence(seq).return_const(Duration::from_secs(0));
|
||||
renderer.expect_render().once().with(eq(0.0)).in_sequence(seq).return_const(());
|
||||
renderer.expect_present().once().in_sequence(seq).returning(|| Ok(()));
|
||||
timer.expect_sleep().once().in_sequence(seq).return_const(());
|
||||
|
||||
timer.expect_elapsed().times(2).in_sequence(seq).return_const(Duration::from_secs(2));
|
||||
renderer.expect_render().once().with(eq(0.5)).in_sequence(seq).return_const(());
|
||||
renderer.expect_present().once().in_sequence(seq).returning(|| Ok(()));
|
||||
timer.expect_sleep().once().in_sequence(seq).return_const(());
|
||||
|
||||
timer.expect_elapsed().times(1).in_sequence(seq).return_const(Duration::from_secs(4));
|
||||
renderer.expect_render().once().with(eq(1.0)).in_sequence(seq).return_const(());
|
||||
renderer.expect_present().once().in_sequence(seq).returning(|| Ok(()));
|
||||
|
||||
let runner = Runner::new(Duration::from_secs(4), timer, renderer);
|
||||
|
||||
runner.run().unwrap();
|
||||
}
|
||||
}
|
147
src/sampler.rs
147
src/sampler.rs
@ -1,147 +0,0 @@
|
||||
use crossterm::style::Color;
|
||||
use crate::animation::Animation;
|
||||
use crate::char::CharSampler;
|
||||
use crate::color::ColorSampler;
|
||||
use crate::fill::FillMode;
|
||||
use crate::vec::Vector;
|
||||
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
/// The action to perform for the given values.
|
||||
pub enum Sample {
|
||||
Keep,
|
||||
Draw { char: char, color: Color },
|
||||
Clear,
|
||||
}
|
||||
|
||||
/// Provides a [Sample] for some values.
|
||||
#[cfg_attr(test, automock)]
|
||||
pub trait Sampler {
|
||||
/// Get a [Sample] for the step of the animation and position on screen.
|
||||
/// # Arguments
|
||||
/// * `step`: `0 <= step` and `step <= 1`
|
||||
fn sample(&self, step: f32, pos: Vector) -> Sample;
|
||||
}
|
||||
|
||||
/// Links primitive samplers into a full [Sampler].
|
||||
pub struct ComposedSampler {
|
||||
animation: Box<dyn Animation>,
|
||||
fill: Box<dyn FillMode>,
|
||||
color: Box<dyn ColorSampler>,
|
||||
char: Box<dyn CharSampler>,
|
||||
}
|
||||
|
||||
impl ComposedSampler {
|
||||
pub fn new(animation: Box<dyn Animation>,
|
||||
fill: Box<dyn FillMode>,
|
||||
color: Box<dyn ColorSampler>,
|
||||
char: Box<dyn CharSampler>) -> Self {
|
||||
Self { animation, fill, color, char }
|
||||
}
|
||||
}
|
||||
|
||||
impl Sampler for ComposedSampler {
|
||||
fn sample(&self, step: f32, pos: Vector) -> Sample {
|
||||
let level = self.animation.sample(step, pos);
|
||||
|
||||
if level >= 1.0 {
|
||||
Sample::Keep
|
||||
} else if level >= 0.0 {
|
||||
let char = self.char.sample(level);
|
||||
let fill = self.fill.sample(level, pos);
|
||||
let color = self.color.sample(fill);
|
||||
|
||||
Sample::Draw { char, color }
|
||||
} else {
|
||||
Sample::Clear
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use mockall::predicate::{always, eq};
|
||||
use super::*;
|
||||
use crate::animation::MockAnimation;
|
||||
use crate::fill::MockFillMode;
|
||||
use crate::color::MockColorSampler;
|
||||
use crate::char::MockCharSampler;
|
||||
|
||||
#[test]
|
||||
fn sample_keep() {
|
||||
let mut anim = Box::new(MockAnimation::new());
|
||||
let fill = Box::new(MockFillMode::new());
|
||||
let color = Box::new(MockColorSampler::new());
|
||||
let char = Box::new(MockCharSampler::new());
|
||||
|
||||
anim.expect_sample().return_const(3.0);
|
||||
|
||||
let sampler = ComposedSampler::new(anim, fill, color, char);
|
||||
|
||||
assert!(matches!(sampler.sample(0.7, Vector::new(0.3, 0.1)), Sample::Keep));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_draw() {
|
||||
let mut anim = Box::new(MockAnimation::new());
|
||||
let mut fill = Box::new(MockFillMode::new());
|
||||
let mut color = Box::new(MockColorSampler::new());
|
||||
let mut char = Box::new(MockCharSampler::new());
|
||||
|
||||
anim.expect_sample().once().with(eq(0.2), always()).return_const(0.3);
|
||||
fill.expect_sample().once().with(eq(0.3), always()).return_const(0.8);
|
||||
color.expect_sample().once().with(eq(0.8)).return_const(Color::Blue);
|
||||
char.expect_sample().once().with(eq(0.3)).return_const('Z');
|
||||
|
||||
let sampler = ComposedSampler::new(anim, fill, color, char);
|
||||
|
||||
assert!(matches!(sampler.sample(0.2, Vector::new(0.3, 0.1)), Sample::Draw { char: 'Z', color: Color::Blue }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_clear() {
|
||||
let mut anim = Box::new(MockAnimation::new());
|
||||
let fill = Box::new(MockFillMode::new());
|
||||
let color = Box::new(MockColorSampler::new());
|
||||
let char = Box::new(MockCharSampler::new());
|
||||
|
||||
anim.expect_sample().return_const(-0.4);
|
||||
|
||||
let sampler = ComposedSampler::new(anim, fill, color, char);
|
||||
|
||||
assert!(matches!(sampler.sample(0.7, Vector::new(0.3, 0.1)), Sample::Clear));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_almost_draw() {
|
||||
let mut anim = Box::new(MockAnimation::new());
|
||||
let fill = Box::new(MockFillMode::new());
|
||||
let color = Box::new(MockColorSampler::new());
|
||||
let char = Box::new(MockCharSampler::new());
|
||||
|
||||
anim.expect_sample().return_const(1.0);
|
||||
|
||||
let sampler = ComposedSampler::new(anim, fill, color, char);
|
||||
|
||||
assert!(matches!(sampler.sample(0.7, Vector::new(0.3, 0.1)), Sample::Keep));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn sample_almost_clear() {
|
||||
let mut anim = Box::new(MockAnimation::new());
|
||||
let mut fill = Box::new(MockFillMode::new());
|
||||
let mut color = Box::new(MockColorSampler::new());
|
||||
let mut char = Box::new(MockCharSampler::new());
|
||||
|
||||
anim.expect_sample().return_const(0.0);
|
||||
fill.expect_sample().return_const(0.8);
|
||||
color.expect_sample().return_const(Color::Blue);
|
||||
char.expect_sample().return_const('a');
|
||||
|
||||
let sampler = ComposedSampler::new(anim, fill, color, char);
|
||||
|
||||
assert!(matches!(sampler.sample(0.7, Vector::new(0.3, 0.1)), Sample::Draw { .. }));
|
||||
}
|
||||
}
|
214
src/surface.rs
214
src/surface.rs
@ -1,214 +0,0 @@
|
||||
use anyhow::Error;
|
||||
use crossterm::cursor::{Hide, MoveTo, Show};
|
||||
use crossterm::{ExecutableCommand, QueueableCommand};
|
||||
use crossterm::style::{Color, Print, SetForegroundColor};
|
||||
use crossterm::terminal::{Clear, ClearType};
|
||||
use std::io::Write;
|
||||
use crate::array::Array2D;
|
||||
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
/// A surface to draw characters on.
|
||||
#[cfg_attr(test, automock)]
|
||||
pub trait Surface {
|
||||
fn width(&self) -> usize;
|
||||
fn height(&self) -> usize;
|
||||
|
||||
/// Overwrite the character on screen with this value.
|
||||
fn draw(&mut self, x: usize, y: usize, char: char, color: Color);
|
||||
|
||||
/// Clear the character on screen.
|
||||
fn clear(&mut self, x: usize, y: usize);
|
||||
|
||||
/// Present the finished frame.
|
||||
fn present(&mut self) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Renders the frames into a [Write] struct.
|
||||
pub struct WriteSurface<T: Write> {
|
||||
out: T,
|
||||
array: Array2D<Cell>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Cell {
|
||||
Keep,
|
||||
Draw { char: char, color: Color },
|
||||
}
|
||||
|
||||
impl Default for Cell {
|
||||
fn default() -> Self { Cell::Keep }
|
||||
}
|
||||
|
||||
impl<T: Write> WriteSurface<T> {
|
||||
pub fn new(mut out: T, width: usize, height: usize) -> Result<Self, Error> {
|
||||
out.queue(Hide)?;
|
||||
|
||||
Ok(Self {
|
||||
out,
|
||||
array: Array2D::new(width, height)
|
||||
})
|
||||
}
|
||||
|
||||
fn try_drop(&mut self) -> Result<(), Error> {
|
||||
self.out.queue(Show)?;
|
||||
self.out.execute(Clear(ClearType::Purge))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Write> Surface for WriteSurface<T> {
|
||||
fn width(&self) -> usize {
|
||||
self.array.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> usize {
|
||||
self.array.height()
|
||||
}
|
||||
|
||||
fn draw(&mut self, x: usize, y: usize, char: char, color: Color) {
|
||||
self.array[(x, y)] = Cell::Draw { char, color };
|
||||
}
|
||||
|
||||
fn clear(&mut self, x: usize, y: usize) {
|
||||
self.array[(x, y)] = Cell::Draw { char: ' ', color: Color::Reset };
|
||||
}
|
||||
|
||||
fn present(&mut self) -> Result<(), Error> {
|
||||
let mut needs_move;
|
||||
let mut last_color = None;
|
||||
|
||||
for y in 0..self.array.height() {
|
||||
needs_move = true;
|
||||
|
||||
for x in 0..self.array.width() {
|
||||
match self.array[(x, y)] {
|
||||
Cell::Keep => {
|
||||
needs_move = true;
|
||||
}
|
||||
Cell::Draw { char, color } => {
|
||||
if needs_move {
|
||||
needs_move = false;
|
||||
self.out.queue(MoveTo(x as u16, y as u16))?;
|
||||
}
|
||||
if last_color.is_none() || last_color.unwrap() != color {
|
||||
last_color = Some(color);
|
||||
self.out.queue(SetForegroundColor(color))?;
|
||||
}
|
||||
self.out.queue(Print(char))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.out.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Write> Drop for WriteSurface<T> {
|
||||
fn drop(&mut self) {
|
||||
if let Err(e) = self.try_drop() {
|
||||
println!("{}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use super::*;
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct Data {
|
||||
flushed: Vec<Vec<u8>>,
|
||||
buffer: Vec<u8>
|
||||
}
|
||||
|
||||
struct MockWrite {
|
||||
data: Rc<RefCell<Data>>
|
||||
}
|
||||
|
||||
impl Data {
|
||||
fn new() -> Rc<RefCell<Self>> {
|
||||
Rc::new(RefCell::new(Self {
|
||||
flushed: Vec::new(),
|
||||
buffer: Vec::new()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl MockWrite {
|
||||
fn new(data: Rc<RefCell<Data>>) -> Box<dyn Write> {
|
||||
Box::new(Self { data })
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for MockWrite {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.data.borrow_mut().buffer.extend_from_slice(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
let data = self.data.borrow_mut().buffer.drain(..).collect();
|
||||
|
||||
self.data.borrow_mut().flushed.push(data);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn width() {
|
||||
let data = Data::new();
|
||||
let mock = MockWrite::new(data);
|
||||
let surface = WriteSurface::new(mock, 10, 2).unwrap();
|
||||
|
||||
assert_eq!(10, surface.width());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn height() {
|
||||
let data = Data::new();
|
||||
let mock = MockWrite::new(data);
|
||||
let surface = WriteSurface::new(mock, 5, 8).unwrap();
|
||||
|
||||
assert_eq!(8, surface.height());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn present() {
|
||||
// Execute
|
||||
let data = Data::new();
|
||||
let mock = MockWrite::new(data.clone());
|
||||
let mut surface = WriteSurface::new(mock, 3, 2).unwrap();
|
||||
|
||||
surface.draw(0, 0, 'A', Color::Green);
|
||||
surface.draw(1, 0, 'x', Color::Green);
|
||||
surface.clear(1, 1);
|
||||
surface.present().unwrap();
|
||||
|
||||
drop(surface);
|
||||
|
||||
// Recreate expectation
|
||||
let expected = Data::new();
|
||||
let mut stream = MockWrite::new(expected.clone());
|
||||
|
||||
stream.queue(Hide).unwrap();
|
||||
stream.queue(MoveTo(0, 0)).unwrap();
|
||||
stream.queue(SetForegroundColor(Color::Green)).unwrap();
|
||||
stream.queue(Print('A')).unwrap();
|
||||
stream.queue(Print('x')).unwrap();
|
||||
stream.queue(MoveTo(1, 1)).unwrap();
|
||||
stream.queue(SetForegroundColor(Color::Reset)).unwrap();
|
||||
stream.queue(Print(' ')).unwrap();
|
||||
stream.flush().unwrap();
|
||||
stream.queue(Show).unwrap();
|
||||
stream.queue(Clear(ClearType::Purge)).unwrap();
|
||||
stream.flush().unwrap();
|
||||
|
||||
// Compare
|
||||
assert_eq!(expected, data);
|
||||
}
|
||||
}
|
55
src/timer.rs
55
src/timer.rs
@ -1,55 +0,0 @@
|
||||
use std::thread::sleep;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
/// Allows for periodic execution of code.
|
||||
#[cfg_attr(test, automock)]
|
||||
pub trait Timer {
|
||||
/// Set the start time of the timer.
|
||||
fn set(&mut self);
|
||||
|
||||
/// Get the elapsed time since calling [Timer::set].
|
||||
fn elapsed(&self) -> Duration;
|
||||
|
||||
/// Sleep until the next tick starts.
|
||||
fn sleep(&mut self);
|
||||
}
|
||||
|
||||
/// A simple [Timer] based on the system clock.
|
||||
pub struct SimpleTimer {
|
||||
delay: Duration,
|
||||
start: Instant,
|
||||
last: Instant
|
||||
}
|
||||
|
||||
impl SimpleTimer {
|
||||
pub fn new(delay: Duration) -> Self {
|
||||
Self {
|
||||
delay,
|
||||
start: Instant::now(),
|
||||
last: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Timer for SimpleTimer {
|
||||
fn set(&mut self) {
|
||||
self.start = Instant::now();
|
||||
self.last = self.start;
|
||||
}
|
||||
|
||||
fn elapsed(&self) -> Duration {
|
||||
Instant::now() - self.start
|
||||
}
|
||||
|
||||
fn sleep(&mut self) {
|
||||
let now = Instant::now();
|
||||
|
||||
if self.last + self.delay > now {
|
||||
sleep(self.delay - (now - self.last));
|
||||
}
|
||||
self.last = Instant::now();
|
||||
}
|
||||
}
|
119
src/vec.rs
119
src/vec.rs
@ -1,119 +0,0 @@
|
||||
use std::ops::Sub;
|
||||
|
||||
/// A vector with a x and y axis.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Vector {
|
||||
pub x: f32,
|
||||
pub y: f32
|
||||
}
|
||||
|
||||
impl Vector {
|
||||
pub fn new(x: f32, y: f32) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
/// Returns the halfway point.
|
||||
pub fn center(self) -> Self {
|
||||
Self::new(self.x / 2.0, self.y / 2.0)
|
||||
}
|
||||
|
||||
/// Returns the length.
|
||||
pub fn length(self) -> f32 {
|
||||
(self.x * self.x + self.y * self.y).sqrt()
|
||||
}
|
||||
|
||||
/// Returns the angle.
|
||||
pub fn angle(self) -> f32 {
|
||||
self.y.atan2(self.x)
|
||||
}
|
||||
|
||||
/// Returns the value of the smaller axis.
|
||||
pub fn smaller(self) -> f32 {
|
||||
self.x.min(self.y)
|
||||
}
|
||||
|
||||
/// Converts all axis into positive values.
|
||||
pub fn abs(self) -> Vector {
|
||||
Self::new(self.x.abs(), self.y.abs())
|
||||
}
|
||||
|
||||
/// Returns the sum of all axis.
|
||||
pub fn sum(self) -> f32 {
|
||||
self.x + self.y
|
||||
}
|
||||
|
||||
/// Creates a vector with the on screen coordinates based on the terminal coordinates.
|
||||
/// # Arguments
|
||||
/// * `x`: The x axis of the terminal character.
|
||||
/// * `y`: The y axis of the terminal character.
|
||||
pub fn from_terminal(x: usize, y: usize) -> Self {
|
||||
Self::new(x as f32, y as f32 * 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Vector {
|
||||
type Output = Vector;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Vector::new(self.x - rhs.x, self.y - rhs.y)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn new() {
|
||||
let vec = Vector::new(3.0, 5.0);
|
||||
|
||||
assert_eq!(3.0, vec.x);
|
||||
assert_eq!(5.0, vec.y);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn center() {
|
||||
let vec = Vector::new(3.0, 8.0);
|
||||
|
||||
assert_eq!(1.5, vec.center().x);
|
||||
assert_eq!(4.0, vec.center().y);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn length() {
|
||||
let vec = Vector::new(3.0, 6.0);
|
||||
|
||||
assert!(6.7 < vec.length() && vec.length() < 6.8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn angle() {
|
||||
let vec = Vector::new(3.0, 6.0);
|
||||
|
||||
assert!(1.1 < vec.angle() && vec.angle() < 1.2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smaller() {
|
||||
assert_eq!(4.0, Vector::new(7.0, 4.0).smaller());
|
||||
assert_eq!(2.0, Vector::new(2.0, 9.0).smaller());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_terminal() {
|
||||
let vec = Vector::from_terminal(2, 4);
|
||||
|
||||
assert_eq!(2.0, vec.x);
|
||||
assert_eq!(8.0, vec.y);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sub() {
|
||||
let left = Vector::new(8.0, 15.0);
|
||||
let right = Vector::new(2.0, 4.0);
|
||||
let result = left - right;
|
||||
|
||||
assert_eq!(6.0, result.x);
|
||||
assert_eq!(11.0, result.y);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user