feat: implement the ability to skip
This commit is contained in:
parent
d94926a235
commit
325304deff
90
src/alert.rs
90
src/alert.rs
|
@ -1,8 +1,9 @@
|
|||
use rodio::{Decoder, OutputStream, Sink};
|
||||
use rodio::{decoder::DecoderError, Decoder, OutputStream, PlayError, Sink, StreamError};
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::PathBuf;
|
||||
use std::thread;
|
||||
use std::{error::Error, fmt::Display, fmt::Formatter, fmt::Result as FmtResult};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Alert {
|
||||
|
@ -18,15 +19,38 @@ impl Alert {
|
|||
self.path = path;
|
||||
}
|
||||
|
||||
pub fn play(&self) {
|
||||
// pub fn play(&self) -> Result<(), Box<dyn Error>> {
|
||||
// let file = File::open(&self.path).unwrap();
|
||||
|
||||
// thread::Builder::new()
|
||||
// .name("Audio Thread".to_string())
|
||||
// .spawn(|| -> Result<(), Error> {
|
||||
// let (_stream, handle) = OutputStream::try_default()?;
|
||||
// let source = Decoder::new(BufReader::new(file))?;
|
||||
// let sink = Sink::try_new(&handle)?;
|
||||
|
||||
// sink.append(source);
|
||||
|
||||
// loop {
|
||||
// if sink.len() == 0 {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Ok(())
|
||||
// })?;
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
pub fn play(&self) -> Result<(), Box<dyn Error>> {
|
||||
let file = File::open(&self.path).unwrap();
|
||||
|
||||
thread::Builder::new()
|
||||
.name("Audio Thread".to_string())
|
||||
.spawn(move || {
|
||||
let (_stream, handle) = OutputStream::try_default().unwrap();
|
||||
let source = Decoder::new(BufReader::new(file)).unwrap();
|
||||
let sink = Sink::try_new(&handle).unwrap();
|
||||
.name("Alert Playback Thread".to_string())
|
||||
.spawn(move || -> Result<(), PlaybackError> {
|
||||
let (_stream, handle) = OutputStream::try_default()?;
|
||||
let source = Decoder::new(BufReader::new(file))?;
|
||||
let sink = Sink::try_new(&handle)?;
|
||||
|
||||
sink.append(source);
|
||||
|
||||
|
@ -35,7 +59,55 @@ impl Alert {
|
|||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum PlaybackError {
|
||||
StreamError(StreamError),
|
||||
DecoderError(DecoderError),
|
||||
SinkError(PlayError),
|
||||
}
|
||||
|
||||
impl From<DecoderError> for PlaybackError {
|
||||
fn from(err: DecoderError) -> Self {
|
||||
Self::DecoderError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StreamError> for PlaybackError {
|
||||
fn from(err: StreamError) -> Self {
|
||||
Self::StreamError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PlayError> for PlaybackError {
|
||||
fn from(err: PlayError) -> Self {
|
||||
Self::SinkError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for PlaybackError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
match &self {
|
||||
Self::StreamError(err) => Some(err),
|
||||
Self::DecoderError(err) => Some(err),
|
||||
Self::SinkError(err) => Some(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PlaybackError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
match &self {
|
||||
Self::StreamError(err) => write!(f, "{}", err),
|
||||
Self::DecoderError(err) => write!(f, "{}", err),
|
||||
Self::SinkError(err) => write!(f, "{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ static SHORT_BREAK_TIME: u64 = 300; // Default: 300 (5min)
|
|||
static LONG_BREAK_TIME: u64 = 600; // Default: 600 (10min)
|
||||
static POLLING_RATE: Duration = Duration::from_millis(100);
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct Domasi {
|
||||
state: State,
|
||||
count: u64,
|
||||
|
@ -32,7 +32,7 @@ impl Default for Domasi {
|
|||
|
||||
impl Domasi {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn start(&mut self, maybe_alert: Option<Alert>) -> crossterm::Result<()> {
|
||||
|
@ -67,18 +67,11 @@ impl Domasi {
|
|||
self.paused.toggle();
|
||||
|
||||
if !self.paused.value {
|
||||
// Apply the elapsed time
|
||||
let duration = self.paused.duration;
|
||||
match self.wait_start {
|
||||
Some(instant) => self.wait_start = Some(instant + duration),
|
||||
None => {
|
||||
unreachable!("Unable to resume from a non-existent timer.")
|
||||
}
|
||||
}
|
||||
|
||||
self.apply_paused_time();
|
||||
self.paused = Default::default();
|
||||
}
|
||||
}
|
||||
KeyCode::Char('s') | KeyCode::Char('S') => self.skip(maybe_alert.as_ref()),
|
||||
KeyCode::Char('r') | KeyCode::Char('R') => self.restart(),
|
||||
KeyCode::Char('q') | KeyCode::Char('Q') => break,
|
||||
KeyCode::Char('c') if key_event.modifiers == KeyModifiers::CONTROL => break,
|
||||
|
@ -92,6 +85,44 @@ impl Domasi {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_paused_time(&mut self) {
|
||||
let duration = self.paused.duration;
|
||||
match self.wait_start {
|
||||
Some(instant) => self.wait_start = Some(instant + duration),
|
||||
None => unreachable!("Unable to resume from a non-existent timer."),
|
||||
}
|
||||
}
|
||||
|
||||
fn skip(&mut self, maybe_alert: Option<&Alert>) {
|
||||
if self.paused.value {
|
||||
self.paused = Default::default();
|
||||
}
|
||||
|
||||
self.next();
|
||||
|
||||
if let Some(alert) = maybe_alert {
|
||||
let _ = alert.play();
|
||||
}
|
||||
|
||||
let _ = self.notify();
|
||||
self.display_time()
|
||||
}
|
||||
|
||||
fn next(&mut self) {
|
||||
let (do_update_count, next_state) = self.get_next();
|
||||
|
||||
if let Count::Increase = do_update_count {
|
||||
self.count += 1;
|
||||
}
|
||||
|
||||
self.wait_start = match next_state {
|
||||
State::UserWait => None,
|
||||
State::Work | State::ShortBreak | State::LongBreak => Some(Instant::now()),
|
||||
State::Start => unreachable!("Domasi#next should never return State::Start"),
|
||||
};
|
||||
self.state = next_state;
|
||||
}
|
||||
|
||||
fn restart(&mut self) {
|
||||
self.wait_start = Some(Instant::now());
|
||||
self.paused = Default::default();
|
||||
|
@ -169,21 +200,10 @@ impl Domasi {
|
|||
}
|
||||
Status::Active => self.display_time(),
|
||||
Status::NextState => {
|
||||
let (update_count, new_state) = self.next();
|
||||
|
||||
if let Count::Increase = update_count {
|
||||
self.count += 1;
|
||||
}
|
||||
|
||||
self.state = new_state;
|
||||
self.wait_start = match new_state {
|
||||
State::UserWait => None,
|
||||
State::Work | State::ShortBreak | State::LongBreak => Some(Instant::now()),
|
||||
State::Start => unreachable!("Domasi#next should never return State::Start"),
|
||||
};
|
||||
self.next();
|
||||
|
||||
if let Some(alert) = maybe_alert {
|
||||
alert.play()
|
||||
let _ = alert.play();
|
||||
}
|
||||
|
||||
let _ = self.notify();
|
||||
|
@ -229,7 +249,7 @@ impl Domasi {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn next(&self) -> (Count, State) {
|
||||
fn get_next(&self) -> (Count, State) {
|
||||
match self.state {
|
||||
State::Work => {
|
||||
let state: State = if (self.count + 1) % 4 == 0 {
|
||||
|
|
Loading…
Reference in New Issue