diff --git a/src/db.rs b/src/db.rs index ef01994..496d3e7 100644 --- a/src/db.rs +++ b/src/db.rs @@ -5,7 +5,7 @@ use sqlx::{Pool, Sqlite}; use crate::db::errors::DatabaseError; use crate::db::model_utils::Period; -use crate::db::models::{Periods, Schedule}; +use crate::db::schedules::{Periods, Schedule}; use crate::types::EmgauwaUid; pub mod errors; @@ -29,7 +29,7 @@ async fn init_schedule( periods: Periods, ) -> Result<(), DatabaseError> { trace!("Initializing schedule {:?}", name); - match schedules::get_schedule_by_uid(&mut pool.acquire().await.unwrap(), uid).await { + match Schedule::get_by_uid(&mut pool.acquire().await.unwrap(), uid).await { Ok(_) => Ok(()), Err(err) => match err { DatabaseError::NotFound => { diff --git a/src/db/model_utils.rs b/src/db/model_utils.rs index 8ec71c7..565a7d2 100644 --- a/src/db/model_utils.rs +++ b/src/db/model_utils.rs @@ -1,3 +1,4 @@ +use crate::db::schedules::Periods; use chrono::{NaiveTime, Timelike}; use serde::{Deserialize, Serialize}; use sqlx::database::HasArguments; @@ -6,8 +7,6 @@ use sqlx::error::BoxDynError; use sqlx::sqlite::{SqliteTypeInfo, SqliteValueRef}; use sqlx::{Decode, Encode, Sqlite, Type}; -use crate::db::models::Periods; - #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] pub struct Period { #[serde(with = "period_format")] @@ -52,54 +51,6 @@ impl Period { } } -//impl ToSql for Periods -//where -// Vec: ToSql, -//{ -// fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result { -// let periods_u8: Vec = 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 FromSql for Periods -//where -// DB: diesel::backend::Backend, -// Vec: FromSql, -//{ -// fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { -// let blob: Vec = 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 for Periods { fn type_info() -> SqliteTypeInfo { <&[u8] as Type>::type_info() diff --git a/src/db/models.rs b/src/db/models.rs index d783925..1c7e5c7 100644 --- a/src/db/models.rs +++ b/src/db/models.rs @@ -1,7 +1,4 @@ -use serde::{Deserialize, Serialize}; - -use crate::db::model_utils::Period; -use crate::types::EmgauwaUid; +use serde::Serialize; #[derive(Debug, Serialize)] pub struct Relay { @@ -10,19 +7,6 @@ pub struct Relay { // TODO } -#[derive(Debug, Serialize, Clone)] -pub struct Schedule { - #[serde(skip)] - pub id: i64, - #[serde(rename(serialize = "id"))] - pub uid: EmgauwaUid, - pub name: String, - pub periods: Periods, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] -pub struct Periods(pub Vec); - #[derive(Debug, Serialize, Clone)] pub struct Tag { pub id: i64, diff --git a/src/db/schedules.rs b/src/db/schedules.rs index 9951ccc..ae94555 100644 --- a/src/db/schedules.rs +++ b/src/db/schedules.rs @@ -1,3 +1,4 @@ +use serde_derive::{Deserialize, Serialize}; use std::borrow::Borrow; use std::ops::DerefMut; @@ -5,134 +6,148 @@ use sqlx::pool::PoolConnection; use sqlx::Sqlite; use crate::db::errors::DatabaseError; +use crate::db::model_utils::Period; use crate::db::models::*; use crate::db::tag::{create_junction_tag_schedule, create_tag}; use crate::types::EmgauwaUid; -pub async fn get_schedule_tags( - conn: &mut PoolConnection, - schedule: &Schedule, -) -> Result, DatabaseError> { - Ok(sqlx::query_scalar!("SELECT tag FROM tags INNER JOIN junction_tag ON junction_tag.tag_id = tags.id WHERE junction_tag.schedule_id = ?", schedule.id) - .fetch_all(conn.deref_mut()) - .await?) +#[derive(Debug, Serialize, Clone)] +pub struct Schedule { + #[serde(skip)] + pub id: i64, + #[serde(rename(serialize = "id"))] + pub uid: EmgauwaUid, + pub name: String, + pub periods: Periods, } -pub async fn get_schedules( - conn: &mut PoolConnection, -) -> Result, DatabaseError> { - Ok(sqlx::query_as!(Schedule, "SELECT * FROM schedules") - .fetch_all(conn.deref_mut()) - .await?) -} +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +pub struct Periods(pub Vec); -pub async fn get_schedule_by_uid( - conn: &mut PoolConnection, - filter_uid: &EmgauwaUid, -) -> Result { - sqlx::query_as!( - Schedule, - "SELECT * FROM schedules WHERE uid = ?", - filter_uid - ) - .fetch_optional(conn.deref_mut()) - .await - .map(|s| s.ok_or(DatabaseError::NotFound))? -} +impl Schedule { + pub async fn get_all( + conn: &mut PoolConnection, + ) -> Result, DatabaseError> { + Ok(sqlx::query_as!(Schedule, "SELECT * FROM schedules") + .fetch_all(conn.deref_mut()) + .await?) + } -pub async fn get_schedules_by_tag( - conn: &mut PoolConnection, - tag: &Tag, -) -> Result, DatabaseError> { - Ok(sqlx::query_as!(Schedule, "SELECT schedule.* FROM schedules AS schedule INNER JOIN junction_tag ON junction_tag.schedule_id = schedule.id WHERE junction_tag.tag_id = ?", tag.id) - .fetch_all(conn.deref_mut()) - .await?) -} - -pub async fn delete_schedule_by_uid( - conn: &mut PoolConnection, - filter_uid: EmgauwaUid, -) -> Result<(), DatabaseError> { - let filter_uid = match filter_uid { - EmgauwaUid::Off => Err(DatabaseError::Protected), - EmgauwaUid::On => Err(DatabaseError::Protected), - EmgauwaUid::Any(_) => Ok(filter_uid), - }?; - - sqlx::query!("DELETE FROM schedules WHERE uid = ?", filter_uid) - .execute(conn.deref_mut()) + pub async fn get_by_uid( + conn: &mut PoolConnection, + filter_uid: &EmgauwaUid, + ) -> Result { + sqlx::query_as!( + Schedule, + "SELECT * FROM schedules WHERE uid = ?", + filter_uid + ) + .fetch_optional(conn.deref_mut()) .await - .map(|res| match res.rows_affected() { - 0 => Err(DatabaseError::DeleteError), - _ => Ok(()), - })? -} + .map(|s| s.ok_or(DatabaseError::NotFound))? + } -pub async fn create_schedule( - conn: &mut PoolConnection, - new_name: &str, - new_periods: &Periods, -) -> Result { - let uid = EmgauwaUid::default(); - sqlx::query_as!( - Schedule, - "INSERT INTO schedules (uid, name, periods) VALUES (?, ?, ?) RETURNING *", - uid, - new_name, - new_periods, - ) - .fetch_optional(conn.deref_mut()) - .await? - .ok_or(DatabaseError::InsertGetError) -} + pub async fn get_by_tag( + conn: &mut PoolConnection, + tag: &Tag, + ) -> Result, DatabaseError> { + Ok(sqlx::query_as!(Schedule, "SELECT schedule.* FROM schedules AS schedule INNER JOIN junction_tag ON junction_tag.schedule_id = schedule.id WHERE junction_tag.tag_id = ?", tag.id) + .fetch_all(conn.deref_mut()) + .await?) + } -pub async fn update_schedule( - conn: &mut PoolConnection, - schedule: &Schedule, - new_name: &str, - new_periods: &Periods, -) -> Result { - // overwrite periods on protected schedules - let new_periods = match schedule.uid { - EmgauwaUid::Off | EmgauwaUid::On => schedule.periods.borrow(), - EmgauwaUid::Any(_) => new_periods, - }; + pub async fn delete_by_uid( + conn: &mut PoolConnection, + filter_uid: EmgauwaUid, + ) -> Result<(), DatabaseError> { + let filter_uid = match filter_uid { + EmgauwaUid::Off => Err(DatabaseError::Protected), + EmgauwaUid::On => Err(DatabaseError::Protected), + EmgauwaUid::Any(_) => Ok(filter_uid), + }?; - sqlx::query!( - "UPDATE schedules SET name = ?, periods = ? WHERE id = ?", - new_name, - new_periods, - schedule.id, - ) - .execute(conn.deref_mut()) - .await?; + sqlx::query!("DELETE FROM schedules WHERE uid = ?", filter_uid) + .execute(conn.deref_mut()) + .await + .map(|res| match res.rows_affected() { + 0 => Err(DatabaseError::DeleteError), + _ => Ok(()), + })? + } - get_schedule_by_uid(conn, &schedule.uid).await -} + pub async fn create( + conn: &mut PoolConnection, + new_name: &str, + new_periods: &Periods, + ) -> Result { + let uid = EmgauwaUid::default(); + sqlx::query_as!( + Schedule, + "INSERT INTO schedules (uid, name, periods) VALUES (?, ?, ?) RETURNING *", + uid, + new_name, + new_periods, + ) + .fetch_optional(conn.deref_mut()) + .await? + .ok_or(DatabaseError::InsertGetError) + } -pub async fn set_schedule_tags( - conn: &mut PoolConnection, - schedule: &Schedule, - new_tags: &[String], -) -> Result<(), DatabaseError> { - sqlx::query!( - "DELETE FROM junction_tag WHERE schedule_id = ?", - schedule.id - ) - .execute(conn.deref_mut()) - .await?; - - for new_tag in new_tags { - let tag: Option = sqlx::query_as!(Tag, "SELECT * FROM tags WHERE tag = ?", new_tag) - .fetch_optional(conn.deref_mut()) - .await?; - - let tag = match tag { - Some(id) => id, - None => create_tag(conn, new_tag).await?, + pub async fn update( + &self, + conn: &mut PoolConnection, + new_name: &str, + new_periods: &Periods, + ) -> Result { + // overwrite periods on protected schedules + let new_periods = match self.uid { + EmgauwaUid::Off | EmgauwaUid::On => self.periods.borrow(), + EmgauwaUid::Any(_) => new_periods, }; - create_junction_tag_schedule(conn, tag, schedule).await?; + sqlx::query!( + "UPDATE schedules SET name = ?, periods = ? WHERE id = ?", + new_name, + new_periods, + self.id, + ) + .execute(conn.deref_mut()) + .await?; + + Schedule::get_by_uid(conn, &self.uid).await + } + + pub async fn get_tags( + &self, + conn: &mut PoolConnection, + ) -> Result, DatabaseError> { + Ok(sqlx::query_scalar!("SELECT tag FROM tags INNER JOIN junction_tag ON junction_tag.tag_id = tags.id WHERE junction_tag.schedule_id = ?", self.id) + .fetch_all(conn.deref_mut()) + .await?) + } + + pub async fn set_tags( + &self, + conn: &mut PoolConnection, + new_tags: &[String], + ) -> Result<(), DatabaseError> { + sqlx::query!("DELETE FROM junction_tag WHERE schedule_id = ?", self.id) + .execute(conn.deref_mut()) + .await?; + + for new_tag in new_tags { + let tag: Option = + sqlx::query_as!(Tag, "SELECT * FROM tags WHERE tag = ?", new_tag) + .fetch_optional(conn.deref_mut()) + .await?; + + let tag = match tag { + Some(id) => id, + None => create_tag(conn, new_tag).await?, + }; + + create_junction_tag_schedule(conn, tag, self).await?; + } + Ok(()) } - Ok(()) } diff --git a/src/db/tag.rs b/src/db/tag.rs index dc3cc16..e612bed 100644 --- a/src/db/tag.rs +++ b/src/db/tag.rs @@ -5,6 +5,7 @@ use sqlx::Sqlite; use crate::db::errors::DatabaseError; use crate::db::models::*; +use crate::db::schedules::Schedule; pub async fn create_tag( conn: &mut PoolConnection, diff --git a/src/handlers/v1/schedules.rs b/src/handlers/v1/schedules.rs index 95237ef..9a5b285 100644 --- a/src/handlers/v1/schedules.rs +++ b/src/handlers/v1/schedules.rs @@ -7,7 +7,6 @@ use sqlx::pool::PoolConnection; use sqlx::{Pool, Sqlite}; use crate::db::errors::DatabaseError; -use crate::db::models::{Periods, Schedule}; use crate::db::schedules::*; use crate::db::tag::get_tag; use crate::handlers::errors::ApiError; @@ -26,7 +25,7 @@ pub struct RequestSchedule { pub async fn index(pool: web::Data>) -> Result { let mut pool_conn = pool.acquire().await?; - let schedules = get_schedules(&mut pool_conn).await?; + let schedules = Schedule::get_all(&mut pool_conn).await?; let mut return_schedules: Vec = schedules.iter().map(ReturnSchedule::from).collect(); @@ -47,7 +46,7 @@ pub async fn tagged( let (tag,) = path.into_inner(); let tag_db = get_tag(&mut pool_conn, &tag).await?; - let schedules = get_schedules_by_tag(&mut pool_conn, &tag_db).await?; + let schedules = Schedule::get_by_tag(&mut pool_conn, &tag_db).await?; let mut return_schedules: Vec = schedules.iter().map(ReturnSchedule::from).collect(); @@ -67,7 +66,7 @@ pub async fn show( let (schedule_uid,) = path.into_inner(); let emgauwa_uid = EmgauwaUid::try_from(schedule_uid.as_str()).or(Err(ApiError::BadUid))?; - let schedule = get_schedule_by_uid(&mut pool_conn, &emgauwa_uid).await?; + let schedule = Schedule::get_by_uid(&mut pool_conn, &emgauwa_uid).await?; let mut return_schedule = ReturnSchedule::from(schedule); return_schedule.load_tags(&mut pool_conn); @@ -81,9 +80,11 @@ pub async fn add( ) -> Result { let mut pool_conn = pool.acquire().await?; - let new_schedule = create_schedule(&mut pool_conn, &data.name, &data.periods).await?; + let new_schedule = Schedule::create(&mut pool_conn, &data.name, &data.periods).await?; - set_schedule_tags(&mut pool_conn, &new_schedule, data.tags.as_slice()).await?; + new_schedule + .set_tags(&mut pool_conn, data.tags.as_slice()) + .await?; let mut return_schedule = ReturnSchedule::from(new_schedule); return_schedule.load_tags(&mut pool_conn); @@ -95,9 +96,11 @@ async fn add_list_single( request_schedule: &RequestSchedule, ) -> Result { let new_schedule = - create_schedule(conn, &request_schedule.name, &request_schedule.periods).await?; + Schedule::create(conn, &request_schedule.name, &request_schedule.periods).await?; - set_schedule_tags(conn, &new_schedule, request_schedule.tags.as_slice()).await?; + new_schedule + .set_tags(conn, request_schedule.tags.as_slice()) + .await?; Ok(new_schedule) } @@ -150,17 +153,15 @@ pub async fn update( let (schedule_uid,) = path.into_inner(); let emgauwa_uid = EmgauwaUid::try_from(schedule_uid.as_str()).or(Err(ApiError::BadUid))?; - let schedule = get_schedule_by_uid(&mut pool_conn, &emgauwa_uid).await?; + let schedule = Schedule::get_by_uid(&mut pool_conn, &emgauwa_uid).await?; - let schedule = update_schedule( - &mut pool_conn, - &schedule, - data.name.as_str(), - data.periods.borrow(), - ) - .await?; + let schedule = schedule + .update(&mut pool_conn, data.name.as_str(), data.periods.borrow()) + .await?; - set_schedule_tags(&mut pool_conn, &schedule, data.tags.as_slice()).await?; + schedule + .set_tags(&mut pool_conn, data.tags.as_slice()) + .await?; let mut return_schedule = ReturnSchedule::from(schedule); return_schedule.load_tags(&mut pool_conn); @@ -181,7 +182,7 @@ pub async fn delete( EmgauwaUid::Off => Err(ApiError::ProtectedSchedule), EmgauwaUid::On => Err(ApiError::ProtectedSchedule), EmgauwaUid::Any(_) => { - delete_schedule_by_uid(&mut pool_conn, emgauwa_uid).await?; + Schedule::delete_by_uid(&mut pool_conn, emgauwa_uid).await?; Ok(HttpResponse::Ok().json("schedule got deleted")) } } diff --git a/src/return_models.rs b/src/return_models.rs index 2b2edbe..2a56fe8 100644 --- a/src/return_models.rs +++ b/src/return_models.rs @@ -1,11 +1,9 @@ +use crate::db::schedules::Schedule; use futures::executor; use serde::Serialize; use sqlx::pool::PoolConnection; use sqlx::Sqlite; -use crate::db::models::Schedule; -use crate::db::schedules::get_schedule_tags; - #[derive(Debug, Serialize)] pub struct ReturnSchedule { #[serde(flatten)] @@ -15,7 +13,7 @@ pub struct ReturnSchedule { impl ReturnSchedule { pub fn load_tags(&mut self, conn: &mut PoolConnection) { - self.tags = executor::block_on(get_schedule_tags(conn, &self.schedule)).unwrap(); + self.tags = executor::block_on(self.schedule.get_tags(conn)).unwrap(); } } diff --git a/src/types/emgauwa_uid.rs b/src/types/emgauwa_uid.rs index 67ef58e..7fd124e 100644 --- a/src/types/emgauwa_uid.rs +++ b/src/types/emgauwa_uid.rs @@ -136,10 +136,6 @@ impl From<&[u8]> for EmgauwaUid { impl From> for EmgauwaUid { fn from(value: Vec) -> Self { - match value.as_slice() { - [EmgauwaUid::OFF_U8] => EmgauwaUid::Off, - [EmgauwaUid::ON_U8] => EmgauwaUid::On, - value_bytes => EmgauwaUid::Any(Uuid::from_slice(value_bytes).unwrap()), - } + EmgauwaUid::from(value.as_slice()) } }