Reset repository

This commit is contained in:
Rico Riedel 2022-07-24 10:22:40 +02:00
parent 7500e5af1b
commit a92cf27664
No known key found for this signature in database
GPG Key ID: 75AC868575DE7B18
28 changed files with 5 additions and 2371 deletions

633
Cargo.lock generated
View File

@ -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"

View File

@ -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"
authors = ["Rico Riedel"]

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 KiB

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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)];
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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)));
}
}

View File

@ -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;
}

View File

@ -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)));
}
}

View File

@ -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,)*]
}
}
}
fn main() {
println!("Hello, world!");
}
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);
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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 { .. }));
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}