caffeine_cli/caffeine/
mod.rs1use core::fmt;
2use std::{
3 error::Error,
4 fs,
5 path::PathBuf,
6 process::Command,
7 thread,
8 time::{Duration, SystemTime, UNIX_EPOCH},
9};
10
11use chrono::{DateTime, Utc};
12use daemonize::Daemonize;
13use serde::{Deserialize, Serialize};
14
15#[derive(Serialize, Deserialize, Debug, Clone)]
16pub struct CaffeineSession {
17 pub proccess_id: String,
18 pub start_time: u64,
19 pub session_length: Option<u64>,
20}
21
22#[derive(Debug)]
23pub enum SessionError {
24 ConflictingSession,
25 NoActiveSession,
26}
27
28impl Error for SessionError {}
29
30impl fmt::Display for SessionError {
31 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32 match self {
33 SessionError::ConflictingSession => write!(f, "A session already exists"),
34 SessionError::NoActiveSession => write!(f, "Couldn't find an active session"),
35 }
36 }
37}
38
39pub fn get_session_path() -> PathBuf {
41 PathBuf::from("/tmp/caffeine-session.json")
42}
43
44pub fn init_session(seconds: Option<u64>) -> Result<CaffeineSession, Box<dyn Error>> {
46 let seconds_str = if let Some(seconds) = seconds {
47 seconds.to_string()
48 } else {
49 String::from("infinity")
50 };
51
52 let process = Command::new("systemd-inhibit")
53 .arg("--what=idle")
54 .arg("sleep")
55 .arg(seconds_str)
56 .spawn()?;
57
58 let process_id = process.id();
59
60 let start_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
61
62 Ok(CaffeineSession {
63 proccess_id: process_id.to_string(),
64 start_time,
65 session_length: if let Some(seconds) = seconds {
66 Some(seconds)
67 } else {
68 None
69 },
70 })
71}
72
73pub fn init_protected_session(seconds: Option<u64>) -> Result<CaffeineSession, Box<dyn Error>> {
75 let session = get_session();
76
77 if session.is_some() {
78 return Err(Box::new(SessionError::ConflictingSession));
79 }
80
81 let new_session = init_session(seconds)?;
82
83 let json = serde_json::to_string(&new_session)?;
84
85 fs::write(get_session_path(), &json)?;
86
87 if let Some(seconds) = seconds {
88 let daemon = Daemonize::new();
89
90 if let Ok(_) = daemon.start() {
91 thread::sleep(Duration::from_secs(seconds));
92
93 let _ = fs::remove_file(get_session_path());
94 }
95 }
96
97 Ok(new_session)
98}
99
100pub fn end_session(session: CaffeineSession) -> Result<(), Box<dyn Error>> {
102 Command::new("kill")
103 .arg(&session.proccess_id)
104 .spawn()
105 .expect("Error killing caffeine session");
106
107 Ok(())
108}
109
110pub fn end_protected_session() -> Result<(), Box<dyn Error>> {
111 let session = get_session();
112
113 if session.is_none() {
114 return Err(Box::new(SessionError::NoActiveSession));
115 }
116
117 end_session(session.unwrap())?;
118
119 fs::remove_file(get_session_path())?;
120
121 Ok(())
122}
123
124pub fn get_session() -> Option<CaffeineSession> {
126 let session_json = fs::read_to_string(get_session_path());
127
128 if session_json.is_err() {
129 return None;
130 }
131
132 let session = serde_json::from_str(&session_json.unwrap());
133
134 if session.is_err() {
135 return None;
136 }
137
138 Some(session.unwrap())
139}
140
141impl CaffeineSession {
142 pub fn get_elapsed_time(&self) -> String {
143 let now = SystemTime::now()
144 .duration_since(UNIX_EPOCH)
145 .unwrap()
146 .as_secs();
147
148 let elapsed = UNIX_EPOCH + Duration::from_secs(now - self.start_time);
149
150 let datetime: DateTime<Utc> = elapsed.into();
151
152 datetime.format("%Hh %Mm %Ss").to_string()
153 }
154
155 pub fn get_session_length(&self) -> Option<String> {
156 return if let Some(session_length) = self.session_length {
157 let timestamp = UNIX_EPOCH + Duration::from_secs(session_length);
158
159 let datetime: DateTime<Utc> = timestamp.into();
160
161 Some(datetime.format("%Hh %Mm %Ss").to_string())
162 } else {
163 None
164 };
165 }
166
167 pub fn get_remaining_time(&self) -> Option<String> {
168 return if let Some(session_length) = self.session_length {
169 let now = SystemTime::now()
170 .duration_since(UNIX_EPOCH)
171 .unwrap()
172 .as_secs();
173
174 let timestamp =
175 UNIX_EPOCH + Duration::from_secs((session_length + self.start_time) - now);
176
177 let datetime: DateTime<Utc> = timestamp.into();
178
179 Some(datetime.format("%Hh %Mm %Ss").to_string())
180 } else {
181 None
182 };
183 }
184}