diff --git a/.sqlx/query-08c517120fcfb4534a3ff540910417afca55278e269f12203f4fc83096944810.json b/.sqlx/query-08c517120fcfb4534a3ff540910417afca55278e269f12203f4fc83096944810.json new file mode 100644 index 0000000..b29819b Binary files /dev/null and b/.sqlx/query-08c517120fcfb4534a3ff540910417afca55278e269f12203f4fc83096944810.json differ diff --git a/.sqlx/query-1cb4cc57ff361d6d84a0c8e8f5df1ff46abbb21dfc3eb3f4f38b251f99d10abd.json b/.sqlx/query-1cb4cc57ff361d6d84a0c8e8f5df1ff46abbb21dfc3eb3f4f38b251f99d10abd.json new file mode 100644 index 0000000..5be1688 Binary files /dev/null and b/.sqlx/query-1cb4cc57ff361d6d84a0c8e8f5df1ff46abbb21dfc3eb3f4f38b251f99d10abd.json differ diff --git a/.sqlx/query-1d658709678f72291d835ef2a4183d24f993442ea05e17910f587a4c92d4e7f5.json b/.sqlx/query-1d658709678f72291d835ef2a4183d24f993442ea05e17910f587a4c92d4e7f5.json new file mode 100644 index 0000000..f9ef3de Binary files /dev/null and b/.sqlx/query-1d658709678f72291d835ef2a4183d24f993442ea05e17910f587a4c92d4e7f5.json differ diff --git a/.sqlx/query-1eda8cf54e553e8e892ac63a31cb94e91e7851a53ebae17a26b19300b83d7dac.json b/.sqlx/query-1eda8cf54e553e8e892ac63a31cb94e91e7851a53ebae17a26b19300b83d7dac.json new file mode 100644 index 0000000..e3769c9 Binary files /dev/null and b/.sqlx/query-1eda8cf54e553e8e892ac63a31cb94e91e7851a53ebae17a26b19300b83d7dac.json differ diff --git a/.sqlx/query-3fe383ea9ed4965e25d54eea08fb5abbab1b0c8eec7cf11597267de780299d0d.json b/.sqlx/query-3fe383ea9ed4965e25d54eea08fb5abbab1b0c8eec7cf11597267de780299d0d.json new file mode 100644 index 0000000..4b85000 Binary files /dev/null and b/.sqlx/query-3fe383ea9ed4965e25d54eea08fb5abbab1b0c8eec7cf11597267de780299d0d.json differ diff --git a/.sqlx/query-457e9d4808332255ed7354a28e6ebc2015558f66dbcd00cb4ce559d80f2bd023.json b/.sqlx/query-457e9d4808332255ed7354a28e6ebc2015558f66dbcd00cb4ce559d80f2bd023.json new file mode 100644 index 0000000..e40bb94 Binary files /dev/null and b/.sqlx/query-457e9d4808332255ed7354a28e6ebc2015558f66dbcd00cb4ce559d80f2bd023.json differ diff --git a/.sqlx/query-66a141b71041a7827f1932e6e288fdab37cc699720e5484c30697b5566b8d513.json b/.sqlx/query-66a141b71041a7827f1932e6e288fdab37cc699720e5484c30697b5566b8d513.json new file mode 100644 index 0000000..c08e9fc Binary files /dev/null and b/.sqlx/query-66a141b71041a7827f1932e6e288fdab37cc699720e5484c30697b5566b8d513.json differ diff --git a/.sqlx/query-7519da166e2e0b6de4c02559fc173396e85db762d482607e4662e788c5542fea.json b/.sqlx/query-7519da166e2e0b6de4c02559fc173396e85db762d482607e4662e788c5542fea.json new file mode 100644 index 0000000..2640d90 Binary files /dev/null and b/.sqlx/query-7519da166e2e0b6de4c02559fc173396e85db762d482607e4662e788c5542fea.json differ diff --git a/.sqlx/query-7b3ee1cad84a146699b9010be6d31665a416687772b95d159e5d3e98cde3511c.json b/.sqlx/query-7b3ee1cad84a146699b9010be6d31665a416687772b95d159e5d3e98cde3511c.json new file mode 100644 index 0000000..c904161 Binary files /dev/null and b/.sqlx/query-7b3ee1cad84a146699b9010be6d31665a416687772b95d159e5d3e98cde3511c.json differ diff --git a/.sqlx/query-87b7396fb761030e25836cb90ed07a1bdaa1d82ae3d43d107cf8bac3e9ba4c25.json b/.sqlx/query-87b7396fb761030e25836cb90ed07a1bdaa1d82ae3d43d107cf8bac3e9ba4c25.json new file mode 100644 index 0000000..63d4cfd Binary files /dev/null and b/.sqlx/query-87b7396fb761030e25836cb90ed07a1bdaa1d82ae3d43d107cf8bac3e9ba4c25.json differ diff --git a/.sqlx/query-96f34b8654265ea5ab5210ab5dcad8c0bacd8d9e73e375dc35e759bdb82369a1.json b/.sqlx/query-96f34b8654265ea5ab5210ab5dcad8c0bacd8d9e73e375dc35e759bdb82369a1.json new file mode 100644 index 0000000..705a3b0 Binary files /dev/null and b/.sqlx/query-96f34b8654265ea5ab5210ab5dcad8c0bacd8d9e73e375dc35e759bdb82369a1.json differ diff --git a/.sqlx/query-a69f0bf9b3fefd7914502aa15fa120ecf4e5542011b781d8023bdb422580d97a.json b/.sqlx/query-a69f0bf9b3fefd7914502aa15fa120ecf4e5542011b781d8023bdb422580d97a.json new file mode 100644 index 0000000..f6c5009 Binary files /dev/null and b/.sqlx/query-a69f0bf9b3fefd7914502aa15fa120ecf4e5542011b781d8023bdb422580d97a.json differ diff --git a/.sqlx/query-a6dc153657cb3fefb5ba5b763dc5b7b4da78ab8acec79c7e7ea51ce5c9414bcb.json b/.sqlx/query-a6dc153657cb3fefb5ba5b763dc5b7b4da78ab8acec79c7e7ea51ce5c9414bcb.json new file mode 100644 index 0000000..a0e8505 Binary files /dev/null and b/.sqlx/query-a6dc153657cb3fefb5ba5b763dc5b7b4da78ab8acec79c7e7ea51ce5c9414bcb.json differ diff --git a/.sqlx/query-a8b2d9cfd386b5f9ad5b76ef08711691dae057a431fffc27417e4f5504dcfb30.json b/.sqlx/query-a8b2d9cfd386b5f9ad5b76ef08711691dae057a431fffc27417e4f5504dcfb30.json new file mode 100644 index 0000000..bd4f9f7 Binary files /dev/null and b/.sqlx/query-a8b2d9cfd386b5f9ad5b76ef08711691dae057a431fffc27417e4f5504dcfb30.json differ diff --git a/.sqlx/query-a94cb95af7e6c13e0e155c74de2febe44c1250f25989034a3f9e4e8bcb39dece.json b/.sqlx/query-a94cb95af7e6c13e0e155c74de2febe44c1250f25989034a3f9e4e8bcb39dece.json new file mode 100644 index 0000000..b203d50 Binary files /dev/null and b/.sqlx/query-a94cb95af7e6c13e0e155c74de2febe44c1250f25989034a3f9e4e8bcb39dece.json differ diff --git a/.sqlx/query-acfc608095768f30a55eb0298dfe86d095af0af9545252cf06f30827e9ca502a.json b/.sqlx/query-acfc608095768f30a55eb0298dfe86d095af0af9545252cf06f30827e9ca502a.json new file mode 100644 index 0000000..a85a644 Binary files /dev/null and b/.sqlx/query-acfc608095768f30a55eb0298dfe86d095af0af9545252cf06f30827e9ca502a.json differ diff --git a/.sqlx/query-b91f6aab0bdb316633b3a0d75303086dd6e4e204e9e063535b19db666476fe88.json b/.sqlx/query-b91f6aab0bdb316633b3a0d75303086dd6e4e204e9e063535b19db666476fe88.json new file mode 100644 index 0000000..24b6a13 Binary files /dev/null and b/.sqlx/query-b91f6aab0bdb316633b3a0d75303086dd6e4e204e9e063535b19db666476fe88.json differ diff --git a/.sqlx/query-c30156fb112fcc28f08fbbec04197c41c9f71f6a4a3f44221f5ec012c99ebf54.json b/.sqlx/query-c30156fb112fcc28f08fbbec04197c41c9f71f6a4a3f44221f5ec012c99ebf54.json new file mode 100644 index 0000000..9d225d6 Binary files /dev/null and b/.sqlx/query-c30156fb112fcc28f08fbbec04197c41c9f71f6a4a3f44221f5ec012c99ebf54.json differ diff --git a/.sqlx/query-db48fb93e9f22ee7da0786ae913d962b1da35b5b2b663f759f6af106650951b8.json b/.sqlx/query-db48fb93e9f22ee7da0786ae913d962b1da35b5b2b663f759f6af106650951b8.json new file mode 100644 index 0000000..f31d95b Binary files /dev/null and b/.sqlx/query-db48fb93e9f22ee7da0786ae913d962b1da35b5b2b663f759f6af106650951b8.json differ diff --git a/.sqlx/query-e9386ab7ecbe4ce13f8ee5ee5852697741fb210ea8bfc3d61f1508106c0b076e.json b/.sqlx/query-e9386ab7ecbe4ce13f8ee5ee5852697741fb210ea8bfc3d61f1508106c0b076e.json new file mode 100644 index 0000000..e54e4a6 Binary files /dev/null and b/.sqlx/query-e9386ab7ecbe4ce13f8ee5ee5852697741fb210ea8bfc3d61f1508106c0b076e.json differ diff --git a/.sqlx/query-ea4b06aaad9436096e20a53d81fd36ed21da18ceed5b71e51a1e749bab466422.json b/.sqlx/query-ea4b06aaad9436096e20a53d81fd36ed21da18ceed5b71e51a1e749bab466422.json new file mode 100644 index 0000000..1967a26 Binary files /dev/null and b/.sqlx/query-ea4b06aaad9436096e20a53d81fd36ed21da18ceed5b71e51a1e749bab466422.json differ diff --git a/Makefile b/Makefile index 9ef63f8..c5184dd 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,11 @@ build: cargo build -sqlx: build +sqlx: + rm ./emgauwa-dev.sqlite cargo sqlx database create cargo sqlx migrate run - cargo sqlx prepare + cargo sqlx prepare --workspace build-rpi: cross build --target arm-unknown-linux-gnueabihf diff --git a/emgauwa-controller/src/main.rs b/emgauwa-controller/src/main.rs index 020b544..ca61306 100644 --- a/emgauwa-controller/src/main.rs +++ b/emgauwa-controller/src/main.rs @@ -2,8 +2,10 @@ use std::str; use futures::{future, pin_mut, StreamExt}; use futures::channel::mpsc; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::io::AsyncReadExt; use tokio_tungstenite::{connect_async, tungstenite::protocol::Message}; +use tokio_tungstenite::tungstenite::Error; +use emgauwa_lib::db; mod settings; @@ -11,6 +13,8 @@ mod settings; async fn main() { let settings = settings::init(); + let _pool = db::init(&settings.database).await; + let url = format!( "ws://{}:{}/api/v1/ws/controllers", settings.core.host, @@ -21,17 +25,11 @@ async fn main() { tokio::spawn(read_stdin(stdin_tx)); let (ws_stream, _) = connect_async(url).await.expect("Failed to connect"); - println!("WebSocket handshake has been successfully completed"); let (write, read) = ws_stream.split(); let stdin_to_ws = stdin_rx.map(Ok).forward(write); - let ws_to_stdout = { - read.for_each(|message| async { - let data = message.unwrap().into_text().unwrap(); - println!("{}", data); - }) - }; + let ws_to_stdout = read.for_each(handle_message); pin_mut!(stdin_to_ws, ws_to_stdout); future::select(stdin_to_ws, ws_to_stdout).await; @@ -50,4 +48,11 @@ async fn read_stdin(tx: mpsc::UnboundedSender) { buf.truncate(n); tx.unbounded_send(Message::text(str::from_utf8(&buf).unwrap())).unwrap(); } +} + +pub async fn handle_message(message_result: Result) { + match message_result { + Ok(message) => println!("{}", message.into_text().unwrap()), + Err(err) => println!("Error: {}", err) + } } \ No newline at end of file diff --git a/emgauwa-core/src/main.rs b/emgauwa-core/src/main.rs index 4efb3e8..bfd365b 100644 --- a/emgauwa-core/src/main.rs +++ b/emgauwa-core/src/main.rs @@ -14,13 +14,13 @@ async fn main() -> std::io::Result<()> { let settings = settings::init(); let log_level: LevelFilter = LevelFilter::from_str(&settings.logging.level) - .unwrap_or_else(|_| panic!("Error parsing log level.")); + .expect("Error parsing log level."); trace!("Log level set to {:?}", log_level); SimpleLogger::new() .with_level(log_level) .init() - .unwrap_or_else(|_| panic!("Error initializing logger.")); + .expect("Error initializing logger."); let pool = emgauwa_lib::db::init(&settings.database).await; diff --git a/emgauwa-lib/src/db/controllers.rs b/emgauwa-lib/src/db/controllers.rs new file mode 100644 index 0000000..b427c63 --- /dev/null +++ b/emgauwa-lib/src/db/controllers.rs @@ -0,0 +1,122 @@ +use serde_derive::{Deserialize, Serialize}; +use std::ops::DerefMut; + +use sqlx::pool::PoolConnection; +use sqlx::Sqlite; + +use crate::db::errors::DatabaseError; +use crate::db::model_utils::Period; +use crate::db::tag::Tag; +use crate::db::types::ControllerUid; + +#[derive(Debug, Serialize, Clone)] +pub struct Controller { + pub id: i64, + pub uid: ControllerUid, + pub name: String, + pub relay_count: i64, + pub active: bool, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +pub struct Periods(pub Vec); + +impl Controller { + pub async fn get_all( + conn: &mut PoolConnection, + ) -> Result, DatabaseError> { + Ok(sqlx::query_as!(Controller, "SELECT * FROM controllers") + .fetch_all(conn.deref_mut()) + .await?) + } + + pub async fn get( + conn: &mut PoolConnection, + id: i64, + ) -> Result { + sqlx::query_as!( + Controller, + "SELECT * FROM controllers WHERE id = ?", + id + ) + .fetch_optional(conn.deref_mut()) + .await + .map(|s| s.ok_or(DatabaseError::NotFound))? + } + + pub async fn get_by_uid( + conn: &mut PoolConnection, + filter_uid: &ControllerUid, + ) -> Result { + sqlx::query_as!( + Controller, + "SELECT * FROM controllers WHERE uid = ?", + filter_uid + ) + .fetch_optional(conn.deref_mut()) + .await + .map(|s| s.ok_or(DatabaseError::NotFound))? + } + + pub async fn get_by_tag( + conn: &mut PoolConnection, + tag: &Tag, + ) -> Result, DatabaseError> { + Ok(sqlx::query_as!(Controller, "SELECT schedule.* FROM controllers 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_by_uid( + conn: &mut PoolConnection, + filter_uid: ControllerUid, + ) -> Result<(), DatabaseError> { + sqlx::query!("DELETE FROM controllers WHERE uid = ?", filter_uid) + .execute(conn.deref_mut()) + .await + .map(|res| match res.rows_affected() { + 0 => Err(DatabaseError::DeleteError), + _ => Ok(()), + })? + } + + pub async fn create( + conn: &mut PoolConnection, + new_uid: &ControllerUid, + new_name: &str, + new_relay_count: i64, + new_active: bool + ) -> Result { + sqlx::query_as!( + Controller, + "INSERT INTO controllers (uid, name, relay_count, active) VALUES (?, ?, ?, ?) RETURNING *", + new_uid, + new_name, + new_relay_count, + new_active, + ) + .fetch_optional(conn.deref_mut()) + .await? + .ok_or(DatabaseError::InsertGetError) + } + + pub async fn update( + &self, + conn: &mut PoolConnection, + new_name: &str, + new_relay_count: i64, + new_active: bool + ) -> Result { + sqlx::query!( + "UPDATE controllers SET name = ?, relay_count = ?, active = ? WHERE id = ?", + new_name, + new_relay_count, + new_active, + self.id, + ) + .execute(conn.deref_mut()) + .await?; + + Controller::get_by_uid(conn, &self.uid).await + } +} diff --git a/emgauwa-lib/src/db/mod.rs b/emgauwa-lib/src/db/mod.rs index a870673..0b31050 100644 --- a/emgauwa-lib/src/db/mod.rs +++ b/emgauwa-lib/src/db/mod.rs @@ -6,17 +6,18 @@ use std::str::FromStr; use crate::db::errors::DatabaseError; use crate::db::model_utils::Period; -use crate::db::types::EmgauwaUid; +use crate::db::types::ScheduleUid; // export for easier/flatter access pub use crate::db::schedules::{Periods, Schedule}; -pub(crate) mod errors; +pub mod errors; mod model_utils; mod models; -pub(crate) mod schedules; -pub(crate) mod tag; +pub mod schedules; +pub mod tag; pub mod types; +pub mod controllers; static MIGRATOR: Migrator = sqlx::migrate!("../migrations"); // defaults to "./migrations" @@ -27,7 +28,7 @@ pub async fn run_migrations(pool: &Pool) { async fn init_schedule( pool: &Pool, - uid: &EmgauwaUid, + uid: &ScheduleUid, name: &str, periods: Periods, ) -> Result<(), DatabaseError> { @@ -68,13 +69,13 @@ pub async fn init(db: &str) -> Pool { run_migrations(&pool).await; - init_schedule(&pool, &EmgauwaUid::Off, "Off", Periods(vec![])) + init_schedule(&pool, &ScheduleUid::Off, "Off", Periods(vec![])) .await .expect("Error initializing schedule Off"); init_schedule( &pool, - &EmgauwaUid::On, + &ScheduleUid::On, "On", Periods(vec![Period::new_on()]), ) diff --git a/emgauwa-lib/src/db/schedules.rs b/emgauwa-lib/src/db/schedules.rs index afe136e..b353969 100644 --- a/emgauwa-lib/src/db/schedules.rs +++ b/emgauwa-lib/src/db/schedules.rs @@ -8,14 +8,14 @@ use sqlx::Sqlite; use crate::db::errors::DatabaseError; use crate::db::model_utils::Period; use crate::db::tag::Tag; -use crate::db::types::EmgauwaUid; +use crate::db::types::ScheduleUid; #[derive(Debug, Serialize, Clone)] pub struct Schedule { #[serde(skip)] pub id: i64, #[serde(rename(serialize = "id"))] - pub uid: EmgauwaUid, + pub uid: ScheduleUid, pub name: String, pub periods: Periods, } @@ -32,9 +32,23 @@ impl Schedule { .await?) } + pub async fn get( + conn: &mut PoolConnection, + id: &i64, + ) -> Result { + sqlx::query_as!( + Schedule, + "SELECT * FROM schedules WHERE id = ?", + id + ) + .fetch_optional(conn.deref_mut()) + .await + .map(|s| s.ok_or(DatabaseError::NotFound))? + } + pub async fn get_by_uid( conn: &mut PoolConnection, - filter_uid: &EmgauwaUid, + filter_uid: &ScheduleUid, ) -> Result { sqlx::query_as!( Schedule, @@ -57,12 +71,12 @@ impl Schedule { pub async fn delete_by_uid( conn: &mut PoolConnection, - filter_uid: EmgauwaUid, + filter_uid: ScheduleUid, ) -> Result<(), DatabaseError> { let filter_uid = match filter_uid { - EmgauwaUid::Off => Err(DatabaseError::Protected), - EmgauwaUid::On => Err(DatabaseError::Protected), - EmgauwaUid::Any(_) => Ok(filter_uid), + ScheduleUid::Off => Err(DatabaseError::Protected), + ScheduleUid::On => Err(DatabaseError::Protected), + ScheduleUid::Any(_) => Ok(filter_uid), }?; sqlx::query!("DELETE FROM schedules WHERE uid = ?", filter_uid) @@ -79,7 +93,7 @@ impl Schedule { new_name: &str, new_periods: &Periods, ) -> Result { - let uid = EmgauwaUid::default(); + let uid = ScheduleUid::default(); sqlx::query_as!( Schedule, "INSERT INTO schedules (uid, name, periods) VALUES (?, ?, ?) RETURNING *", @@ -100,8 +114,8 @@ impl Schedule { ) -> Result { // overwrite periods on protected schedules let new_periods = match self.uid { - EmgauwaUid::Off | EmgauwaUid::On => self.periods.borrow(), - EmgauwaUid::Any(_) => new_periods, + ScheduleUid::Off | ScheduleUid::On => self.periods.borrow(), + ScheduleUid::Any(_) => new_periods, }; sqlx::query!( diff --git a/emgauwa-lib/src/db/tag.rs b/emgauwa-lib/src/db/tag.rs index 82819f0..5eb192b 100644 --- a/emgauwa-lib/src/db/tag.rs +++ b/emgauwa-lib/src/db/tag.rs @@ -37,6 +37,16 @@ impl Tag { } pub async fn get( + conn: &mut PoolConnection, + id: i64, + ) -> Result { + sqlx::query_as!(Tag, "SELECT * FROM tags WHERE id = ?", id) + .fetch_optional(conn.deref_mut()) + .await + .map(|t| t.ok_or(DatabaseError::NotFound))? + } + + pub async fn get_by_tag( conn: &mut PoolConnection, target_tag: &str, ) -> Result { diff --git a/emgauwa-lib/src/db/types/controller_uid.rs b/emgauwa-lib/src/db/types/controller_uid.rs new file mode 100644 index 0000000..63817f1 --- /dev/null +++ b/emgauwa-lib/src/db/types/controller_uid.rs @@ -0,0 +1,62 @@ +use serde::{Serialize, Serializer}; +use sqlx::{Decode, Encode, Sqlite, Type}; +use sqlx::database::HasArguments; +use sqlx::encode::IsNull; +use sqlx::error::BoxDynError; +use sqlx::sqlite::{SqliteTypeInfo, SqliteValueRef}; +use uuid::Uuid; + +#[derive(Clone, Debug)] +pub struct ControllerUid(Uuid); + +impl Serialize for ControllerUid { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + String::from(self).serialize(serializer) + } +} + +impl From<&ControllerUid> for String { + fn from(uid: &ControllerUid) -> String { + uid.0.as_hyphenated().to_string() + } +} + +impl Type for ControllerUid { + fn type_info() -> SqliteTypeInfo { + <&[u8] as Type>::type_info() + } + + fn compatible(ty: &SqliteTypeInfo) -> bool { + <&[u8] as Type>::compatible(ty) + } +} + +impl<'q> Encode<'q, Sqlite> for ControllerUid { + //noinspection DuplicatedCode + fn encode_by_ref(&self, buf: &mut >::ArgumentBuffer) -> IsNull { + let uuid_val = self.0.as_bytes().to_vec(); + <&Vec as Encode>::encode(&uuid_val, buf) + } +} + +impl<'r> Decode<'r, Sqlite> for ControllerUid { + //noinspection DuplicatedCode + fn decode(value: SqliteValueRef<'r>) -> Result { + Ok(Self::from(<&[u8] as Decode>::decode(value)?)) + } +} + +impl From<&[u8]> for ControllerUid { + fn from(value: &[u8]) -> Self { + Self(Uuid::from_slice(&value).unwrap()) + } +} + +impl From> for ControllerUid { + fn from(value: Vec) -> Self { + Self::from(value.as_slice()) + } +} diff --git a/emgauwa-lib/src/db/types/emgauwa_uid.rs b/emgauwa-lib/src/db/types/emgauwa_uid.rs deleted file mode 100644 index 6287ad4..0000000 --- a/emgauwa-lib/src/db/types/emgauwa_uid.rs +++ /dev/null @@ -1,146 +0,0 @@ -use std::convert::TryFrom; -use std::fmt::{Debug, Formatter}; -use std::str::FromStr; - -use serde::{Serialize, Serializer}; -use sqlx::database::HasArguments; -use sqlx::encode::IsNull; -use sqlx::error::BoxDynError; -use sqlx::sqlite::{SqliteTypeInfo, SqliteValueRef}; -use sqlx::{Decode, Encode, Sqlite, Type}; -use uuid::Uuid; - -#[derive(Clone)] -pub enum EmgauwaUid { - Off, - On, - Any(Uuid), -} - -impl EmgauwaUid { - const OFF_STR: &'static str = "off"; - const ON_STR: &'static str = "on"; - const OFF_U8: u8 = 0; - const ON_U8: u8 = 1; - const OFF_U128: u128 = 0; - const ON_U128: u128 = 1; -} - -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::Off => EmgauwaUid::OFF_STR.fmt(f), - EmgauwaUid::On => EmgauwaUid::ON_STR.fmt(f), - EmgauwaUid::Any(value) => value.fmt(f), - } - } -} - -impl Type for EmgauwaUid { - fn type_info() -> SqliteTypeInfo { - <&[u8] as Type>::type_info() - } - - fn compatible(ty: &SqliteTypeInfo) -> bool { - <&[u8] as Type>::compatible(ty) - } -} - -impl<'q> Encode<'q, Sqlite> for EmgauwaUid { - //noinspection DuplicatedCode - fn encode_by_ref(&self, buf: &mut >::ArgumentBuffer) -> IsNull { - <&Vec as Encode>::encode(&Vec::from(self), buf) - } -} - -impl<'r> Decode<'r, Sqlite> for EmgauwaUid { - fn decode(value: SqliteValueRef<'r>) -> Result { - Ok(EmgauwaUid::from(<&[u8] as Decode>::decode(value)?)) - } -} - -impl Serialize for EmgauwaUid { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - String::from(self).serialize(serializer) - } -} - -impl From for EmgauwaUid { - fn from(uid: Uuid) -> EmgauwaUid { - match uid.as_u128() { - EmgauwaUid::OFF_U128 => EmgauwaUid::Off, - EmgauwaUid::ON_U128 => EmgauwaUid::On, - _ => EmgauwaUid::Any(uid), - } - } -} - -impl TryFrom<&str> for EmgauwaUid { - type Error = uuid::Error; - - fn try_from(value: &str) -> Result { - match value { - EmgauwaUid::OFF_STR => Ok(EmgauwaUid::Off), - EmgauwaUid::ON_STR => Ok(EmgauwaUid::On), - any => match Uuid::from_str(any) { - Ok(uuid) => Ok(EmgauwaUid::Any(uuid)), - Err(err) => Err(err), - }, - } - } -} - -impl From<&EmgauwaUid> for Uuid { - fn from(emgauwa_uid: &EmgauwaUid) -> Uuid { - match emgauwa_uid { - EmgauwaUid::Off => Uuid::from_u128(EmgauwaUid::OFF_U128), - EmgauwaUid::On => Uuid::from_u128(EmgauwaUid::ON_U128), - EmgauwaUid::Any(value) => *value, - } - } -} - -impl From<&EmgauwaUid> for String { - fn from(emgauwa_uid: &EmgauwaUid) -> String { - match emgauwa_uid { - EmgauwaUid::Off => String::from(EmgauwaUid::OFF_STR), - EmgauwaUid::On => String::from(EmgauwaUid::ON_STR), - EmgauwaUid::Any(value) => value.as_hyphenated().to_string(), - } - } -} - -impl From<&EmgauwaUid> for Vec { - fn from(emgauwa_uid: &EmgauwaUid) -> Vec { - match emgauwa_uid { - EmgauwaUid::Off => vec![EmgauwaUid::OFF_U8], - EmgauwaUid::On => vec![EmgauwaUid::ON_U8], - EmgauwaUid::Any(value) => value.as_bytes().to_vec(), - } - } -} - -impl From<&[u8]> for EmgauwaUid { - fn from(value: &[u8]) -> Self { - match value { - [EmgauwaUid::OFF_U8] => EmgauwaUid::Off, - [EmgauwaUid::ON_U8] => EmgauwaUid::On, - value_bytes => EmgauwaUid::Any(Uuid::from_slice(value_bytes).unwrap()), - } - } -} - -impl From> for EmgauwaUid { - fn from(value: Vec) -> Self { - EmgauwaUid::from(value.as_slice()) - } -} diff --git a/emgauwa-lib/src/db/types/mod.rs b/emgauwa-lib/src/db/types/mod.rs index 04e5e86..9974977 100644 --- a/emgauwa-lib/src/db/types/mod.rs +++ b/emgauwa-lib/src/db/types/mod.rs @@ -1,2 +1,5 @@ -pub mod emgauwa_uid; -pub use emgauwa_uid::EmgauwaUid; +mod schedule_uid; +mod controller_uid; + +pub use schedule_uid::ScheduleUid; +pub use controller_uid::ControllerUid; \ No newline at end of file diff --git a/emgauwa-lib/src/db/types/schedule_uid.rs b/emgauwa-lib/src/db/types/schedule_uid.rs new file mode 100644 index 0000000..69d9339 --- /dev/null +++ b/emgauwa-lib/src/db/types/schedule_uid.rs @@ -0,0 +1,147 @@ +use std::convert::TryFrom; +use std::fmt::{Debug, Formatter}; +use std::str::FromStr; + +use serde::{Serialize, Serializer}; +use sqlx::database::HasArguments; +use sqlx::encode::IsNull; +use sqlx::error::BoxDynError; +use sqlx::sqlite::{SqliteTypeInfo, SqliteValueRef}; +use sqlx::{Decode, Encode, Sqlite, Type}; +use uuid::Uuid; + +#[derive(Clone)] +pub enum ScheduleUid { + Off, + On, + Any(Uuid), +} + +impl ScheduleUid { + const OFF_STR: &'static str = "off"; + const ON_STR: &'static str = "on"; + const OFF_U8: u8 = 0; + const ON_U8: u8 = 1; + const OFF_U128: u128 = 0; + const ON_U128: u128 = 1; +} + +impl Default for ScheduleUid { + fn default() -> Self { + Self::Any(Uuid::new_v4()) + } +} + +impl Debug for ScheduleUid { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::Off => Self::OFF_STR.fmt(f), + Self::On => Self::ON_STR.fmt(f), + Self::Any(value) => value.fmt(f), + } + } +} + +impl Type for ScheduleUid { + fn type_info() -> SqliteTypeInfo { + <&[u8] as Type>::type_info() + } + + fn compatible(ty: &SqliteTypeInfo) -> bool { + <&[u8] as Type>::compatible(ty) + } +} + +impl<'q> Encode<'q, Sqlite> for ScheduleUid { + //noinspection DuplicatedCode + fn encode_by_ref(&self, buf: &mut >::ArgumentBuffer) -> IsNull { + <&Vec as Encode>::encode(&Vec::from(self), buf) + } +} + +impl<'r> Decode<'r, Sqlite> for ScheduleUid { + //noinspection DuplicatedCode + fn decode(value: SqliteValueRef<'r>) -> Result { + Ok(Self::from(<&[u8] as Decode>::decode(value)?)) + } +} + +impl Serialize for ScheduleUid { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + String::from(self).serialize(serializer) + } +} + +impl From for ScheduleUid { + fn from(uid: Uuid) -> Self { + match uid.as_u128() { + Self::OFF_U128 => Self::Off, + Self::ON_U128 => Self::On, + _ => Self::Any(uid), + } + } +} + +impl TryFrom<&str> for ScheduleUid { + type Error = uuid::Error; + + fn try_from(value: &str) -> Result { + match value { + Self::OFF_STR => Ok(Self::Off), + Self::ON_STR => Ok(Self::On), + any => match Uuid::from_str(any) { + Ok(uuid) => Ok(Self::Any(uuid)), + Err(err) => Err(err), + }, + } + } +} + +impl From<&ScheduleUid> for Uuid { + fn from(uid: &ScheduleUid) -> Uuid { + match uid { + ScheduleUid::Off => Uuid::from_u128(ScheduleUid::OFF_U128), + ScheduleUid::On => Uuid::from_u128(ScheduleUid::ON_U128), + ScheduleUid::Any(value) => *value, + } + } +} + +impl From<&ScheduleUid> for String { + fn from(uid: &ScheduleUid) -> String { + match uid { + ScheduleUid::Off => String::from(ScheduleUid::OFF_STR), + ScheduleUid::On => String::from(ScheduleUid::ON_STR), + ScheduleUid::Any(value) => value.as_hyphenated().to_string(), + } + } +} + +impl From<&ScheduleUid> for Vec { + fn from(uid: &ScheduleUid) -> Vec { + match uid { + ScheduleUid::Off => vec![ScheduleUid::OFF_U8], + ScheduleUid::On => vec![ScheduleUid::ON_U8], + ScheduleUid::Any(value) => value.as_bytes().to_vec(), + } + } +} + +impl From<&[u8]> for ScheduleUid { + fn from(value: &[u8]) -> Self { + match value { + [Self::OFF_U8] => Self::Off, + [Self::ON_U8] => Self::On, + value_bytes => Self::Any(Uuid::from_slice(value_bytes).unwrap()), + } + } +} + +impl From> for ScheduleUid { + fn from(value: Vec) -> Self { + Self::from(value.as_slice()) + } +} diff --git a/emgauwa-lib/src/handlers/v1/schedules.rs b/emgauwa-lib/src/handlers/v1/schedules.rs index 5c62a59..841e6e4 100644 --- a/emgauwa-lib/src/handlers/v1/schedules.rs +++ b/emgauwa-lib/src/handlers/v1/schedules.rs @@ -6,7 +6,7 @@ use sqlx::{Pool, Sqlite}; use crate::db::errors::DatabaseError; use crate::db::{Periods, Schedule}; use crate::db::tag::Tag; -use crate::db::types::EmgauwaUid; +use crate::db::types::ScheduleUid; use crate::handlers::errors::ApiError; use crate::return_models::ReturnSchedule; use crate::utils::vec_has_error; @@ -41,7 +41,7 @@ pub async fn tagged( let mut pool_conn = pool.acquire().await?; let (tag,) = path.into_inner(); - let tag_db = Tag::get(&mut pool_conn, &tag).await?; + let tag_db = Tag::get_by_tag(&mut pool_conn, &tag).await?; let schedules = Schedule::get_by_tag(&mut pool_conn, &tag_db).await?; @@ -61,9 +61,9 @@ pub async fn show( let mut pool_conn = pool.acquire().await?; let (schedule_uid,) = path.into_inner(); - let emgauwa_uid = EmgauwaUid::try_from(schedule_uid.as_str()).or(Err(ApiError::BadUid))?; + let uid = ScheduleUid::try_from(schedule_uid.as_str()).or(Err(ApiError::BadUid))?; - let schedule = Schedule::get_by_uid(&mut pool_conn, &emgauwa_uid).await?; + let schedule = Schedule::get_by_uid(&mut pool_conn, &uid).await?; let mut return_schedule = ReturnSchedule::from(schedule); return_schedule.load_tags(&mut pool_conn); @@ -148,9 +148,9 @@ pub async fn update( let mut pool_conn = pool.acquire().await?; let (schedule_uid,) = path.into_inner(); - let emgauwa_uid = EmgauwaUid::try_from(schedule_uid.as_str()).or(Err(ApiError::BadUid))?; + let uid = ScheduleUid::try_from(schedule_uid.as_str()).or(Err(ApiError::BadUid))?; - let schedule = Schedule::get_by_uid(&mut pool_conn, &emgauwa_uid).await?; + let schedule = Schedule::get_by_uid(&mut pool_conn, &uid).await?; let schedule = schedule .update(&mut pool_conn, data.name.as_str(), &data.periods) @@ -173,13 +173,13 @@ pub async fn delete( let mut pool_conn = pool.acquire().await?; let (schedule_uid,) = path.into_inner(); - let emgauwa_uid = EmgauwaUid::try_from(schedule_uid.as_str()).or(Err(ApiError::BadUid))?; + let uid = ScheduleUid::try_from(schedule_uid.as_str()).or(Err(ApiError::BadUid))?; - match emgauwa_uid { - EmgauwaUid::Off => Err(ApiError::ProtectedSchedule), - EmgauwaUid::On => Err(ApiError::ProtectedSchedule), - EmgauwaUid::Any(_) => { - Schedule::delete_by_uid(&mut pool_conn, emgauwa_uid).await?; + match uid { + ScheduleUid::Off => Err(ApiError::ProtectedSchedule), + ScheduleUid::On => Err(ApiError::ProtectedSchedule), + ScheduleUid::Any(_) => { + Schedule::delete_by_uid(&mut pool_conn, uid).await?; Ok(HttpResponse::Ok().json("schedule got deleted")) } } diff --git a/migrations/20231120000000_init.up.sql b/migrations/20231120000000_init.up.sql index e963801..108c5a6 100644 --- a/migrations/20231120000000_init.up.sql +++ b/migrations/20231120000000_init.up.sql @@ -6,18 +6,15 @@ CREATE TABLE controllers AUTOINCREMENT NOT NULL, uid - VARCHAR(36) + BLOB NOT NULL UNIQUE, name VARCHAR(128) NOT NULL, - ip - VARCHAR(16), - port - INTEGER, relay_count - INTEGER, + INTEGER + NOT NULL, active BOOLEAN NOT NULL