1use chrono::{DateTime, Utc};
2use cron::Schedule;
3use notify_rust::Notification;
4use serde::{Deserialize, Serialize};
5use std::{fmt::Display, str::FromStr};
6
7use crate::RecollectError as Error;
8
9#[derive(Serialize, Deserialize, Debug)]
10pub struct Event {
11 schedule: String,
12 pub summary: String,
13 pub body: String,
14 pub disabled: bool,
15 pub upcoming: Option<DateTime<Utc>>,
16}
17
18#[derive(Debug)]
19pub struct Summary<'a> {
20 schedule: &'a str,
21 summary: &'a str,
22}
23
24impl Display for Event {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 write!(
27 f,
28 "Schedule: {schedule}\nSummary: {summary}\nBody: {body}\nDisabled: {disabled}",
29 schedule = self.schedule,
30 summary = self.summary,
31 body = self.body,
32 disabled = self.disabled,
33 )
34 }
35}
36
37impl<'a> Display for Summary<'a> {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 write!(
40 f,
41 "Schedule: {schedule}, Summary: {summary}",
42 schedule = self.schedule,
43 summary = self.summary,
44 )
45 }
46}
47
48impl PartialEq for Event {
49 fn eq(&self, other: &Self) -> bool {
50 self.schedule == other.schedule && self.summary == other.summary && self.body == other.body
51 }
52}
53
54pub fn validate_schedule<Sched: AsRef<str>>(schedule: Sched) -> Result<(), Error> {
56 let schedule = schedule.as_ref();
57
58 Schedule::from_str(schedule).map_err(|_| Error::ParseSchedError(schedule.to_owned()))?;
59
60 Ok(())
61}
62
63impl Event {
64 pub fn new<Sched, Sum, Body>(
71 schedule: Sched,
72 summary: Sum,
73 body: Body,
74 disabled: bool,
75 ) -> Result<Self, Error>
76 where
77 Sched: Into<String>,
78 Sum: Into<String>,
79 Body: Into<String>,
80 {
81 let schedule = schedule.into();
82 validate_schedule(&schedule)?;
83
84 Ok(Self {
85 schedule,
86 summary: summary.into(),
87 body: body.into(),
88 disabled,
89 upcoming: None,
90 })
91 }
92
93 pub fn schedule(&self) -> Schedule {
95 Schedule::from_str(&self.schedule).unwrap()
97 }
98
99 pub fn upcoming(&mut self) -> DateTime<Utc> {
105 self.upcoming.unwrap_or_else(|| self.update_upcoming())
106 }
107
108 pub fn upcoming_timeline(&self, n: usize) -> Vec<DateTime<Utc>> {
110 self.schedule().upcoming(Utc).take(n).collect()
111 }
112
113 pub fn update_upcoming(&mut self) -> DateTime<Utc> {
115 let upcoming = self.schedule().upcoming(Utc).take(1).next().unwrap();
116 self.upcoming = Some(upcoming);
117 upcoming
118 }
119
120 pub fn notification(&self) -> Notification {
122 Notification::new()
123 .summary(&self.summary)
124 .body(&self.body)
125 .finalize()
126 }
127
128 pub fn update_schedule<S: Into<String>>(&mut self, schedule: S) -> Result<(), Error> {
135 let sched = schedule.into();
136
137 Schedule::from_str(&sched)
138 .map(|_| self.schedule = sched.clone())
139 .map_err(|_| Error::ParseSchedError(sched))?;
140
141 Ok(())
142 }
143
144 pub fn summary(&self) -> Summary {
146 Summary {
147 schedule: &self.schedule,
148 summary: &self.summary,
149 }
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn test_schedule() {
159 let event = Event::new("* * * * * * *", "summary", "body", false).unwrap();
160
161 assert_eq!(
162 event.schedule(),
163 Schedule::from_str("* * * * * * *").unwrap()
164 );
165 }
166}