1use crate::times::DateRange;
2
3use chrono::{Date, DateTime, Datelike, FixedOffset, Local, TimeZone};
4
5use std::env;
6
7use std::io::{ErrorKind, Read};
8
9use std::fs::{self, File, Metadata, OpenOptions};
10use std::path::Path;
11use std::time::SystemTime;
12
13use regex::Regex;
14
15pub fn get_metadata_created(metadata: Metadata) -> DateTime<FixedOffset> {
17 match metadata.created() {
18 Ok(created_at) => system_time_to_datetime(&created_at),
19 Err(e) => panic!("err getting session metadata: {:?}", e),
20 }
21}
22
23pub fn system_time_to_datetime(time: &SystemTime) -> DateTime<FixedOffset> {
25 match time.duration_since(SystemTime::UNIX_EPOCH) {
26 Ok(duration) => local_to_fixed_offset(Local.timestamp(duration.as_secs() as i64, 0)),
27 Err(e) => panic!("error getting SystemTime seconds: {}", e),
28 }
29}
30
31pub fn format_datetime(time: &DateTime<FixedOffset>) -> String {
32 time.to_rfc3339()
33}
34
35pub fn datetime_from_str(time: &str) -> DateTime<FixedOffset> {
36 DateTime::parse_from_rfc3339(time)
37 .unwrap_or_else(|e| panic!("failed to parse datetime {}: {}", time, e))
38}
39
40pub fn get_home_dir() -> String {
42 env::var("HOME").unwrap_or_else(|e| panic!("error getting $HOME env variable: {}", e))
43}
44
45pub fn create_dir(dir: &str) {
48 fs::create_dir_all(&dir).unwrap_or_else(|e| {
49 if e.kind() != ErrorKind::AlreadyExists {
51 panic!("could not create {} directory: {}", dir, e);
52 }
53 });
54}
55
56pub fn create_or_open_file(path: &str) -> File {
59 OpenOptions::new()
60 .create(true)
61 .append(true)
62 .open(&path)
63 .expect(&format!("Error opening {}", &path))
64}
65
66pub fn get_length_hours(start: &DateTime<FixedOffset>, end: &DateTime<FixedOffset>) -> f64 {
68 ((end.timestamp() - start.timestamp()) as f64) / 3600.0
69}
70
71pub fn get_file_contents(path: &Path) -> String {
72 let mut file = match File::open(&path) {
73 Ok(file) => file,
74 Err(e) => {
75 eprintln!("error opening {}: {}", path.display(), e);
76 std::process::exit(1);
77 }
78 };
79
80 let mut contents = String::new();
81 if let Err(e) = file.read_to_string(&mut contents) {
82 eprintln!("error reading {}: {}", path.display(), e);
83 std::process::exit(1);
84 }
85
86 contents
87}
88
89pub fn datetime_to_readable_str(date: &DateTime<FixedOffset>) -> String {
92 date.format("%Y-%m-%d %H:%M:%S").to_string()
93}
94
95pub fn get_start_date() -> DateTime<FixedOffset> {
96 local_to_fixed_offset(Local.ymd(1970, 1, 1).and_hms(0, 0, 0))
97}
98
99pub fn get_date_from_arg(date_arg: &str, end: bool) -> DateTime<FixedOffset> {
104 let get_time = |dt: Date<Local>| {
105 if !end {
106 dt.and_hms(0, 0, 0)
107 } else {
108 dt.and_hms(23, 59, 59)
109 }
110 };
111
112 if date_arg == "today" {
113 return local_to_fixed_offset(get_time(Local::today()));
114 }
115
116 let re = Regex::new(r"^(\d{4}-)?(\d{1,2})-(\d{1,2})$").unwrap();
117
118 let caps = re
119 .captures(date_arg)
120 .expect(&format!("{} is not a valid date", date_arg));
121
122 let year = match caps.get(1) {
123 Some(_) => caps[1][0..4].parse().unwrap(),
126 None => Local::today().year(),
128 };
129
130 let month: u32 = caps[2].parse().unwrap();
131 let day: u32 = caps[3].parse().unwrap();
132
133 local_to_fixed_offset(get_time(Local.ymd(year, month, day)))
134}
135
136pub fn local_to_fixed_offset(date: DateTime<Local>) -> DateTime<FixedOffset> {
137 DateTime::parse_from_rfc3339(&date.to_rfc3339()).unwrap()
144}
145
146pub fn parse_date_range(date_range: &str) -> Result<DateRange, &str> {
154 let dates = date_range.split("..").collect::<Vec<&str>>();
155
156 let start_date = get_start_date();
157
158 if dates.len() == 1 {
159 Ok(DateRange(start_date, get_date_from_arg(dates[0], true)))
161 } else if dates.len() == 2 {
162 match (dates[0], dates[1]) {
163 ("", "") => Err("Invalid date-range provided"),
164 ("", _) => Ok(DateRange(start_date, get_date_from_arg(dates[1], true))),
165 (_, "") => Ok(DateRange(
166 get_date_from_arg(dates[0], false),
167 get_date_from_arg("today", true),
168 )),
169 (_, _) => Ok(DateRange(
170 get_date_from_arg(dates[0], false),
171 get_date_from_arg(dates[1], true),
172 )),
173 }
174 } else {
175 Err("Invalid date-range provided")
176 }
177}
178
179#[cfg(test)]
180mod test {
181 use super::*;
182
183 #[test]
184 fn test_date_range() {
185 let DateRange(start, end) = parse_date_range("2021-12-01..2021-12-13").unwrap();
186
187 assert_eq!(
188 Local.ymd(2021, 12, 1).and_hms(0, 0, 0).timestamp(),
189 start.timestamp()
190 );
191
192 assert_eq!(
193 Local.ymd(2021, 12, 13).and_hms(23, 59, 59).timestamp(),
194 end.timestamp()
195 );
196 }
197
198 #[test]
199 fn test_date_range_end_only() {
200 let DateRange(start, end) = parse_date_range("2021-12-01").unwrap();
201
202 assert_eq!(
203 Local.ymd(1970, 1, 1).and_hms(0, 0, 0).timestamp(),
204 start.timestamp()
205 );
206
207 assert_eq!(
208 Local.ymd(2021, 12, 1).and_hms(23, 59, 59).timestamp(),
209 end.timestamp()
210 );
211 }
212
213 #[test]
214 fn test_date_range_start_only() {
215 let DateRange(start, end) = parse_date_range("2021-12-01..").unwrap();
216
217 assert_eq!(
218 Local.ymd(2021, 12, 1).and_hms(0, 0, 0).timestamp(),
219 start.timestamp()
220 );
221
222 assert_eq!(
223 Local::today().and_hms(23, 59, 59).timestamp(),
224 end.timestamp()
225 );
226 }
227
228 #[test]
229 fn test_date_range_end_only_with_dots() {
230 let DateRange(start, end) = parse_date_range("..12-01").unwrap();
232
233 assert_eq!(
234 Local.ymd(1970, 1, 1).and_hms(0, 0, 0).timestamp(),
235 start.timestamp()
236 );
237
238 assert_eq!(
239 Local.ymd(2021, 12, 1).and_hms(23, 59, 59).timestamp(),
240 end.timestamp()
241 );
242 }
243
244 #[test]
245 fn test_date_range_today() {
246 let DateRange(start, end) = parse_date_range("today").unwrap();
247
248 assert_eq!(
249 Local.ymd(1970, 1, 1).and_hms(0, 0, 0).timestamp(),
250 start.timestamp()
251 );
252
253 assert_eq!(
254 Local::today().and_hms(23, 59, 59).timestamp(),
255 end.timestamp()
256 );
257 }
258
259 #[test]
260 fn test_date_range_today_2() {
261 let DateRange(start, end) = parse_date_range("today..2021-12-31").unwrap();
262
263 assert_eq!(
264 Local::today().and_hms(0, 0, 0).timestamp(),
265 start.timestamp()
266 );
267
268 assert_eq!(
269 Local.ymd(2021, 12, 31).and_hms(23, 59, 59).timestamp(),
270 end.timestamp()
271 );
272 }
273
274 #[test]
275 fn test_date_range_today_3() {
276 let DateRange(start, end) = parse_date_range("2021-12-1..today").unwrap();
277
278 assert_eq!(
279 Local.ymd(2021, 12, 1).and_hms(0, 0, 0).timestamp(),
280 start.timestamp()
281 );
282
283 assert_eq!(
284 Local::today().and_hms(23, 59, 59).timestamp(),
285 end.timestamp()
286 );
287 }
288
289 #[test]
290 fn test_get_date_from_arg() {
291 let d = get_date_from_arg("2021-12-01", false);
292
293 assert_eq!(
294 Local.ymd(2021, 12, 1).and_hms(0, 0, 0).timestamp(),
295 d.timestamp()
296 );
297 }
298
299 #[test]
300 fn test_get_date_from_arg_end() {
301 let d = get_date_from_arg("2021-12-01", true);
302
303 assert_eq!(
304 Local.ymd(2021, 12, 1).and_hms(23, 59, 59).timestamp(),
305 d.timestamp()
306 );
307 }
308
309 #[test]
310 fn test_get_date_from_arg_one_digit_day() {
311 let d = get_date_from_arg("2021-12-1", false);
312
313 assert_eq!(
314 Local.ymd(2021, 12, 1).and_hms(0, 0, 0).timestamp(),
315 d.timestamp()
316 );
317 }
318
319 #[test]
320 fn test_get_date_from_arg_one_digit_month() {
321 let d = get_date_from_arg("2021-1-10", false);
322
323 assert_eq!(
324 Local.ymd(2021, 1, 10).and_hms(0, 0, 0).timestamp(),
325 d.timestamp()
326 );
327 }
328
329 #[test]
330 fn test_get_date_from_arg_one_digit_month_and_day() {
331 let d = get_date_from_arg("2021-1-1", false);
332
333 assert_eq!(
334 Local.ymd(2021, 1, 1).and_hms(0, 0, 0).timestamp(),
335 d.timestamp()
336 );
337 }
338
339 #[test]
340 fn test_get_date_from_arg_no_year() {
341 let d = get_date_from_arg("12-01", false);
342
343 assert_eq!(
344 Local
345 .ymd(Local::today().year(), 12, 1)
346 .and_hms(0, 0, 0)
347 .timestamp(),
348 d.timestamp()
349 );
350 }
351
352 #[test]
353 fn test_get_date_from_arg_no_year_one_digit_day() {
354 let d = get_date_from_arg("12-5", false);
355
356 assert_eq!(
357 Local
358 .ymd(Local::today().year(), 12, 5)
359 .and_hms(0, 0, 0)
360 .timestamp(),
361 d.timestamp()
362 );
363 }
364
365 #[test]
366 fn test_get_date_from_arg_today() {
367 let d = get_date_from_arg("today", false);
368
369 assert_eq!(Local::today().and_hms(0, 0, 0).timestamp(), d.timestamp());
370 }
371}