use crate::db::models::Periods; use chrono::{NaiveTime, Timelike}; use serde::{Deserialize, Serialize}; use sqlx::database::HasArguments; use sqlx::encode::IsNull; use sqlx::error::BoxDynError; use sqlx::sqlite::{SqliteTypeInfo, SqliteValueRef}; use sqlx::{Decode, Encode, Sqlite, Type}; #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] 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)) // } //} impl Type<Sqlite> for Periods { fn type_info() -> SqliteTypeInfo { <&[u8] as Type<Sqlite>>::type_info() } fn compatible(ty: &SqliteTypeInfo) -> bool { <&[u8] as Type<Sqlite>>::compatible(ty) } } impl<'q> Encode<'q, Sqlite> for Periods { //noinspection DuplicatedCode fn encode_by_ref(&self, buf: &mut <Sqlite as HasArguments<'q>>::ArgumentBuffer) -> IsNull { <&Vec<u8> as Encode<Sqlite>>::encode(&Vec::from(self), buf) } } impl<'r> Decode<'r, Sqlite> for Periods { fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> { let blob = <&[u8] as Decode<Sqlite>>::decode(value)?; Ok(Periods::from(Vec::from(blob))) } } impl From<&Periods> for Vec<u8> { fn from(periods: &Periods) -> Vec<u8> { periods .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() } } impl From<Vec<u8>> for Periods { fn from(value: Vec<u8>) -> Self { let mut vec = Vec::new(); for i in (3..value.len()).step_by(4) { let start_val_h: u32 = value[i - 3] as u32; let start_val_m: u32 = value[i - 2] as u32; let end_val_h: u32 = value[i - 1] as u32; let end_val_m: u32 = value[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(), }); } Periods(vec) } }