feat: implement the ability to skip

This commit is contained in:
Rekai Nyangadzayi Musuka 2020-12-15 21:34:46 -06:00
parent d94926a235
commit 325304deff
2 changed files with 126 additions and 34 deletions

View File

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

View File

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