clean up outer functions
This commit is contained in:
Generated
+7
@@ -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
@@ -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
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user