clean up outer functions

This commit is contained in:
Dr. Matthias Ratajczak
2023-03-02 16:14:30 +01:00
parent 54c2255c1c
commit 7bf31b9ee2
3 changed files with 89 additions and 39 deletions
Generated
+7
View File
@@ -2,6 +2,12 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "anyhow"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@@ -183,6 +189,7 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
name = "pomodoro" name = "pomodoro"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"crossterm", "crossterm",
"futures", "futures",
"tokio", "tokio",
+2 -1
View File
@@ -4,8 +4,9 @@ edition = "2021"
version = "0.1.0" version = "0.1.0"
[dependencies] [dependencies]
futures = { version = "0.3", default-features = false } anyhow = "1.0"
crossterm = { version = "0.26", default-features = false, features = ["event-stream"] } crossterm = { version = "0.26", default-features = false, features = ["event-stream"] }
futures = { version = "0.3", default-features = false }
tokio = { version = "1.26", default-features = false, features = ["sync", "time", "rt", "macros"] } tokio = { version = "1.26", default-features = false, features = ["sync", "time", "rt", "macros"] }
[profile.release] [profile.release]
+79 -37
View File
@@ -3,48 +3,67 @@ use std::fs::{self, read_to_string};
use std::io::{stdout, Stdout, Write}; use std::io::{stdout, Stdout, Write};
use std::os::unix::prelude::OsStringExt; use std::os::unix::prelude::OsStringExt;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::{Command, ExitCode};
use std::time::Duration; use std::time::Duration;
use anyhow::{anyhow, bail, Result};
use crossterm::{cursor, QueueableCommand}; use crossterm::{cursor, QueueableCommand};
use crossterm::{ use crossterm::{
cursor::MoveTo, cursor::MoveTo,
event::{Event, EventStream, KeyCode, KeyModifiers}, event::{Event, EventStream, KeyCode, KeyModifiers},
terminal::{self, disable_raw_mode, enable_raw_mode}, terminal::{self, disable_raw_mode, enable_raw_mode},
ExecutableCommand, Result, ExecutableCommand,
}; };
use futures::StreamExt; use futures::StreamExt;
use tokio::sync::broadcast::{channel, Receiver, Sender}; use tokio::sync::broadcast::{channel, Receiver, Sender};
use tokio::task::JoinSet;
use tokio::time::sleep; use tokio::time::sleep;
const ONE_SECOND: Duration = Duration::from_secs(1); const ONE_SECOND: Duration = Duration::from_secs(1);
const PID_FILE: &str = "/tmp/pomodoro.pid"; const PID_FILE: &str = "/tmp/pomodoro.pid";
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> { async fn main() -> ExitCode {
match main2().await {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("{}", e.root_cause().to_string());
ExitCode::FAILURE
}
}
}
async fn main2() -> Result<()> {
let pid = if let Some(pid) = Pid::check_already_running() { let pid = if let Some(pid) = Pid::check_already_running() {
eprintln!("already running (pid {})", pid.pid); bail!("already running (pid {})", pid.pid)
return Ok(());
} else { } else {
Pid::create() Pid::create()?
}; };
let result = run_all_tasks().await;
Ui::clear_terminal()?;
pid.remove()?;
result
}
async fn run_all_tasks() -> Result<()> {
let (tx, rx) = channel::<Message>(10); let (tx, rx) = channel::<Message>(10);
let timer = Pomodoro::new(rx); let timer = Pomodoro::new(rx);
let ui = Ui::new(tx); let ui = Ui::new(tx);
let timer_task = tokio::spawn(async move { timer.main_loop().await }); let mut tasks = JoinSet::new();
let ui_task = tokio::spawn(async move { ui.main_loop().await });
tokio::select! ( tasks.spawn(timer.main_loop());
result = timer_task => result??, tasks.spawn(ui.main_loop());
result = ui_task => result??,
);
Ui::clear_terminal()?; while let Some(result) = tasks.join_next().await {
result??;
pid.remove(); }
Ok(()) Ok(())
} }
@@ -82,16 +101,17 @@ impl Pid {
Some(result) Some(result)
} }
fn create() -> Self { fn create() -> Result<Self> {
let pid = std::process::id(); let pid = std::process::id();
let path = PathBuf::from(PID_FILE); let path = PathBuf::from(PID_FILE);
let mut fd = fs::File::create(&path).unwrap(); let mut fd = fs::File::create(&path)?;
write!(fd, "{pid}").unwrap(); write!(fd, "{pid}")?;
Self { pid, path } let result = Self { pid, path };
Ok(result)
} }
fn remove(self) { fn remove(self) -> Result<()> {
fs::remove_file(self.path).unwrap(); fs::remove_file(self.path).map_err(|e| anyhow!(e))
} }
} }
@@ -112,18 +132,35 @@ impl Ui {
stdout().execute(cursor::Hide)?; stdout().execute(cursor::Hide)?;
if let Some(Ok(Event::Key(key_event))) = reader.next().await { if let Some(Ok(Event::Key(key_event))) = reader.next().await {
if Self::is_char_q(key_event) || Self::is_ctrl_c(key_event) { let should_exit = self.handle_key_event(key_event)?;
self.sender.send(Message::Quit).unwrap(); if should_exit {
disable_raw_mode()?;
stdout().execute(cursor::Show)?;
return Ok(()); return Ok(());
}; };
}
}
}
fn handle_key_event(&self, key_event: crossterm::event::KeyEvent) -> Result<bool> {
if Self::is_char_q(key_event) {
self.prepare_for_program_quit()?;
return Ok(true);
};
if Self::is_ctrl_c(key_event) {
self.prepare_for_program_quit()?;
self.sender.send(Message::CtrlC)?;
bail!("<Ctrl-C>");
}
if Self::is_space(key_event) { if Self::is_space(key_event) {
self.sender.send(Message::TogglePause).unwrap(); self.sender.send(Message::TogglePause)?;
}
} }
Ok(false)
} }
fn prepare_for_program_quit(&self) -> Result<()> {
self.sender.send(Message::Quit)?;
disable_raw_mode()?;
stdout().execute(cursor::Show)?;
Ok(())
} }
fn is_char_q(key_event: crossterm::event::KeyEvent) -> bool { fn is_char_q(key_event: crossterm::event::KeyEvent) -> bool {
@@ -200,24 +237,28 @@ impl Pomodoro {
'inner: loop { 'inner: loop {
self.show_status()?; self.show_status()?;
if self.paused {
if self.receiver.recv().await.unwrap() == Message::TogglePause {
self.toggle_pause();
continue;
}
}
tokio::select! ( tokio::select! (
_ = sleep(ONE_SECOND) => {self.remaining -= ONE_SECOND;} _ = sleep(ONE_SECOND) => {
if !self.paused {
self.remaining -= ONE_SECOND;
}
},
message = self.receiver.recv() => { message = self.receiver.recv() => {
if let Ok(m) = message{ if let Ok(m) = message{
match m { match m {
Message::Quit => {return Ok(());}, Message::Quit => {
return Ok(());
},
Message::CtrlC => {
bail!("<Ctrl-C>");
},
Message::TogglePause => { Message::TogglePause => {
self.toggle_pause(); self.toggle_pause();
continue; continue;
} }
} }
} else {
bail!("<terminate>");
} }
} }
); );
@@ -310,6 +351,7 @@ fn format(dur: Duration) -> String {
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
enum Message { enum Message {
CtrlC,
Quit, Quit,
TogglePause, TogglePause,
} }