Skip to main content

ptsd/
utils.rs

1use chrono::{Datelike, Timelike};
2use prism::{Context, drawable::Drawable};
3
4pub use chrono::{DateTime, Local, Utc};
5
6// #[derive(Clone, Copy, Deserialize, Serialize, Debug)]
7// pub struct InternetConnection(pub bool);
8
9/// `Timestamp` contains the date time in an easy-to-read format.
10#[derive(Clone, Debug, PartialEq, Hash, Eq)]
11pub struct Timestamp(DateTime<Utc>);
12
13impl std::fmt::Display for Timestamp {
14    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15        write!(f, "{}", self.friendly())
16    }
17}
18
19impl Default for Timestamp {
20    fn default() -> Self {
21        Timestamp::new(Local::now())
22    }
23}
24
25impl Timestamp {
26    /// Create a `Timestamp` from a local [`DateTime<Local>`].
27    pub fn new(dt: DateTime<Local>) -> Self {Timestamp(dt.into())}
28
29    /// Create a `Timestamp` with date and time set as pending (`"-"`).
30    pub fn pending() -> (String, String) {
31        ("-".to_string(), "-".to_string())
32    }
33
34    /// Tries to convert the `Timestamp` into a `DateTime<Local>`.
35    ///
36    /// Parses the stored date and time strings using the format `M/D/YY H:MM AM/PM`.
37    pub fn as_local(&self) -> DateTime<Local> {
38        self.0.into()
39    }
40
41    /// Returns a human-readable, "direct" representation of the timestamp.
42    ///
43    /// Formats the timestamp based on how recent it is:
44    /// - **Today**: `"H:MM am/pm"`
45    /// - **Yesterday**: `"yesterday, H:MM am/pm"`
46    /// - **Same week**: day of the week (e.g., `"Monday"`)
47    /// - **Same year**: `"Month D"` (e.g., `"August 16"`)
48    /// - **Otherwise**: `"MM/DD/YY"`
49    ///
50    /// Returns `None` if the timestamp cannot be converted to a local datetime.
51    pub fn friendly(&self) -> String {
52        let dt = self.as_local();
53        let today = Local::now().date_naive();
54        let date = dt.date_naive();
55        let hour = dt.hour();
56        let minute = dt.minute();
57        let (hour12, am_pm) = match hour == 0 {
58            true => (12, "AM"),
59            false if hour < 12 => (hour, "AM"),
60            false if hour == 12 => (12, "PM"),
61            false => (hour - 12, "PM")
62        };
63
64        let the_time = format!("{hour12}:{minute:02} {am_pm}");
65
66        match date == today {
67            true => the_time,
68            false if date == today.pred_opt().unwrap_or(today) => format!("yesterday, {the_time}"),
69            false if date.iso_week() == today.iso_week() => format!("{}", dt.format("%A")),
70            false if date.year() == today.year() => format!("{}", dt.format("%B %-d")),
71            false => format!("{}", dt.format("%m/%d/%y")),
72        }
73    }
74
75    pub fn date_friendly(&self) -> String {
76        let dt = self.as_local();
77        let today = Local::now().date_naive();
78        let date = dt.date_naive();
79
80        if date == today {return "Today".to_string();}
81        if date.iso_week() == today.iso_week() { return format!("{}", dt.format("%A")); }
82        if date.year() == today.year() { return format!("{}", dt.format("%B %-d")); }
83        format!("{}", dt.format("%m/%d/%y"))
84    }
85
86    /// Returns the date.
87    pub fn date(&self) -> String {self.0.format("%-m/%-d/%y").to_string()}
88    /// Returns the time.
89    pub fn time(&self) -> String {self.0.format("%-I:%M %p").to_string()}
90}
91
92// impl From<String> for PelicanError {
93//     fn from(s: String, ap: impl AppPage) -> Self {
94//         PelicanError::Err(s, ap)
95//     }
96// }
97
98#[derive(Debug, Clone)]
99pub struct TitleSubtitle {
100    pub title: String, 
101    pub subtitle: Option<String>
102}
103
104impl TitleSubtitle {
105    pub fn new(title: &str, subtitle: Option<&str>) -> Self {
106        TitleSubtitle{
107            title: title.to_string(), 
108            subtitle: subtitle.map(|s| s.to_string())
109        }
110    }
111}
112
113
114pub trait ValidationFn: FnMut(&Vec<Box<dyn Drawable>>) -> bool + 'static {
115    fn clone_box(&self) -> Box<dyn ValidationFn>;
116}
117
118impl<F> ValidationFn for F where F: FnMut(&Vec<Box<dyn Drawable>>) -> bool + Clone + 'static {
119    fn clone_box(&self) -> Box<dyn ValidationFn> { Box::new(self.clone()) }
120}
121
122impl Clone for Box<dyn ValidationFn> { fn clone(&self) -> Self { self.as_ref().clone_box() } }
123
124impl std::fmt::Debug for dyn ValidationFn {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {  write!(f, "ValidationFn...") }
126}
127
128
129pub trait Callback: FnMut(&mut Context) + 'static {
130    fn clone_box(&self) -> Box<dyn Callback>;
131}
132
133impl PartialEq for dyn Callback{fn eq(&self, _: &Self) -> bool {true}}
134
135impl<F> Callback for F where F: FnMut(&mut Context) + Clone + 'static {
136    fn clone_box(&self) -> Box<dyn Callback> {
137        Box::new(self.clone())
138    }
139}
140
141impl Clone for Box<dyn Callback> {
142    fn clone(&self) -> Self {
143        self.as_ref().clone_box()
144    }
145}
146
147impl std::fmt::Debug for dyn Callback {
148    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149        write!(f, "Clonable Closure")
150    }
151}