First commit

This commit is contained in:
zuma 2025-07-01 16:42:46 +02:00
commit 718b4c49ff
12 changed files with 7007 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
debug
target

578
Cargo.lock generated Normal file
View file

@ -0,0 +1,578 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "convert_case"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "crossterm"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
dependencies = [
"bitflags",
"crossterm_winapi",
"derive_more",
"document-features",
"mio",
"parking_lot",
"rustix",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
dependencies = [
"winapi",
]
[[package]]
name = "derive_more"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "document-features"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
dependencies = [
"litrs",
]
[[package]]
name = "errno"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [
"libc",
"windows-sys 0.60.2",
]
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
]
[[package]]
name = "include-lines"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be037b494f74d7bded0680228b3133bc268cbe50282f51c25fa46b890cfedb9"
dependencies = [
"include-lines-proc",
]
[[package]]
name = "include-lines-proc"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd1ff2792810c5bb1b591ae8ac768bda8b0238a3b61cb546d062709f2a6a4f77"
dependencies = [
"quote",
"syn 1.0.109",
]
[[package]]
name = "libc"
version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "litrs"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
[[package]]
name = "lock_api"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "mio"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"log",
"wasi 0.11.1+wasi-snapshot-preview1",
"windows-sys 0.59.0",
]
[[package]]
name = "parking_lot"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets 0.52.6",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom",
]
[[package]]
name = "redox_syscall"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
dependencies = [
"bitflags",
]
[[package]]
name = "rustix"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.59.0",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "signal-hook"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tapote"
version = "0.1.0"
dependencies = [
"crossterm",
"include-lines",
"rand",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-segmentation"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[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-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.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.2",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
dependencies = [
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
"windows_i686_gnullvm 0.53.0",
"windows_i686_msvc 0.53.0",
"windows_x86_64_gnu 0.53.0",
"windows_x86_64_gnullvm 0.53.0",
"windows_x86_64_msvc 0.53.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]
[[package]]
name = "zerocopy"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]

9
Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "tapote"
version = "0.1.0"
edition = "2024"
[dependencies]
crossterm = "0.29.0"
include-lines = "1.1.2"
rand = "0.9.1"

17
README.md Normal file
View file

@ -0,0 +1,17 @@
![Gif d'utilisation de tapote](assets/tapote.gif)
# Tapote
Mon premier logiciel pour apprendre à coder en Rust. Il permet de tester ma rapidité au clavier !
La base du code est largement inspirée de ce tutoriel [Hecto.rs](https://philippflenker.com/hecto/).
# Instruction pour compiler
Afin de compiler le logiciel il vous faudra cargo, rustc et clippy sur votre système. Une fois installés vous pouvez compiler un executable avec :
```rust
cargo build
```
Et pour faire une version release il suffit d'ajouter le paramètre à la fin :
```rust
cargo build --release
```

BIN
assets/tapote.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

1000
data/words_english.txt Normal file

File diff suppressed because it is too large Load diff

5000
data/words_french.txt Normal file

File diff suppressed because it is too large Load diff

8
src/main.rs Normal file
View file

@ -0,0 +1,8 @@
#![warn(clippy::all, clippy::pedantic, clippy::print_stdout)]
mod typer;
use typer::Typer;
fn main() {
Typer::default().run();
}

132
src/typer.rs Normal file
View file

@ -0,0 +1,132 @@
use crossterm::event::{read, Event, Event::Key, KeyCode::{Char, Backspace}, KeyEvent, KeyModifiers};
mod terminal;
use terminal::{Terminal, Position};
mod view;
use view::{View, TyperInfo, ResultInfo};
mod words;
use words::WordList;
use std::io::Error;
use std::time::{SystemTime};
pub struct Typer {
should_quit: bool,
should_display_result: bool,
words_to_write: WordList,
chars_written: Vec<char>,
current_num_of_words: u16,
wpm: u16,
errors: u16,
start_timestamp: Option<SystemTime>,
}
impl Typer {
pub fn default() -> Self {
Typer{
should_quit: false,
should_display_result: false,
words_to_write: WordList::new(50),
chars_written: Vec::new() as Vec<char>,
current_num_of_words: 0,
wpm: 0,
errors: 0,
start_timestamp: None
}
}
pub fn run(&mut self) {
Terminal::initialize().unwrap();
let result = self.repl();
Terminal::terminate().unwrap();
result.unwrap();
}
fn repl(&mut self) -> Result<(), Error> {
loop {
self.refresh_screen()?;
if self.should_quit {
break;
}
let event = read()?;
self.evaluate_event(&event);
}
Ok(())
}
fn evaluate_event(&mut self, event: &Event) {
if let Key(KeyEvent { code, modifiers, .. }) = event {
match code {
Char('q') if *modifiers == KeyModifiers::CONTROL => {
self.should_quit = true;
}
Backspace => {
if self.chars_written.pop().unwrap() == ' ' && self.current_num_of_words > 0 {
self.current_num_of_words -= 1;
}
}
_ => {
if code.as_char().is_some() {
if self.chars_written.len() < self.words_to_write.chars.len() - 2 {
// Start the timer for WPM
if self.start_timestamp.is_none() {
self.start_timestamp = Some(SystemTime::now());
}
let c = code.as_char().unwrap();
// Add an error if the character written is not the correct.
if c != *self.words_to_write.chars.get(self.chars_written.len()).unwrap() {
self.errors += 1;
}
// Increase the number of written words on space.
if c == ' ' {
self.current_num_of_words += 1;
}
// Update WPM
match self.start_timestamp.unwrap().elapsed() {
Ok(elapsed) => {
if elapsed.as_secs() != 0 {
self.wpm = (f32::from(self.current_num_of_words) / (elapsed.as_secs() as f32 / 60_f32)) as u16;
}
}
Err(_e) => {
panic!("Problem with the timer");
}
}
self.chars_written.push(c);
} else {
self.should_display_result = true;
}
}
},
}
}
}
fn refresh_screen(&mut self) -> Result<(), Error> {
if self.should_quit {
Terminal::clear_screen()?;
Terminal::print("Goodbye.\r\n")?;
} else if self.should_display_result {
Terminal::clear_screen()?;
View::render_result(ResultInfo {
wpm: self.wpm,
errors: self.errors,
accuracy: 100_f32 * (1_f32 - (self.errors as f32 / self.words_to_write.chars.len() as f32)),
})?;
} else {
View::render_typer(TyperInfo {
words_to_write: self.words_to_write.words.clone(),
words_written: self.chars_written.clone(),
wpm: self.wpm,
errors: self.errors
})?;
Terminal::move_cursor_to(Position {x: 0, y: 0})?;
}
Terminal::execute()?;
Ok(())
}
}

95
src/typer/terminal.rs Normal file
View file

@ -0,0 +1,95 @@
use crossterm::cursor::{Hide, Show, MoveTo};
use crossterm::queue;
use crossterm::style::{Print, ResetColor, SetForegroundColor, Color};
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, size, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen};
use std::io::{stdout, Error, Write};
#[derive(Copy, Clone)]
pub struct Size {
pub height: u16,
pub width: u16,
}
#[derive(Copy, Clone)]
pub struct Position {
pub x: u16,
pub y: u16,
}
pub struct Terminal;
impl Terminal {
pub fn terminate() -> Result<(), Error> {
Self::show_cursor()?;
Self::execute()?;
Self::leave_alternate_screen()?;
disable_raw_mode()?;
Ok(())
}
pub fn initialize() -> Result<(), Error> {
enable_raw_mode()?;
Self::enter_alternate_screen()?;
Self::clear_screen()?;
Self::move_cursor_to(Position {x: 0, y: 0})?;
Self::hide_cursor()?;
Self::execute()?;
Ok(())
}
pub fn leave_alternate_screen() -> Result<(), Error> {
queue!(stdout(), LeaveAlternateScreen)?;
Ok(())
}
pub fn enter_alternate_screen() -> Result<(), Error> {
queue!(stdout(), EnterAlternateScreen)?;
Ok(())
}
pub fn clear_screen() -> Result<(), Error> {
queue!(stdout(), Clear(ClearType::All))?;
Ok(())
}
pub fn reset_color() -> Result<(), Error> {
queue!(stdout(), ResetColor)?;
Ok(())
}
pub fn set_foreground_color(color: Color) -> Result<(), Error> {
queue!(stdout(), SetForegroundColor(color))?;
Ok(())
}
pub fn move_cursor_to(position: Position) -> Result<(), Error> {
queue!(stdout(), MoveTo(position.x,position.y))?;
Ok(())
}
pub fn hide_cursor() -> Result<(), Error> {
queue!(stdout(), Hide)?;
Ok(())
}
pub fn show_cursor() -> Result<(), Error> {
queue!(stdout(), Show)?;
Ok(())
}
pub fn print(string: &str) -> Result<(), Error> {
queue!(stdout(), Print(string))?;
Ok(())
}
pub fn size() -> Result<Size, Error> {
let (width, height) = size()?;
Ok(Size { height, width })
}
pub fn execute() -> Result<(), Error> {
stdout().flush()?;
Ok(())
}
}

128
src/typer/view.rs Normal file
View file

@ -0,0 +1,128 @@
use super::terminal::{Size, Position, Terminal};
use crossterm::style::Color;
use std::io::Error;
const NAME: &str = env!("CARGO_PKG_NAME");
const VERSION: &str = env!("CARGO_PKG_VERSION");
pub struct TyperInfo {
pub words_to_write: Vec<String>,
pub words_written: Vec<char>,
pub wpm: u16,
pub errors: u16,
}
pub struct ResultInfo {
pub wpm: u16,
pub errors: u16,
pub accuracy: f32,
}
pub struct View;
impl View {
pub fn render_typer(info: TyperInfo) -> Result<(), Error> {
Self::draw_static_ui()?;
Self::draw_dynamic_ui(&info)?;
Ok(())
}
pub fn render_result(info: ResultInfo) -> Result<(), Error> {
Self::draw_footer()?;
let Size{width, ..} = Terminal::size()?;
let title = "Bravo ! Voici vos résultats.";
#[allow(clippy::cast_possible_truncation)]
Terminal::move_cursor_to(Position {x: (width - title.len() as u16) / 2, y: 5})?;
Terminal::print(title)?;
let x_padding = width / 4;
let min_x = x_padding;
let min_y = 7;
Terminal::move_cursor_to(Position {x: min_x, y: min_y})?;
Terminal::print(&format!("WPM: {} ", info.wpm))?;
Terminal::move_cursor_to(Position {x: min_x, y: min_y + 1})?;
Terminal::print(&format!("Erreurs: {}", info.errors))?;
Terminal::move_cursor_to(Position {x: min_x, y: min_y + 2})?;
Terminal::print(&format!("Précision: {:.2}%", info.accuracy))?;
Ok(())
}
fn draw_static_ui() -> Result<(), Error> {
let Size{width, ..} = Terminal::size()?;
let title = "C'est parti !";
#[allow(clippy::cast_possible_truncation)]
Terminal::move_cursor_to(Position {x: (width - title.len() as u16) / 2, y: 5})?;
Terminal::print(title)?;
Self::draw_footer()?;
Ok(())
}
fn draw_footer() -> Result<(), Error> {
let Size{width, height} = Terminal::size()?;
let version_name = NAME.to_owned() + " - " + VERSION;
#[allow(clippy::cast_possible_truncation)]
Terminal::move_cursor_to(Position {x: width - version_name.len() as u16, y: height - 1})?;
Terminal::print(&version_name)?;
Terminal::move_cursor_to(Position {x: 0, y: height - 1})?;
Terminal::print("Appuyez sur CTRL+Q pour quitter")?;
Ok(())
}
fn draw_dynamic_ui(info: &TyperInfo) -> Result<(), Error> {
let Size{width, ..} = Terminal::size()?;
let x_padding = width / 4;
let min_x = x_padding;
let min_y = 7;
let max_x = width - x_padding;
let mut x_offset = 0;
let mut y_offset = 2;
let mut char_offset = 0;
Terminal::move_cursor_to(Position {x: min_x, y: min_y})?;
Terminal::print(&format!("Erreurs: {}", info.errors))?;
Terminal::move_cursor_to(Position {x: min_x + 20, y: min_y})?;
Terminal::print(&format!("WPM: {} ", info.wpm))?;
Terminal::move_cursor_to(Position {x: min_x, y: min_y + y_offset})?;
for i in 0..info.words_to_write.len() {
let current_word = info.words_to_write.get(i).unwrap();
#[allow(clippy::cast_possible_truncation)]
if min_x + x_offset + current_word.len() as u16 > max_x {
x_offset = 0;
y_offset += 1;
Terminal::move_cursor_to(Position {x: min_x + x_offset, y: min_y + y_offset})?;
}
for c in current_word.chars() {
if char_offset < info.words_written.len() {
if *info.words_written.get(char_offset).unwrap() == c {
Terminal::set_foreground_color(Color::White)?;
} else {
Terminal::set_foreground_color(Color::Red)?;
}
} else {
Terminal::set_foreground_color(Color::DarkGrey)?;
}
Terminal::print(&format!("{c}"))?;
char_offset += 1;
x_offset += 1;
}
Terminal::print(" ")?;
char_offset += 1;
x_offset += 1;
}
Terminal::reset_color()?;
Ok(())
}
}

38
src/typer/words.rs Normal file
View file

@ -0,0 +1,38 @@
use include_lines::{static_include_lines};
use rand::Rng;
static_include_lines!(WORD_LIST_ENGLISH, "data/words_english.txt");
static_include_lines!(WORD_LIST_FRENCH, "data/words_french.txt");
pub struct WordList {
pub words: Vec<String>,
pub chars: Vec<char>
}
impl WordList {
pub fn new(num_of_words: u16) -> WordList {
let mut rng = rand::rng();
let mut chars_to_write: Vec<char> = Vec::new();
let mut words_to_write: Vec<String> = Vec::new();
let words = Self::get_words();
for _ in 0..num_of_words {
let i = rng.random_range(0..words.len());
let word = words.get(i).unwrap();
words_to_write.push(word.to_owned());
for c in word.chars() {
chars_to_write.push(c);
}
chars_to_write.push(' ');
}
WordList{ words: words_to_write, chars: chars_to_write }
}
fn get_words() -> Vec<String> {
let mut words: Vec<String> = Vec::new();
for word in WORD_LIST_FRENCH {
words.push(word.to_owned());
}
words
}
}