refactor: make code more readable and maintainable
parent
bd91ea9d90
commit
e3fd65e98b
12
src/cli.rs
12
src/cli.rs
|
@ -44,7 +44,7 @@ pub enum Command {
|
||||||
#[clap(visible_alias = "t")]
|
#[clap(visible_alias = "t")]
|
||||||
Toggle {
|
Toggle {
|
||||||
/// name of the timer to toggle
|
/// name of the timer to toggle
|
||||||
name: String
|
name: String,
|
||||||
},
|
},
|
||||||
/// Remove a timer
|
/// Remove a timer
|
||||||
#[clap(visible_alias = "r")]
|
#[clap(visible_alias = "r")]
|
||||||
|
@ -91,16 +91,12 @@ pub enum PomodoroCommand {
|
||||||
List,
|
List,
|
||||||
/// Toggle pomodoro
|
/// Toggle pomodoro
|
||||||
#[clap(visible_alias = "t")]
|
#[clap(visible_alias = "t")]
|
||||||
Toggle
|
Toggle,
|
||||||
}
|
|
||||||
|
|
||||||
fn get_stream(socket_path: &String) -> Result<UnixStream> {
|
|
||||||
UnixStream::connect(socket_path)
|
|
||||||
.context(format!("Could not connect to socket {}!", socket_path))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_command(socket_path: &String, command: OtherCommand) -> Result<Answer> {
|
pub fn send_command(socket_path: &String, command: OtherCommand) -> Result<Answer> {
|
||||||
let stream = get_stream(socket_path)?;
|
let stream = UnixStream::connect(socket_path)
|
||||||
|
.context(format!("Could not connect to socket {}!", socket_path))?;
|
||||||
serde_cbor::to_writer(&stream, &command).context("Could not write command!")?;
|
serde_cbor::to_writer(&stream, &command).context("Could not write command!")?;
|
||||||
stream
|
stream
|
||||||
.shutdown(Shutdown::Write)
|
.shutdown(Shutdown::Write)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::notification::send_notifictation;
|
use crate::helper::send_notifictation;
|
||||||
use crate::pomodoro::Pomodoro;
|
use crate::pomodoro::Pomodoro;
|
||||||
pub use crate::timer::Timer;
|
pub use crate::timer::Timer;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
@ -39,27 +39,6 @@ pub enum Answer {
|
||||||
Pomodoro(Option<Pomodoro>),
|
Pomodoro(Option<Pomodoro>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Answer {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
|
||||||
match self {
|
|
||||||
Answer::Ok => write!(f, ""),
|
|
||||||
Answer::Timers(timers) => {
|
|
||||||
if timers.is_empty() {
|
|
||||||
writeln!(f, "No timers running.")
|
|
||||||
} else {
|
|
||||||
let strings: Vec<String> =
|
|
||||||
timers.iter().map(|timer| timer.to_string()).collect();
|
|
||||||
writeln!(f, "{}", strings.join("\n"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Answer::Pomodoro(pomodoro) => match pomodoro {
|
|
||||||
Some(p) => write!(f, "{}", p),
|
|
||||||
None => write!(f, "No pomodoro running."),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error, Serialize, Deserialize)]
|
#[derive(Debug, thiserror::Error, Serialize, Deserialize)]
|
||||||
pub enum AnswerErr {
|
pub enum AnswerErr {
|
||||||
#[error("Timer with name '{}' already exists", .0)]
|
#[error("Timer with name '{}' already exists", .0)]
|
||||||
|
@ -70,6 +49,12 @@ pub enum AnswerErr {
|
||||||
NoPomodoro,
|
NoPomodoro,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum DaemonErr {
|
||||||
|
#[error("Daemon already running!")]
|
||||||
|
AlreadyRunning,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Daemon {
|
pub struct Daemon {
|
||||||
socket_path: Box<Path>,
|
socket_path: Box<Path>,
|
||||||
pid_file_path: Box<Path>,
|
pid_file_path: Box<Path>,
|
||||||
|
@ -79,12 +64,6 @@ pub struct Daemon {
|
||||||
notify: bool,
|
notify: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum DaemonErr {
|
|
||||||
#[error("Daemon already running!")]
|
|
||||||
AlreadyRunning,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Daemon {
|
impl Daemon {
|
||||||
pub fn new(socket: String, pid_file: String, no_notify: bool) -> anyhow::Result<Self> {
|
pub fn new(socket: String, pid_file: String, no_notify: bool) -> anyhow::Result<Self> {
|
||||||
let pid_file_path = std::path::Path::new(&pid_file);
|
let pid_file_path = std::path::Path::new(&pid_file);
|
||||||
|
@ -179,10 +158,9 @@ impl Daemon {
|
||||||
Some(ref mut pomodoro) => {
|
Some(ref mut pomodoro) => {
|
||||||
pomodoro.timer.toggle();
|
pomodoro.timer.toggle();
|
||||||
Ok(Answer::Ok)
|
Ok(Answer::Ok)
|
||||||
},
|
|
||||||
None => Err(AnswerErr::NoPomodoro),
|
|
||||||
}
|
}
|
||||||
|
None => Err(AnswerErr::NoPomodoro),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,18 +173,10 @@ impl Daemon {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_timers(&mut self) {
|
fn check_timers(&mut self) {
|
||||||
self.timers.retain(|timer| {
|
self.timers.retain(|timer| !timer.is_expired());
|
||||||
if timer.is_expired() {
|
|
||||||
timer.handle_expiration(self.notify);
|
|
||||||
}
|
|
||||||
|
|
||||||
!timer.is_expired()
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(pomodoro) = &mut self.pomodoro {
|
if let Some(pomodoro) = &mut self.pomodoro {
|
||||||
if pomodoro.is_expired() {
|
pomodoro.update();
|
||||||
pomodoro.handle_expiration(self.notify);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,11 +189,13 @@ impl Daemon {
|
||||||
for sig in signal_hook::consts::TERM_SIGNALS {
|
for sig in signal_hook::consts::TERM_SIGNALS {
|
||||||
signal_hook::flag::register(*sig, Arc::clone(&term))?;
|
signal_hook::flag::register(*sig, Arc::clone(&term))?;
|
||||||
}
|
}
|
||||||
while !term.load(Ordering::Relaxed) {
|
self.main_loop(term)
|
||||||
while let Ok((stream, _)) = self.listener.accept() {
|
|
||||||
if let Err(e) = self.handle_stream(&stream) {
|
|
||||||
println!("Error while handling stream: {}", e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn main_loop(&mut self, term: Arc<AtomicBool>) -> anyhow::Result<()> {
|
||||||
|
while !term.load(Ordering::Relaxed) {
|
||||||
|
if let Ok((stream, _)) = self.listener.accept() {
|
||||||
|
self.handle_stream(&stream)?;
|
||||||
}
|
}
|
||||||
self.check_timers();
|
self.check_timers();
|
||||||
sleep(Duration::from_millis(100));
|
sleep(Duration::from_millis(100));
|
||||||
|
@ -244,3 +216,24 @@ impl Drop for Daemon {
|
||||||
println!("Stopped successfully!");
|
println!("Stopped successfully!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for Answer {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
|
match self {
|
||||||
|
Answer::Ok => write!(f, ""),
|
||||||
|
Answer::Timers(timers) => {
|
||||||
|
if timers.is_empty() {
|
||||||
|
writeln!(f, "No timers running.")
|
||||||
|
} else {
|
||||||
|
let strings: Vec<String> =
|
||||||
|
timers.iter().map(|timer| timer.to_string()).collect();
|
||||||
|
writeln!(f, "{}", strings.join("\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Answer::Pomodoro(pomodoro) => match pomodoro {
|
||||||
|
Some(p) => write!(f, "{}", p),
|
||||||
|
None => write!(f, "No pomodoro running."),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use notify_rust::Notification;
|
||||||
|
|
||||||
pub fn getuid() -> u32 {
|
pub fn getuid() -> u32 {
|
||||||
unsafe { libc::getuid() }
|
unsafe { libc::getuid() }
|
||||||
}
|
}
|
||||||
|
@ -5,3 +7,10 @@ pub fn getuid() -> u32 {
|
||||||
pub fn run_path() -> String {
|
pub fn run_path() -> String {
|
||||||
format!("/run/user/{}", getuid())
|
format!("/run/user/{}", getuid())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_notifictation(msg: &str) {
|
||||||
|
match Notification::new().summary(" Timers").body(msg).show() {
|
||||||
|
Ok(_) => println!("Sent notification sucessfully."),
|
||||||
|
Err(_) => println!("Failed to send notification."),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
use notify_rust::Notification;
|
|
||||||
|
|
||||||
pub fn send_notifictation(msg: &str) {
|
|
||||||
match Notification::new().summary(" Timers").body(msg).show() {
|
|
||||||
Ok(_) => println!("Sent notification sucessfully."),
|
|
||||||
Err(_) => println!("Failed to send notification."),
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -64,8 +64,13 @@ impl Pomodoro {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_expiration(&mut self, notify: bool) {
|
pub fn update(&mut self) {
|
||||||
self.timer.handle_expiration(notify);
|
if self.timer.is_expired() {
|
||||||
|
self.switch();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn switch(&mut self) {
|
||||||
let duration = match self.status {
|
let duration = match self.status {
|
||||||
Status::Working => {
|
Status::Working => {
|
||||||
if self.pauses == self.pauses_till_long {
|
if self.pauses == self.pauses_till_long {
|
||||||
|
@ -90,8 +95,4 @@ impl Pomodoro {
|
||||||
};
|
};
|
||||||
self.timer = Timer::new(self.status.to_string().into_boxed_str(), duration);
|
self.timer = Timer::new(self.status.to_string().into_boxed_str(), duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_expired(&self) -> bool {
|
|
||||||
self.timer.is_expired()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
147
src/timer.rs
147
src/timer.rs
|
@ -1,10 +1,86 @@
|
||||||
use crate::notification::send_notifictation;
|
use crate::helper::send_notifictation;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{Display, Formatter},
|
fmt::{Display, Formatter},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
|
pub struct Timer {
|
||||||
|
pub name: Box<str>,
|
||||||
|
#[serde(with = "approx_instant")]
|
||||||
|
start: Instant,
|
||||||
|
duration: Duration,
|
||||||
|
state: State,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
|
pub enum State {
|
||||||
|
Running,
|
||||||
|
Paused,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timer {
|
||||||
|
/// Create a new [`Timer`] with the supplied name and duration
|
||||||
|
/// The timer is instantly started.
|
||||||
|
pub fn new(name: Box<str>, duration: Duration) -> Timer {
|
||||||
|
Timer {
|
||||||
|
name,
|
||||||
|
start: Instant::now(),
|
||||||
|
duration,
|
||||||
|
state: State::Running,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this [`Timer`] has expired
|
||||||
|
pub fn is_expired(&self) -> bool {
|
||||||
|
let expired = Instant::now() - self.start > self.duration;
|
||||||
|
if expired {
|
||||||
|
self.handle_expiration()
|
||||||
|
};
|
||||||
|
expired
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the remaining duration rounded to seconds of this [`Timer`].
|
||||||
|
pub fn remaining(&self) -> Duration {
|
||||||
|
if self.state == State::Paused {
|
||||||
|
return self.duration;
|
||||||
|
};
|
||||||
|
let exact = self.duration - (Instant::now() - self.start);
|
||||||
|
Duration::from_secs(exact.as_secs())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles the expiration of this [`Timer`]
|
||||||
|
fn handle_expiration(&self) {
|
||||||
|
let msg = format!("Timer {} has expired!", self.name);
|
||||||
|
println!("{}", &msg);
|
||||||
|
send_notifictation(msg.as_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggle [`State`] of this [`Timer`]
|
||||||
|
/// from [`State::Running`] to [`State::Paused`] and vice versa
|
||||||
|
pub fn toggle(&mut self) {
|
||||||
|
if self.state != State::Paused {
|
||||||
|
self.duration = self.remaining();
|
||||||
|
self.state = State::Paused;
|
||||||
|
} else {
|
||||||
|
self.start = Instant::now();
|
||||||
|
self.state = State::Running;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Timer {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} has {} remaining.",
|
||||||
|
self.name,
|
||||||
|
humantime::format_duration(self.remaining())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod approx_instant {
|
mod approx_instant {
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
@ -32,72 +108,3 @@ mod approx_instant {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
|
||||||
pub struct Timer {
|
|
||||||
pub name: Box<str>,
|
|
||||||
#[serde(with = "approx_instant")]
|
|
||||||
start: Instant,
|
|
||||||
duration: Duration,
|
|
||||||
state: State,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
|
||||||
pub enum State {
|
|
||||||
Running,
|
|
||||||
Paused,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Timer {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{} has {} remaining.",
|
|
||||||
self.name,
|
|
||||||
humantime::format_duration(self.remaining())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Timer {
|
|
||||||
pub fn new(name: Box<str>, duration: Duration) -> Timer {
|
|
||||||
Timer {
|
|
||||||
name,
|
|
||||||
start: Instant::now(),
|
|
||||||
duration,
|
|
||||||
state: State::Running,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_expired(&self) -> bool {
|
|
||||||
Instant::now() - self.start > self.duration
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the remaining duration rounded to seconds of this [`Timer`].
|
|
||||||
pub fn remaining(&self) -> Duration {
|
|
||||||
if self.state == State::Paused {
|
|
||||||
return self.duration;
|
|
||||||
};
|
|
||||||
let exact = self.duration - (Instant::now() - self.start);
|
|
||||||
Duration::from_secs(exact.as_secs())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Logs or notifies timer expiration
|
|
||||||
pub fn handle_expiration(&self, notify: bool) {
|
|
||||||
let msg = format!("Timer {} has expired!", self.name);
|
|
||||||
println!("{}", &msg);
|
|
||||||
if notify {
|
|
||||||
send_notifictation(msg.as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Toggles the timers state
|
|
||||||
pub fn toggle(&mut self) {
|
|
||||||
if self.state != State::Paused {
|
|
||||||
self.duration = self.remaining();
|
|
||||||
self.state = State::Paused;
|
|
||||||
} else {
|
|
||||||
self.start = Instant::now();
|
|
||||||
self.state = State::Running;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue