diff --git a/Cargo.lock b/Cargo.lock index e44d40b..f721850 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,10 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "arc-swap" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cfg-if" version = "0.1.6" @@ -28,6 +33,20 @@ name = "lazy_static" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "libc" +version = "0.2.48" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "signal-hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arc-swap 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "smallvec" version = "0.6.8" @@ -41,6 +60,7 @@ name = "stopwatch" version = "0.1.0" dependencies = [ "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "signal-hook 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -57,10 +77,13 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] +"checksum arc-swap 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1025aeae2b664ca0ea726a89d574fe8f4e77dd712d443236ad1de00379450cf6" "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" "checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" "checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" +"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" +"checksum signal-hook 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1f272d1b7586bec132ed427f532dd418d8beca1ca7f2caf7df35569b1415a4b4" "checksum smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "88aea073965ab29f6edb5493faf96ad662fb18aa9eeb186a3b7057951605ed15" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" diff --git a/Cargo.toml b/Cargo.toml index 5452cf5..2804f2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,4 @@ keywords = ["stopwatch", "cli", "time"] [dependencies] crossbeam-channel = "0.3" +signal-hook = "0.1" diff --git a/src/duration.rs b/src/duration.rs new file mode 100644 index 0000000..db14f7e --- /dev/null +++ b/src/duration.rs @@ -0,0 +1,19 @@ +use std::convert::From; +use std::{fmt, time}; + +#[derive(Debug)] +pub struct FmtDuration(time::Duration); + +impl fmt::Display for FmtDuration { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let secs = self.0.as_secs(); + let millis = self.0.subsec_millis(); + write!(f, "{:02}:{:02}:{:03}", secs / 60, secs % 60, millis) + } +} + +impl From for FmtDuration { + fn from(dur: time::Duration) -> Self { + FmtDuration(dur) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..cee9906 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,32 @@ +use crossbeam_channel::{bounded, Receiver}; +use duration::*; +use signal_hook::iterator::Signals; +use signal_hook::SIGINT; +use std::io::{Result, StdoutLock, Write}; +use std::thread; + +pub mod duration; + +pub fn signal_handler() -> Result> { + let (s, r) = bounded(1); + let signals = Signals::new(&[SIGINT])?; + + thread::spawn(move || { + for _ in signals.forever() { + if s.send(()).is_err() { + break; + } + } + }); + + Ok(r) +} + +/// Resets the current line on stdout +/// +/// # Arguments +/// +/// * w - A reference to the locked stdout handle +pub fn reset_line(w: &mut StdoutLock) -> Result<()> { + write!(w, "\r[2K") +} diff --git a/src/main.rs b/src/main.rs index 3b4a562..69a8f92 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,30 +2,44 @@ use crossbeam_channel::tick; use std::io::{stdout, Write}; -use std::time::{Duration, Instant}; +use std::time::Instant; +use stopwatch::duration::FmtDuration; +use stopwatch::*; #[macro_use] extern crate crossbeam_channel; fn main() { - let out = stdout(); - let mut out = out.lock(); - // 30 fps should be enough - let step_duration = Duration::from_millis(1000 / 30); + let step_duration = std::time::Duration::from_millis(1000 / 30); let start = Instant::now(); let ticker = tick(step_duration); + let signals = match signal_handler() { + Ok(r) => r, + Err(err) => { + println!("{}", err); + return; + } + }; + + let out = stdout(); + let mut out = out.lock(); + let mut elapsed = FmtDuration::from(start.elapsed()); + loop { select! { recv(ticker) -> now => { - let elapsed = now.unwrap().duration_since(start); - let secs = elapsed.as_secs(); - let millis = elapsed.subsec_millis(); - write!(out, "\r[2K").unwrap(); - write!(out, "{:02}:{:02}:{:03}", secs/60, secs % 60, millis).unwrap(); + elapsed = FmtDuration::from(now.unwrap().duration_since(start)); + reset_line(&mut out).unwrap(); + write!(out, "{}",elapsed).unwrap(); out.flush().unwrap(); } + recv(signals) -> _ => { + reset_line(&mut out).unwrap(); + writeln!(out, "Total time: {}", elapsed).unwrap(); + return; + } } } }