domasi/src/main.rs

205 lines
5.9 KiB
Rust
Raw Normal View History

2020-05-22 05:21:27 +00:00
use clap::{App, Arg, ArgMatches, SubCommand};
2020-08-20 01:57:54 +00:00
use crossbeam::channel::{unbounded, Receiver, Sender};
2020-05-25 06:51:07 +00:00
use crossterm::{
event::{poll, read, Event, KeyCode, KeyEvent, KeyModifiers},
2020-08-20 01:57:54 +00:00
terminal::{disable_raw_mode, enable_raw_mode},
2020-05-25 06:51:07 +00:00
};
2020-08-24 03:27:49 +00:00
use domasi::pomodoro::{Alert, Remaining, Status};
use domasi::{Config, Pomodoro};
use std::fs;
2020-08-20 01:57:54 +00:00
use std::io::{self, Write};
2020-05-22 05:21:27 +00:00
use std::path::{Path, PathBuf};
use std::thread;
2020-05-21 04:04:31 +00:00
fn main() {
2020-08-24 03:27:49 +00:00
let matches = App::new("domasi")
2020-05-21 06:03:38 +00:00
.version("0.1.0")
2020-08-24 03:27:49 +00:00
.author("Rekai Musuka <rekai@musuka.dev>")
2020-05-21 06:03:38 +00:00
.about("Yet another pomodoro timer.")
2020-06-29 23:11:26 +00:00
.arg(
Arg::with_name("create-config")
.short("C")
.long("create-config")
.help("Creates a Settings.toml and an alert directory"),
)
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();
// match matches.subcommand() {
// ("start", Some(sub_matches)) => start(sub_matches),
// _ => {}
// }
2020-06-29 23:11:26 +00:00
if matches.is_present("create-config") {
create_config()
}
if let ("start", Some(sub_matches)) = matches.subcommand() {
start(sub_matches);
2020-05-21 06:03:38 +00:00
}
}
2020-06-29 23:11:26 +00:00
pub fn create_config() {
2020-08-20 01:57:54 +00:00
let config_dir = Config::get_config_directory();
2020-06-29 23:11:26 +00:00
let data_dir = Config::get_data_directory().join("alert");
if !config_dir.exists() {
fs::create_dir_all(&config_dir).unwrap_or_else(|err| {
panic!("Failed to create {}: {}", config_dir.to_string_lossy(), err)
})
}
if !data_dir.exists() {
fs::create_dir_all(&data_dir).unwrap_or_else(|err| {
panic!("Failed to create {}: {}", data_dir.to_string_lossy(), err)
});
}
let config: Config = Default::default();
Config::save(&config).unwrap_or_else(|err| {
let cfg_path = config_dir.to_string_lossy();
panic!("Error while writing settings.toml to {}: {}", cfg_path, err);
});
let data_path = data_dir.to_string_lossy();
let settings_path = config_dir.join("settings.toml");
let settings_path = settings_path.to_string_lossy();
println!(
"Successfully created \"{}\" and \"{}\"",
settings_path, data_path
);
}
2020-05-22 05:21:27 +00:00
pub fn start(args: &ArgMatches) {
let mut pomodoro = Pomodoro::new();
2020-08-20 01:57:54 +00:00
let (tx, rx): (Sender<Status>, Receiver<Status>) = unbounded();
2020-05-25 06:51:07 +00:00
// UI Thread
thread::spawn(move || loop {
if let Ok(status) = rx.recv() {
loop {
2020-08-24 03:27:49 +00:00
let (text, remain) = match Remaining::from_status(status) {
Some(seconds) => (seconds.to_string(), seconds.remaining_secs()),
None => ("??? Status: ??:??".to_string(), 0),
};
2020-08-20 01:57:54 +00:00
let out = io::stdout();
let mut handle = out.lock();
handle.write_all(text.as_bytes()).unwrap();
handle.flush().unwrap();
// TODO: Make sure this isn't an issue.
if remain < 1 {
2020-05-25 06:51:07 +00:00
break;
}
2020-05-25 06:51:07 +00:00
2020-08-24 03:27:49 +00:00
thread::sleep(Remaining::polling_interval());
}
2020-05-25 06:51:07 +00:00
}
});
// User Input Thread
thread::spawn(|| setup_user_input().unwrap());
2020-08-20 01:57: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().join("alert");
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);
}
}
}
2020-08-20 01:57:54 +00:00
}
2020-08-20 01:57:54 +00:00
match maybe_audio {
Some(audio_path) => {
let default_device = rodio::default_output_device().unwrap();
let alert = Alert::new(&audio_path, &default_device);
2020-08-20 01:57:54 +00:00
pomodoro.start(config, tx, Some(&alert));
}
2020-08-20 01:57:54 +00:00
None => pomodoro.start(config, tx, None),
}
2020-05-21 04:04:31 +00:00
}
fn get_audio_file(items: std::fs::ReadDir) -> Option<PathBuf> {
for maybe_entry in items {
2020-08-24 03:27:49 +00:00
if let Ok(entry) = maybe_entry {
if let Some(ext) = entry.path().extension() {
if let Some("mp3") | Some("wav") | Some("ogg") | Some("flac") = ext.to_str() {
return Some(entry.path());
}
}
}
}
2020-08-24 03:27:49 +00:00
None
}
2020-05-25 06:51:07 +00:00
fn setup_user_input() -> crossterm::Result<()> {
enable_raw_mode()?;
get_user_input()?;
disable_raw_mode()
}
fn get_user_input() -> crossterm::Result<()> {
loop {
2020-08-24 03:27:49 +00:00
if poll(Remaining::polling_interval())? {
2020-05-25 06:51:07 +00:00
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 {
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);
}