Add working clock, and improve appearance of the output

This commit is contained in:
Rekai Musuka
2020-05-21 23:56:18 -05:00
parent f21e9a5cae
commit 171ccafe72
4 changed files with 267 additions and 43 deletions

View File

@@ -6,6 +6,8 @@ pub mod pomodoro {
use std::fs::{self, File};
use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::sync::mpsc::Sender;
use std::time::{Duration, Instant};
#[derive(Copy, Clone)]
pub struct Alert<'a> {
@@ -21,10 +23,6 @@ pub mod pomodoro {
}
}
pub fn load<'a, 'b, P: AsRef<Path>>(&'a mut self, _path: &'b P) {
unimplemented!()
}
pub fn play(&self) {
let file = File::open(self.path).unwrap();
let source = Decoder::new(BufReader::new(file)).unwrap();
@@ -33,7 +31,7 @@ pub mod pomodoro {
}
}
#[derive(PartialEq, Eq)]
#[derive(Copy, Clone, Debug)]
pub enum State {
Work,
ShortBreak,
@@ -41,32 +39,58 @@ pub mod pomodoro {
Inactive,
}
pub struct Config {
pub work_time: u64,
pub short_break: u64,
pub long_break: u64,
pub data_directory: PathBuf,
}
impl Default for Config {
fn default() -> Self {
let dirs = ProjectDirs::from("moe", "paoda", "domasi").unwrap();
let data_directory = dirs.data_dir().to_owned();
if !data_directory.exists() {
fs::create_dir_all(&data_directory).unwrap();
}
Config {
work_time: 1,
short_break: 1,
long_break: 1,
data_directory,
impl std::fmt::Display for State {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
State::Work => f.write_str("Working: "),
State::ShortBreak => f.write_str("Resting: "),
State::LongBreak => f.write_str("REALLY Resting: "),
State::Inactive => f.write_str("Inactive: "),
}
}
}
pub struct Config {
pub work_time: Duration,
pub short_break: Duration,
pub long_break: Duration,
pub data_directory: PathBuf,
}
impl Config {
pub fn new(work: u64, short_break: u64, long_break: u64) -> Config {
let data_directory = Self::get_data_directory();
if !data_directory.exists() {
fs::create_dir_all(&data_directory).unwrap();
}
let seconds_in_minutes = 60;
Config {
work_time: Duration::from_secs(work * seconds_in_minutes),
short_break: Duration::from_secs(short_break * seconds_in_minutes),
long_break: Duration::from_secs(long_break * seconds_in_minutes),
data_directory,
}
}
pub fn with_data_directory<P: AsRef<Path>>(mut self, path: &P) -> Config {
self.data_directory = path.as_ref().to_owned();
self
}
fn get_data_directory() -> PathBuf {
let dirs = ProjectDirs::from("moe", "paoda", "domasi").unwrap();
dirs.data_dir().to_owned()
}
}
impl Default for Config {
fn default() -> Self {
Config::new(20, 5, 10)
}
}
#[derive(Copy, Clone)]
pub struct Pomodoro<'a> {
count: u64,
state: State,
@@ -99,30 +123,26 @@ pub mod pomodoro {
}
}
async fn wait(minutes: u64) {
use std::time::Duration;
let duration = Duration::from_secs(minutes * 5);
async fn wait(duration: Duration) {
async_std::task::sleep(duration).await;
}
pub async fn start(&mut self, config: Config) {
pub async fn start(&mut self, config: Config, tx: Sender<Status>) {
loop {
self.next();
self.alert.play();
match self.state {
State::Work => {
println!("Start Work.");
Self::send_to_clock(&tx, self.state, config.work_time);
Self::wait(config.work_time).await;
}
State::ShortBreak => {
println!("Start Short Break.");
Self::send_to_clock(&tx, self.state, config.short_break);
Self::wait(config.short_break).await;
}
State::LongBreak => {
println!("Start Long Break.");
Self::send_to_clock(&tx, self.state, config.long_break);
Self::wait(config.long_break).await;
}
State::Inactive => {
@@ -133,8 +153,73 @@ pub mod pomodoro {
}
}
pub fn completed(&self) -> u64 {
self.count / 4
fn send_to_clock(tx: &Sender<Status>, state: State, length: Duration) {
let status = Status {
start: Instant::now(),
length,
state,
};
tx.send(status).unwrap();
}
}
#[derive(Copy, Clone, Debug)]
pub struct Status {
pub start: Instant,
pub length: Duration,
pub state: State,
}
pub struct Clock;
impl Clock {
pub fn get_formatted_string(status: Status) -> (u64, String) {
let now = Instant::now();
let elapsed = now.checked_duration_since(status.start);
match elapsed {
Some(duration) => {
let remaining = (status.length.as_secs() * 60) - duration.as_secs();
let seconds = remaining;
let hours = seconds / 3600;
let minutes = (seconds - (hours * 3600)) / 60;
let seconds = seconds - (hours * 3600) - (minutes * 60);
let mut clock = String::new();
clock.push_str(&format!("{}", status.state));
if hours > 0 {
// We don't want o bother with the hours part if there is none
if hours < 10 {
clock.push_str(&format!("0{}:", hours));
} else {
clock.push_str(&format!("{}:", hours));
}
}
if minutes < 10 {
clock.push_str(&format!("0{}:", minutes));
} else {
clock.push_str(&format!("{}:", minutes));
}
if seconds < 10 {
clock.push_str(&format!("0{}", seconds));
} else {
clock.push_str(&format!("{}", seconds));
}
(remaining, clock)
}
None => (0, "??:??:??".to_string()), // This will break the loop
}
}
pub fn get_polling_interval() -> Duration {
Duration::from_millis(500)
}
}
}

View File

@@ -1,7 +1,12 @@
use async_std::task;
use clap::{App, ArgMatches, SubCommand};
use domasi::pomodoro::{Alert, Config};
use crossterm::{cursor, terminal::Clear, terminal::ClearType, QueueableCommand};
use domasi::pomodoro::{Alert, Clock, Config, Status};
use domasi::Pomodoro;
use std::io::{stdout, Write};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
fn main() {
let matches = App::new("Domasi")
@@ -11,9 +16,13 @@ fn main() {
.subcommand(SubCommand::with_name("start").about("Start the Pomodoro Timer"))
.get_matches();
match matches.subcommand() {
("start", Some(sub_matches)) => start(sub_matches),
_ => {}
// match matches.subcommand() {
// ("start", Some(sub_matches)) => start(sub_matches),
// _ => {}
// }
if let ("start", Some(sub_matches)) = matches.subcommand() {
start(sub_matches);
}
}
@@ -26,6 +35,34 @@ pub fn start(_args: &ArgMatches) {
let mut pomodoro = Pomodoro::new(&alert);
task::block_on(async {
pomodoro.start(config).await;
let (tx, rx): (Sender<Status>, Receiver<Status>) = channel();
thread::spawn(move || loop {
if let Ok(status) = rx.recv() {
loop {
let (remaining, string) = Clock::get_formatted_string(status);
print_overwrite(&string);
// Super fun race condition that you gotta handle better :)
if remaining < 1 {
break;
}
thread::sleep(Clock::get_polling_interval());
}
}
});
pomodoro.start(config, tx).await;
});
}
fn print_overwrite(text: &str) {
let mut stdout = stdout();
stdout.queue(Clear(ClearType::CurrentLine)).unwrap();
stdout.queue(cursor::SavePosition).unwrap();
stdout.write_all(text.as_bytes()).unwrap();
stdout.queue(cursor::RestorePosition).unwrap();
stdout.flush().unwrap();
}