Add working clock, and improve appearance of the output
This commit is contained in:
parent
f21e9a5cae
commit
171ccafe72
|
@ -19,6 +19,12 @@ dependencies = [
|
|||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
|
@ -185,6 +191,15 @@ version = "0.4.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f86c952727a495bda7abaf09bafdee1a939194dd793d9a8e26281df55ac43b00"
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.5"
|
||||
|
@ -280,6 +295,31 @@ dependencies = [
|
|||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.17.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a880035bfe4707e344da9acf50cc94d003fe337f50afd94c8722c1bb4e0a933"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"signal-hook",
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "057b7146d02fb50175fd7dbe5158f6097f33d02831f43b4ee8ae4ddf67b68f5c"
|
||||
dependencies = [
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "directories"
|
||||
version = "2.0.2"
|
||||
|
@ -308,6 +348,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"async-std",
|
||||
"clap",
|
||||
"crossterm",
|
||||
"directories",
|
||||
"rodio",
|
||||
]
|
||||
|
@ -445,6 +486,15 @@ dependencies = [
|
|||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.8"
|
||||
|
@ -600,6 +650,30 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cloudabi",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
|
@ -718,6 +792,27 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ff2db2112d6c761e12522c65f7768548bd6e8cd23d2a9dae162520626629bd6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.2"
|
||||
|
@ -735,6 +830,12 @@ dependencies = [
|
|||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
|
||||
|
||||
[[package]]
|
||||
name = "stdweb"
|
||||
version = "0.1.3"
|
||||
|
|
|
@ -11,3 +11,4 @@ rodio = "0.11.0"
|
|||
clap = "2.33.1"
|
||||
directories = "2.0.2"
|
||||
async-std = "1.5.0"
|
||||
crossterm = "0.17.4"
|
||||
|
|
161
src/lib.rs
161
src/lib.rs
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
47
src/main.rs
47
src/main.rs
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue