use crate::db::models::Periods; use chrono::{NaiveTime, Timelike}; use diesel::deserialize::FromSql; use diesel::serialize::{IsNull, Output, ToSql}; use diesel::sql_types::Binary; use diesel::sqlite::Sqlite; use diesel::{deserialize, serialize}; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, AsExpression, FromSqlRow, PartialEq, Clone)] #[diesel(sql_type = Binary)] pub struct Period { #[serde(with = "period_format")] pub start: NaiveTime, #[serde(with = "period_format")] pub end: NaiveTime, } mod period_format { use chrono::NaiveTime; use serde::{self, Deserialize, Deserializer, Serializer}; const FORMAT: &str = "%H:%M"; pub fn serialize<S>(time: &NaiveTime, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { let s = format!("{}", time.format(FORMAT)); serializer.serialize_str(&s) } pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveTime, D::Error> where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; NaiveTime::parse_from_str(&s, FORMAT).map_err(serde::de::Error::custom) } } impl Period { pub fn new(start: NaiveTime, end: NaiveTime) -> Self { Period { start, end } } pub fn new_on() -> Self { Period { start: NaiveTime::from_hms_opt(0, 0, 0).unwrap(), end: NaiveTime::from_hms_opt(0, 0, 0).unwrap(), } } } impl ToSql<Binary, Sqlite> for Periods where Vec<u8>: ToSql<Binary, Sqlite>, { fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result { let periods_u8: Vec<u8> = self .0 .iter() .flat_map(|period| { let vec = vec![ period.start.hour() as u8, period.start.minute() as u8, period.end.hour() as u8, period.end.minute() as u8, ]; vec }) .collect(); out.set_value(periods_u8); Ok(IsNull::No) } } impl<DB> FromSql<Binary, DB> for Periods where DB: diesel::backend::Backend, Vec<u8>: FromSql<Binary, DB>, { fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> { let blob: Vec<u8> = Vec::from_sql(bytes).unwrap(); let mut vec = Vec::new(); for i in (3..blob.len()).step_by(4) { let start_val_h: u32 = blob[i - 3] as u32; let start_val_m: u32 = blob[i - 2] as u32; let end_val_h: u32 = blob[i - 1] as u32; let end_val_m: u32 = blob[i] as u32; vec.push(Period { start: NaiveTime::from_hms_opt(start_val_h, start_val_m, 0).unwrap(), end: NaiveTime::from_hms_opt(end_val_h, end_val_m, 0).unwrap(), }); } Ok(Periods(vec)) } }