2020-05-22 02:07:19 +00:00
|
|
|
use async_std::task;
|
2020-05-22 05:21:27 +00:00
|
|
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
2020-05-25 06:51:07 +00:00
|
|
|
use crossterm::{
|
|
|
|
cursor,
|
|
|
|
event::{poll, read, Event, KeyCode, KeyEvent, KeyModifiers},
|
|
|
|
terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType},
|
|
|
|
QueueableCommand,
|
|
|
|
};
|
2020-06-21 21:44:54 +00:00
|
|
|
use domasi::pomodoro::{Alert, Clock, Status};
|
|
|
|
use domasi::{Config, Pomodoro};
|
|
|
|
use std::fs;
|
2020-05-22 04:56:18 +00:00
|
|
|
use std::io::{stdout, Write};
|
2020-05-22 05:21:27 +00:00
|
|
|
use std::path::{Path, PathBuf};
|
2020-05-22 04:56:18 +00:00
|
|
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
|
|
|
use std::thread;
|
2020-05-22 02:07:19 +00:00
|
|
|
|
2020-05-21 04:04:31 +00:00
|
|
|
fn main() {
|
2020-05-21 06:03:38 +00:00
|
|
|
let matches = App::new("Domasi")
|
|
|
|
.version("0.1.0")
|
|
|
|
.author("paoda <musukarekai@gmail.com>")
|
|
|
|
.about("Yet another pomodoro timer.")
|
2020-05-22 05:21:27 +00:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("start")
|
|
|
|
.about("Start the Pomodoro Timer")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("alert")
|
|
|
|
.short("a")
|
|
|
|
.long("alert")
|
|
|
|
.value_name("FILE")
|
|
|
|
.takes_value(true)
|
|
|
|
.help("Aloud Sound. (Supports WAV, MP3, Vorbis, FLAC)"),
|
|
|
|
),
|
|
|
|
)
|
2020-05-21 06:03:38 +00:00
|
|
|
.get_matches();
|
|
|
|
|
2020-05-22 04:56:18 +00:00
|
|
|
// match matches.subcommand() {
|
|
|
|
// ("start", Some(sub_matches)) => start(sub_matches),
|
|
|
|
// _ => {}
|
|
|
|
// }
|
|
|
|
|
|
|
|
if let ("start", Some(sub_matches)) = matches.subcommand() {
|
|
|
|
start(sub_matches);
|
2020-05-21 06:03:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-22 05:21:27 +00:00
|
|
|
pub fn start(args: &ArgMatches) {
|
2020-06-21 21:44:54 +00:00
|
|
|
let mut pomodoro = Pomodoro::new();
|
2020-05-22 02:07:19 +00:00
|
|
|
|
2020-05-25 06:51:07 +00:00
|
|
|
let (tx, rx): (Sender<Status>, Receiver<Status>) = channel();
|
2020-05-22 04:56:18 +00:00
|
|
|
|
2020-05-25 06:51:07 +00:00
|
|
|
// UI Thread
|
|
|
|
thread::spawn(move || loop {
|
|
|
|
if let Ok(status) = rx.recv() {
|
|
|
|
loop {
|
2020-06-21 21:44:54 +00:00
|
|
|
let (remain, text) = Clock::get_formatted_string(status);
|
|
|
|
print_overwrite(&text);
|
2020-05-22 04:56:18 +00:00
|
|
|
|
2020-06-21 21:44:54 +00:00
|
|
|
// Make this check better pls
|
|
|
|
if remain < 1 {
|
2020-05-25 06:51:07 +00:00
|
|
|
break;
|
2020-05-22 04:56:18 +00:00
|
|
|
}
|
2020-05-25 06:51:07 +00:00
|
|
|
|
|
|
|
thread::sleep(Clock::get_polling_interval());
|
2020-05-22 04:56:18 +00:00
|
|
|
}
|
2020-05-25 06:51:07 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// User Input Thread
|
2020-06-21 21:44:54 +00:00
|
|
|
thread::spawn(|| setup_user_input().unwrap());
|
2020-05-22 04:56:18 +00:00
|
|
|
|
2020-06-21 21:44:54 +00:00
|
|
|
// Async Pomodoro
|
2020-05-25 06:51:07 +00:00
|
|
|
task::block_on(async {
|
2020-06-21 21:44:54 +00:00
|
|
|
let config = {
|
|
|
|
match Config::load() {
|
|
|
|
Some(cfg) => cfg,
|
|
|
|
None => Default::default(),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let maybe_audio: Option<PathBuf>;
|
|
|
|
|
|
|
|
match args.value_of("alert") {
|
|
|
|
Some(path) => maybe_audio = Some(Path::new(path).to_path_buf()),
|
|
|
|
None => {
|
|
|
|
match &config.sound_file {
|
|
|
|
Some(path) => maybe_audio = Some(path.clone()),
|
|
|
|
None => {
|
|
|
|
// Look in the default locations
|
|
|
|
// check for .mp3, .wav, .ogg, .flac
|
|
|
|
let data_dir = Config::get_data_directory();
|
|
|
|
let data_dir_str = data_dir.to_string_lossy();
|
|
|
|
|
|
|
|
let items = fs::read_dir(&data_dir).unwrap_or_else(|_err| {
|
|
|
|
panic!("Unable to read the contents of {}", data_dir_str);
|
|
|
|
});
|
|
|
|
|
|
|
|
maybe_audio = get_audio_file(items);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
match maybe_audio {
|
|
|
|
Some(audio_path) => {
|
|
|
|
let default_device = rodio::default_output_device().unwrap();
|
|
|
|
let alert = Alert::new(&audio_path, &default_device);
|
|
|
|
|
|
|
|
pomodoro.start(config, tx, Some(&alert)).await;
|
|
|
|
}
|
|
|
|
None => pomodoro.start(config, tx, None).await,
|
|
|
|
}
|
2020-05-22 02:07:19 +00:00
|
|
|
});
|
2020-05-21 04:04:31 +00:00
|
|
|
}
|
2020-05-22 04:56:18 +00:00
|
|
|
|
2020-06-21 21:44:54 +00:00
|
|
|
fn get_audio_file(items: std::fs::ReadDir) -> Option<PathBuf> {
|
|
|
|
let mut result: Option<PathBuf> = None;
|
|
|
|
|
|
|
|
for maybe_entry in items {
|
|
|
|
match maybe_entry {
|
|
|
|
Ok(entry) => match entry.path().extension() {
|
|
|
|
Some(ext) => match ext.to_str() {
|
|
|
|
Some("mp3") => {
|
|
|
|
result = Some(entry.path());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Some("wav") => {
|
|
|
|
result = Some(entry.path());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Some("ogg") => {
|
|
|
|
result = Some(entry.path());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Some("flac") => {
|
|
|
|
result = Some(entry.path());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Some(_ext) => continue,
|
|
|
|
None => continue,
|
|
|
|
},
|
|
|
|
None => continue,
|
|
|
|
},
|
|
|
|
Err(_err) => continue,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result
|
|
|
|
}
|
|
|
|
|
2020-05-25 06:51:07 +00:00
|
|
|
fn setup_user_input() -> crossterm::Result<()> {
|
|
|
|
enable_raw_mode()?;
|
|
|
|
|
|
|
|
get_user_input()?;
|
|
|
|
|
|
|
|
disable_raw_mode()
|
|
|
|
}
|
|
|
|
|
2020-05-22 04:56:18 +00:00
|
|
|
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();
|
|
|
|
}
|
2020-05-25 06:51:07 +00:00
|
|
|
|
|
|
|
fn get_user_input() -> crossterm::Result<()> {
|
|
|
|
loop {
|
|
|
|
if poll(Clock::get_polling_interval())? {
|
|
|
|
if let Event::Key(event) = read()? {
|
|
|
|
handle_key_event(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_key_event(event: KeyEvent) {
|
|
|
|
match event.code {
|
|
|
|
KeyCode::Char('c') => {
|
|
|
|
if let KeyModifiers::CONTROL = event.modifiers {
|
2020-06-21 21:44:54 +00:00
|
|
|
exit_domasi(0)
|
2020-05-25 06:51:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
KeyCode::Esc | KeyCode::Char('q') => exit_domasi(0),
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn exit_domasi(code: i32) {
|
|
|
|
disable_raw_mode().unwrap();
|
|
|
|
|
|
|
|
std::process::exit(code);
|
|
|
|
}
|