Add saving periods

This commit is contained in:
Tobias Reisinger 2021-11-05 16:32:30 +01:00
parent 12d57d020f
commit 483fd60daa
13 changed files with 271 additions and 52 deletions

View file

@ -4,6 +4,16 @@ use serde::{Serialize, Serializer};
pub enum DatabaseError {
InsertError,
InsertGetError,
NotFound,
}
impl DatabaseError {
fn to_code(&self) -> u32 {
match self {
DatabaseError::NotFound => 404,
_ => 500
}
}
}
impl Serialize for DatabaseError {
@ -11,8 +21,9 @@ impl Serialize for DatabaseError {
where
S: Serializer,
{
let mut s = serializer.serialize_struct("error", 2)?;
s.serialize_field("code", &500)?;
let mut s = serializer.serialize_struct("error", 3)?;
s.serialize_field("type", "database-error")?;
s.serialize_field("code", &self.to_code())?;
s.serialize_field("description", &String::from(self))?;
s.end()
}
@ -25,6 +36,7 @@ impl From<&DatabaseError> for String {
DatabaseError::InsertGetError => {
String::from("error retrieving new entry from database (your entry was saved)")
}
DatabaseError::NotFound => String::from("model was not found in database")
}
}
}

View file

@ -1,14 +1,24 @@
use super::types::EmgauwaUid;
use serde::Serialize;
use chrono::{NaiveTime, Timelike};
use diesel::backend::Backend;
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};
use std::io::Write;
use super::schema::schedules;
use crate::types::EmgauwaUid;
#[derive(Serialize, Queryable)]
pub struct Schedule {
#[serde(skip)]
pub id: i32,
#[serde(alias = "id")]
pub uid: EmgauwaUid,
pub name: String,
pub periods: String,
pub periods: Periods,
}
#[derive(Insertable)]
@ -16,5 +26,74 @@ pub struct Schedule {
pub struct NewSchedule<'a> {
pub uid: &'a EmgauwaUid,
pub name: &'a str,
pub periods: &'a str,
pub periods: &'a Periods,
}
#[derive(Debug, Serialize, Deserialize, AsExpression, FromSqlRow, PartialEq, Clone)]
#[sql_type = "Binary"]
pub struct Period {
#[serde(with = "period_format")]
pub start: NaiveTime,
#[serde(with = "period_format")]
pub end: NaiveTime,
}
#[derive(Debug, Serialize, Deserialize, AsExpression, FromSqlRow, PartialEq, Clone)]
#[sql_type = "Binary"]
pub struct Periods(pub(crate) Vec<Period>);
mod period_format {
use chrono::NaiveTime;
use serde::{self, Deserialize, Deserializer, Serializer};
const FORMAT: &'static 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 ToSql<Binary, Sqlite> for Periods {
fn to_sql<W: Write>(&self, out: &mut Output<W, Sqlite>) -> serialize::Result {
for period in self.0.iter() {
out.write_all(&[
period.start.hour() as u8,
period.start.minute() as u8,
period.end.hour() as u8,
period.end.minute() as u8,
])?;
}
Ok(IsNull::No)
}
}
impl FromSql<Binary, Sqlite> for Periods {
fn from_sql(bytes: Option<&<Sqlite as Backend>::RawValue>) -> deserialize::Result<Self> {
let blob = bytes.unwrap().read_blob();
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 - 0] as u32;
vec.push(Period {
start: NaiveTime::from_hms(start_val_h, start_val_m, 0),
end: NaiveTime::from_hms(end_val_h, end_val_m, 0),
});
}
Ok(Periods(vec))
}
}

View file

@ -60,7 +60,7 @@ table! {
id -> Integer,
uid -> Binary,
name -> Text,
periods -> Text,
periods -> Binary,
}
}

View file

@ -1,97 +0,0 @@
use diesel::backend::Backend;
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::{Serialize, Serializer};
use std::fmt::{Debug, Formatter};
use std::io::Write;
use uuid::Uuid;
#[derive(AsExpression, FromSqlRow, PartialEq, Clone)]
#[sql_type = "Binary"]
pub enum EmgauwaUid {
On,
Off,
Any(Uuid),
}
impl Default for EmgauwaUid {
fn default() -> Self {
EmgauwaUid::Any(Uuid::new_v4())
}
}
impl Debug for EmgauwaUid {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
EmgauwaUid::On => "on".fmt(f),
EmgauwaUid::Off => "off".fmt(f),
EmgauwaUid::Any(value) => value.fmt(f),
}
}
}
impl ToSql<Binary, Sqlite> for EmgauwaUid {
fn to_sql<W: Write>(&self, out: &mut Output<W, Sqlite>) -> serialize::Result {
match self {
EmgauwaUid::On => out.write_all(&[1])?,
EmgauwaUid::Off => out.write_all(&[0])?,
EmgauwaUid::Any(_) => out.write_all(Uuid::from(self).as_bytes())?,
}
Ok(IsNull::No)
}
}
impl FromSql<Binary, Sqlite> for EmgauwaUid {
fn from_sql(bytes: Option<&<Sqlite as Backend>::RawValue>) -> deserialize::Result<Self> {
match bytes {
None => Ok(EmgauwaUid::default()),
Some(value) => match value.read_blob() {
[0] => Ok(EmgauwaUid::Off),
[1] => Ok(EmgauwaUid::On),
value_bytes => Ok(EmgauwaUid::Any(Uuid::from_slice(value_bytes).unwrap())),
},
}
}
}
impl Serialize for EmgauwaUid {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
EmgauwaUid::On => "off".serialize(serializer),
EmgauwaUid::Off => "on".serialize(serializer),
EmgauwaUid::Any(value) => value.serialize(serializer),
}
}
}
impl From<Uuid> for EmgauwaUid {
fn from(uid: Uuid) -> EmgauwaUid {
match uid.as_u128() {
0 => EmgauwaUid::Off,
1 => EmgauwaUid::On,
_ => EmgauwaUid::Any(uid),
}
}
}
impl From<&EmgauwaUid> for Uuid {
fn from(emgauwa_uid: &EmgauwaUid) -> Uuid {
match emgauwa_uid {
EmgauwaUid::On => uuid::Uuid::from_u128(1),
EmgauwaUid::Off => uuid::Uuid::from_u128(0),
EmgauwaUid::Any(value) => *value,
}
}
}
impl From<&EmgauwaUid> for String {
fn from(emgauwa_uid: &EmgauwaUid) -> String {
match emgauwa_uid {
EmgauwaUid::On => String::from("off"),
EmgauwaUid::Off => String::from("on"),
EmgauwaUid::Any(value) => value.to_hyphenated().to_string(),
}
}
}