Add cancel feature

This commit is contained in:
Rico Riedel 2022-11-23 19:53:31 +01:00
parent 19461dde26
commit 1e4403b5cb
No known key found for this signature in database
GPG Key ID: 75AC868575DE7B18
5 changed files with 95 additions and 53 deletions

74
Cargo.lock generated
View File

@ -43,6 +43,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cancellation"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7a879c84c21f354f13535f87ad119ac3be22ebb9097b552a0af6a78f86628c4"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -51,26 +57,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "3.2.16" version = "4.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3dbbb6653e7c55cc8595ad3e1f7be8f32aba4eb7ff7f0fd1163d4f3d137c0a9" checksum = "2148adefda54e14492fb9bddcc600b4344c5d1a3123bd666dcb939c6f0e0e57e"
dependencies = [ dependencies = [
"atty", "atty",
"bitflags", "bitflags",
"clap_derive", "clap_derive",
"clap_lex", "clap_lex",
"indexmap",
"once_cell", "once_cell",
"strsim", "strsim",
"termcolor", "termcolor",
"textwrap",
] ]
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "3.2.15" version = "4.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ba52acd3b0a5c33aeada5cdaa3267cdc7c594a98731d4268cdc1532f4264cb4" checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro-error", "proc-macro-error",
@ -81,9 +85,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.2.4" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
dependencies = [ dependencies = [
"os_str_bytes", "os_str_bytes",
] ]
@ -96,9 +100,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]] [[package]]
name = "crossterm" name = "crossterm"
version = "0.24.0" version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab9f7409c70a38a56216480fba371ee460207dd8926ccf5b4160591759559170" checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crossterm_winapi", "crossterm_winapi",
@ -119,6 +123,16 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "ctrlc"
version = "3.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d91974fbbe88ec1df0c24a4f00f99583667a7e2e6272b2b92d294d81e462173"
dependencies = [
"nix",
"winapi",
]
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "0.99.17" version = "0.99.17"
@ -176,12 +190,6 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.4.0" version = "0.4.0"
@ -197,16 +205,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "indexmap"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.10.3" version = "0.10.3"
@ -224,9 +222,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.126" version = "0.2.137"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
@ -292,6 +290,18 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "nix"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb"
dependencies = [
"autocfg",
"bitflags",
"cfg-if",
"libc",
]
[[package]] [[package]]
name = "normalize-line-endings" name = "normalize-line-endings"
version = "0.3.0" version = "0.3.0"
@ -565,12 +575,6 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b"
[[package]]
name = "textwrap"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.3" version = "1.0.3"
@ -668,8 +672,10 @@ name = "wipe"
version = "2.0.0" version = "2.0.0"
dependencies = [ dependencies = [
"approx", "approx",
"cancellation",
"clap", "clap",
"crossterm", "crossterm",
"ctrlc",
"derive_more", "derive_more",
"mockall", "mockall",
"rand", "rand",

View File

@ -8,8 +8,10 @@ repository = "https://github.com/ricoriedel/wipe"
authors = ["Rico Riedel <rico.riedel@protonmail.ch>"] authors = ["Rico Riedel <rico.riedel@protonmail.ch>"]
[dependencies] [dependencies]
clap = { version = "3.2", features = ["derive"] } clap = { version = "4.0.26", features = ["derive"] }
crossterm = "0.24" crossterm = "0.25.0"
ctrlc = "3.2.3"
cancellation = "0.1.0"
derive_more = "0.99" derive_more = "0.99"
rand = "0.8" rand = "0.8"

View File

@ -21,6 +21,12 @@ impl From<&str> for Error {
} }
} }
impl From<ctrlc::Error> for Error {
fn from(err: ctrlc::Error) -> Self {
Error(err.to_string())
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

View File

@ -1,5 +1,6 @@
use crate::Error; use crate::Error;
use crate::Renderer; use crate::Renderer;
use cancellation::CancellationToken;
use std::thread; use std::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -29,23 +30,23 @@ impl Clock for ClockImpl {
/// A timer for rendering. /// A timer for rendering.
#[derive(derive_more::Constructor)] #[derive(derive_more::Constructor)]
pub struct Timer<T> { pub struct Executor<T> {
clock: T, clock: T,
duration: Duration, duration: Duration,
delay: Duration, delay: Duration,
} }
impl<T: Clock> Timer<T> { impl<T: Clock> Executor<T> {
/// Runs the animation main loop. /// Runs the animation main loop.
pub fn run(&self, mut renderer: impl Renderer) -> Result<(), Error> { pub fn run(&self, mut renderer: impl Renderer, token: &CancellationToken) -> Result<(), Error> {
let start = self.clock.now(); let start = self.clock.now();
let mut now = start; let mut tick = start;
while now.duration_since(start) < self.duration { while !token.is_canceled() && tick.duration_since(start) < self.duration {
let step = now.duration_since(start).as_secs_f32() / self.duration.as_secs_f32(); let step = tick.duration_since(start).as_secs_f32() / self.duration.as_secs_f32();
renderer.render(step)?; renderer.render(step)?;
now = self.delay(now); tick = self.delay(tick);
} }
Ok(()) Ok(())
} }
@ -66,6 +67,7 @@ impl<T: Clock> Timer<T> {
mod test { mod test {
use super::*; use super::*;
use crate::MockRenderer; use crate::MockRenderer;
use cancellation::CancellationTokenSource;
use mockall::predicate::eq; use mockall::predicate::eq;
use mockall::Sequence; use mockall::Sequence;
@ -91,7 +93,7 @@ mod test {
.return_const(begin + Duration::from_secs(20)) .return_const(begin + Duration::from_secs(20))
.in_sequence(clock_seq); .in_sequence(clock_seq);
let timer = Timer::new(clock, Duration::from_secs(20), Duration::from_secs(10)); let timer = Executor::new(clock, Duration::from_secs(20), Duration::from_secs(10));
let mut renderer = MockRenderer::new(); let mut renderer = MockRenderer::new();
let renderer_seq = &mut Sequence::new(); let renderer_seq = &mut Sequence::new();
@ -109,7 +111,7 @@ mod test {
.returning(|_| Ok(())) .returning(|_| Ok(()))
.in_sequence(renderer_seq); .in_sequence(renderer_seq);
timer.run(renderer).unwrap(); timer.run(renderer, CancellationToken::none()).unwrap();
} }
#[test] #[test]
@ -140,12 +142,12 @@ mod test {
.return_const(begin + Duration::from_secs(10)) .return_const(begin + Duration::from_secs(10))
.in_sequence(clock_seq); .in_sequence(clock_seq);
let timer = Timer::new(clock, Duration::from_secs(10), Duration::from_secs(10)); let timer = Executor::new(clock, Duration::from_secs(10), Duration::from_secs(10));
let mut renderer = MockRenderer::new(); let mut renderer = MockRenderer::new();
renderer.expect_render().returning(|_| Ok(())); renderer.expect_render().returning(|_| Ok(()));
timer.run(renderer).unwrap(); timer.run(renderer, CancellationToken::none()).unwrap();
} }
#[test] #[test]
@ -165,11 +167,30 @@ mod test {
.return_const(begin + Duration::from_secs(12)) .return_const(begin + Duration::from_secs(12))
.in_sequence(clock_seq); .in_sequence(clock_seq);
let timer = Timer::new(clock, Duration::from_secs(10), Duration::from_secs(10)); let timer = Executor::new(clock, Duration::from_secs(10), Duration::from_secs(10));
let mut renderer = MockRenderer::new(); let mut renderer = MockRenderer::new();
renderer.expect_render().returning(|_| Ok(())); renderer.expect_render().returning(|_| Ok(()));
timer.run(renderer).unwrap(); timer.run(renderer, CancellationToken::none()).unwrap();
}
#[test]
fn run_canceled_aborts() {
let mut clock = MockClock::new();
let mut renderer = MockRenderer::new();
let src = CancellationTokenSource::new();
let token = src.token().clone();
clock.expect_now().return_const(Instant::now());
clock.expect_sleep().once().return_const(());
renderer.expect_render().once().returning(move |_| {
src.cancel();
Ok(())
});
let timer = Executor::new(clock, Duration::from_secs(10), Duration::from_secs(1));
timer.run(renderer, &token).unwrap();
} }
} }

View File

@ -3,22 +3,23 @@ pub mod pattern;
pub mod transform; pub mod transform;
mod error; mod error;
mod exec;
mod printer; mod printer;
mod renderer; mod renderer;
mod term; mod term;
mod timer;
mod vec; mod vec;
pub use error::*; pub use error::*;
pub use exec::*;
pub use printer::*; pub use printer::*;
pub use renderer::*; pub use renderer::*;
pub use term::*; pub use term::*;
pub use timer::*;
pub use vec::*; pub use vec::*;
use crate::convert::*; use crate::convert::*;
use crate::pattern::*; use crate::pattern::*;
use crate::transform::*; use crate::transform::*;
use cancellation::CancellationTokenSource;
use clap::builder::NonEmptyStringValueParser; use clap::builder::NonEmptyStringValueParser;
use clap::{value_parser, Parser, ValueEnum}; use clap::{value_parser, Parser, ValueEnum};
use crossterm::style::Color; use crossterm::style::Color;
@ -267,9 +268,15 @@ fn main() -> Result<(), Error> {
let renderer = RendererImpl::new(sampler, converter, printer)?; let renderer = RendererImpl::new(sampler, converter, printer)?;
let clock = ClockImpl::new(); let clock = ClockImpl::new();
let timer = Timer::new(clock, duration, delay); let executor = Executor::new(clock, duration, delay);
timer.run(renderer) let src = CancellationTokenSource::new();
let token = src.token().clone();
ctrlc::set_handler(move || {
src.cancel();
})?;
executor.run(renderer, &token)
} }
#[cfg(test)] #[cfg(test)]