use chrono::{Datelike, Timelike};
use prism::{Context, drawable::Drawable};
pub use chrono::{DateTime, Local, Utc};
#[derive(Clone, Debug, PartialEq, Hash, Eq)]
pub struct Timestamp(Option<DateTime<Utc>>);
impl std::fmt::Display for Timestamp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.friendly())
}
}
impl Default for Timestamp {
fn default() -> Self {
Timestamp::new(Some(Local::now()))
}
}
impl Timestamp {
pub fn new(dt: Option<DateTime<Local>>) -> Self {Timestamp(dt.map(|s| s.into()))}
pub fn pending() -> (String, String) {
("-".to_string(), "-".to_string())
}
pub fn as_local(&self) -> Option<DateTime<Local>> {
self.0.map(|dt| dt.into())
}
pub fn friendly(&self) -> String {
if let Some(dt) = self.as_local() {
let today = Local::now().date_naive();
let date = dt.date_naive();
let hour = dt.hour();
let minute = dt.minute();
let (hour12, am_pm) = match hour == 0 {
true => (12, "AM"),
false if hour < 12 => (hour, "AM"),
false if hour == 12 => (12, "PM"),
false => (hour - 12, "PM")
};
let the_time = format!("{hour12}:{minute:02} {am_pm}");
match date == today {
true => the_time,
false if date == today.pred_opt().unwrap_or(today) => format!("yesterday, {the_time}"),
false if date.iso_week() == today.iso_week() => format!("{}", dt.format("%A")),
false if date.year() == today.year() => format!("{}", dt.format("%B %-d")),
false => format!("{}", dt.format("%m/%d/%y")),
}
} else {"Pending".to_string()}
}
pub fn date(&self) -> String {
self.as_local().map(|dt| dt.format("%-m/%-d/%y").to_string()).unwrap_or("Pending".to_string())
}
pub fn time(&self) -> String {
self.as_local().map(|dt| dt.format("%-I:%M %p").to_string()).unwrap_or("Pending".to_string())
}
pub fn date_friendly(&self) -> String {
if let Some(dt) = self.as_local() {
let today = Local::now().date_naive();
let date = dt.date_naive();
if date == today {return "Today".to_string();}
if date.iso_week() == today.iso_week() { return format!("{}", dt.format("%A")); }
if date.year() == today.year() { return format!("{}", dt.format("%B %-d")); }
format!("{}", dt.format("%m/%d/%y"))
} else {"Pending".to_string()}
}
}
#[derive(Debug, Clone)]
pub struct TitleSubtitle {
pub title: String,
pub subtitle: Option<String>
}
impl TitleSubtitle {
pub fn new(title: &str, subtitle: Option<&str>) -> Self {
TitleSubtitle{
title: title.to_string(),
subtitle: subtitle.map(|s| s.to_string())
}
}
}
pub trait ValidationFn: FnMut(&mut Vec<Box<dyn Drawable>>) -> bool + 'static {
fn clone_box(&self) -> Box<dyn ValidationFn>;
}
impl<F> ValidationFn for F where F: FnMut(&mut Vec<Box<dyn Drawable>>) -> bool + Clone + 'static {
fn clone_box(&self) -> Box<dyn ValidationFn> { Box::new(self.clone()) }
}
impl Clone for Box<dyn ValidationFn> { fn clone(&self) -> Self { self.as_ref().clone_box() } }
impl std::fmt::Debug for dyn ValidationFn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "ValidationFn...") }
}
pub trait Callback: FnMut(&mut Context) + 'static {
fn clone_box(&self) -> Box<dyn Callback>;
}
impl PartialEq for dyn Callback{fn eq(&self, _: &Self) -> bool {true}}
impl<F> Callback for F where F: FnMut(&mut Context) + Clone + 'static {
fn clone_box(&self) -> Box<dyn Callback> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn Callback> {
fn clone(&self) -> Self {
self.as_ref().clone_box()
}
}
impl std::fmt::Debug for dyn Callback {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Clonable Closure")
}
}