1use chrono::{DateTime, Local};
2use cron::Schedule;
3use serde::{Deserialize, Serialize};
4use std::str::FromStr;
5use uuid::Uuid;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Reminder {
9 pub id: Uuid,
10 pub title: String,
11 pub description: Option<String>,
12 pub schedule: ReminderSchedule,
13 pub created_at: DateTime<Local>,
14 pub next_trigger: Option<DateTime<Local>>,
15 pub completed: bool,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub enum ReminderSchedule {
20 OneTime(DateTime<Local>),
21 Cron(String),
22}
23
24impl Reminder {
25 pub fn new_one_time(
26 title: String,
27 description: Option<String>,
28 time: DateTime<Local>,
29 ) -> Self {
30 Self {
31 id: Uuid::new_v4(),
32 title,
33 description,
34 schedule: ReminderSchedule::OneTime(time),
35 created_at: Local::now(),
36 next_trigger: Some(time),
37 completed: false,
38 }
39 }
40
41 pub fn new_cron(
42 title: String,
43 description: Option<String>,
44 cron_expr: String,
45 ) -> anyhow::Result<Self> {
46 let schedule = Schedule::from_str(&cron_expr)?;
47 let next = schedule.upcoming(Local).next();
48
49 Ok(Self {
50 id: Uuid::new_v4(),
51 title,
52 description,
53 schedule: ReminderSchedule::Cron(cron_expr),
54 created_at: Local::now(),
55 next_trigger: next,
56 completed: false,
57 })
58 }
59
60 pub fn calculate_next_trigger(&mut self) {
61 match &self.schedule {
62 ReminderSchedule::OneTime(_) => {
63 self.completed = true;
64 self.next_trigger = None;
65 }
66 ReminderSchedule::Cron(expr) => {
67 if let Ok(schedule) = Schedule::from_str(expr) {
68 self.next_trigger = schedule.upcoming(Local).next();
69 }
70 }
71 }
72 }
73
74 pub fn is_due(&self) -> bool {
75 if self.completed {
76 return false;
77 }
78 if let Some(next) = self.next_trigger {
79 return Local::now() >= next;
80 }
81 false
82 }
83}