take_breath/
lib.rs

1use std::time::Duration;
2pub const DEFAULT_WORK_DUR: Duration = Duration::from_secs(45 * 60);
3pub const DEFAULT_WORK_IDLE_TO_PAUSE: Duration = Duration::from_secs(2 * 60);
4pub const DEFAULT_REST_DUR: Duration = Duration::from_secs(15 * 60);
5
6#[cfg(feature = "config")]
7pub mod config {
8    use super::*;
9    use toml;
10    use std::path::Path;
11    use serde::{Serialize, Deserialize};
12    use std::time::Duration;
13    use std::error::Error;
14    use std::fs;
15
16    #[derive(Serialize, Deserialize, Default, Debug)]
17    pub struct Config {
18	#[serde(default)]
19	pub work_time: WorkTime,
20
21	#[serde(default)]
22	pub rest_time: RestTime,
23    }
24
25    impl Config {
26	pub fn from_file<T: AsRef<Path>>(path: T) -> Result<Self, Box<dyn Error>> {
27	    let file_data = fs::read_to_string(path)?;
28	    Ok(toml::from_str(&file_data)?)
29	}
30
31	pub fn apply_from_file<T: AsRef<Path>>(path: T) -> Self {
32	    match Self::from_file(path) {
33		Ok(config) => config,
34		Err(_) => Self::default(),
35	    }
36	}
37    }
38
39    #[derive(Serialize, Deserialize, Debug)]
40    pub struct WorkTime {
41	#[serde(with = "humantime_serde")]
42	#[serde(default = "WorkTime::default_duration")]
43	pub duration: Duration,
44
45	#[serde(with = "humantime_serde")]
46	#[serde(default = "WorkTime::default_idle_to_pause")]
47	pub idle_to_pause: Duration,
48    }
49
50    impl WorkTime {
51	fn default_duration() -> Duration {
52	    DEFAULT_WORK_DUR
53	}
54
55	fn default_idle_to_pause() -> Duration {
56	    DEFAULT_WORK_IDLE_TO_PAUSE
57	}
58    }
59
60    impl Default for WorkTime {
61	fn default() -> Self {
62	    Self {
63		duration: Self::default_duration(),
64		idle_to_pause: Self::default_idle_to_pause(),
65	    }
66	}
67    }
68
69    #[derive(Serialize, Deserialize, Debug)]
70    pub struct RestTime {
71	#[serde(with = "humantime_serde")]
72	#[serde(default = "RestTime::default_duration")]
73	pub duration: Duration,
74    }
75
76    impl RestTime {
77	fn default_duration() -> Duration {
78	    DEFAULT_REST_DUR
79	}
80    }
81
82    impl Default for RestTime {
83	fn default() -> Self {
84	    Self {
85		duration: Self::default_duration(),
86	    }
87	}
88    }
89}
90
91pub mod counter {
92    #[cfg(feature = "notify")]
93    use notify_rust::{Notification, Timeout};
94    use user_idle::UserIdle;
95    use std::time::Duration;
96    use std::thread;
97
98    /// Work time counter structure.
99    pub struct Work {
100	/// Work duration.
101	work_dur: Duration,
102
103	/// How much time computer have to be idle to disable work time counting.
104	idle_dur: Duration,
105    }
106
107    impl Work {
108	/// Returns a new work time counter.
109	pub fn new(work_dur: Duration,
110		   idle_dur: Duration) -> Self {
111	    Self {
112		work_dur,
113		idle_dur,
114	    }
115	}
116
117	/// Starts a work time counter.
118	pub fn count(&self) {
119	    let mut ctr = 0;
120	    let idle_dur = self.idle_dur.as_secs();
121	    let work_dur = self.work_dur.as_secs();
122
123	    self.trigger();
124	    while ctr < work_dur {
125		if idle_time() < idle_dur {
126		    wait(1);
127		    ctr += 1;
128		} else {
129		    if (ctr as i32 - idle_dur as i32) <= 0 {
130			ctr = 0;
131		    } else {
132			ctr -= idle_dur;
133		    }
134
135		    self.idle_trigger();
136		    loop {
137			if idle_time() == 0 {
138			    self.work_resumed_trigger();
139			    break;
140			}
141		    }
142		}
143	    }
144	}
145
146	/// Function to run when it is time to work.
147	fn trigger(&self) {
148	    println!("It's time to work.");
149	    #[cfg(feature = "notify")]
150	    Notification::new()
151		.summary("Take a breath: Work")
152		.body("It's time to work.")
153		.timeout(Timeout::Milliseconds(5000))
154		.show()
155		.unwrap();
156	}
157
158	/// Function to run when idle while work.
159	fn idle_trigger(&self) {
160	    println!("Idle while work counter started.");
161	    #[cfg(feature = "notify")]
162	    Notification::new()
163		.summary("Take a breath: Work Idle")
164		.body("Idle while work counter started.")
165		.timeout(Timeout::Milliseconds(5000))
166		.show()
167		.unwrap();
168	}
169
170	/// Function to run when work has been resumed.
171	fn work_resumed_trigger(&self) {
172	    println!("Work has been resumed.");
173	    #[cfg(feature = "notify")]
174	    Notification::new()
175		.summary("Take a breath: Work Resumed")
176		.body("Work has been resumed.")
177		.timeout(Timeout::Milliseconds(5000))
178		.show()
179		.unwrap();
180	}
181    }
182
183    /// Rest time counter structure.
184    pub struct Rest {
185	/// Rest duration.
186	rest_dur: Duration,
187    }
188
189    impl Rest {
190	/// Returns a new rest time counter.
191	pub fn new(rest_dur: Duration) -> Self {
192	    Self {
193		rest_dur,
194	    }
195	}
196
197	/// Starts a rest time counter.
198	pub fn count(&self) {
199	    let mut ctr = 0;
200	    let rest_dur = self.rest_dur.as_secs();
201
202	    self.trigger();
203	    while ctr < rest_dur {
204		wait(1);
205		if idle_time() > 0 {
206		    ctr += 1;
207		} else {
208		    self.short_rest_trigger(Duration::from_secs(ctr));
209		    loop {
210			if idle_time() > 0 {
211			    self.rest_resumed_trigger();
212			    break;
213			}
214		    }
215		}
216	    }
217	}
218
219	/// Function to run when it is time to take a breath.
220	fn trigger(&self) {
221	    println!("It's time ti take a breath.");
222	    #[cfg(feature = "notify")]
223	    Notification::new()
224		.summary("Take a breath")
225		.body("It's time to take a breath.")
226		.timeout(Timeout::Milliseconds(5000))
227		.show()
228		.unwrap();
229	}
230
231	/// Function to run when the rest is too short.
232	fn short_rest_trigger(&self, ctr: Duration) {
233	    let left = (self.rest_dur - ctr).as_secs() as f32 / 60.0;
234	    println!("You had too little rest! {:.2} minutes left.", left);
235	    #[cfg(feature = "notify")]
236	    Notification::new()
237		.summary("Take a breath")
238		.body(&format!("You had too little rest!\n{:.2} minutes left.", left))
239		.timeout(Timeout::Milliseconds(10000))
240		.show()
241		.unwrap();
242	}
243
244	/// Function to run when rest has been resumed.
245	fn rest_resumed_trigger(&self) {
246	    println!("Rest has been resumed.");
247	    #[cfg(feature = "notify")]
248	    Notification::new()
249		.summary("Take a breath")
250		.body("Rest has been resumed.")
251		.timeout(Timeout::Milliseconds(5000))
252		.show()
253		.unwrap();
254	}
255    }
256
257    /// Wait some time in seconds.
258    fn wait(secs: u64) {
259	thread::sleep(Duration::from_secs(secs));
260    }
261
262    /// Get computer idle time.
263    fn idle_time() -> u64 {
264	UserIdle::get_time().unwrap().as_seconds()
265    }
266}