From 19e2ea003b577b738d4aa962c78bb28220af89ca Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Thu, 2 May 2024 13:30:47 +0200
Subject: [PATCH 01/18] Add sql transactions

---
 src/db/controllers.rs             | 58 ++++++++++++++--------------
 src/db/junction_relay_schedule.rs | 36 ++++++++---------
 src/db/junction_tag.rs            | 10 ++---
 src/db/macro.rs                   | 54 +++++++++++++-------------
 src/db/macro_action.rs            | 30 +++++++--------
 src/db/mod.rs                     |  8 ++--
 src/db/relays.rs                  | 62 +++++++++++++++---------------
 src/db/schedules.rs               | 64 +++++++++++++++----------------
 src/db/tag.rs                     | 30 +++++++--------
 src/models/controller.rs          | 18 ++++-----
 src/models/macro.rs               | 12 +++---
 src/models/macro_action.rs        | 20 +++++-----
 src/models/mod.rs                 | 20 +++++-----
 src/models/relay.rs               | 28 +++++++-------
 src/models/schedule.rs            | 10 ++---
 src/models/tag.rs                 | 16 ++++----
 src/types/request.rs              |  6 +--
 17 files changed, 242 insertions(+), 240 deletions(-)

diff --git a/src/db/controllers.rs b/src/db/controllers.rs
index bf0a1f0..79cb0df 100644
--- a/src/db/controllers.rs
+++ b/src/db/controllers.rs
@@ -1,7 +1,7 @@
 use std::ops::DerefMut;
 
 use serde_derive::{Deserialize, Serialize};
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::db::{DbRelay, DbTag};
@@ -21,26 +21,26 @@ pub struct DbController {
 
 impl DbController {
 	pub async fn get_all(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<Vec<DbController>, DatabaseError> {
 		sqlx::query_as!(DbController, "SELECT * FROM controllers")
-			.fetch_all(conn.deref_mut())
+			.fetch_all(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		id: i64,
 	) -> Result<Option<DbController>, DatabaseError> {
 		sqlx::query_as!(DbController, "SELECT * FROM controllers WHERE id = ?", id)
-			.fetch_optional(conn.deref_mut())
+			.fetch_optional(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_uid(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		filter_uid: &EmgauwaUid,
 	) -> Result<Option<DbController>, DatabaseError> {
 		sqlx::query_as!(
@@ -48,39 +48,39 @@ impl DbController {
 			"SELECT * FROM controllers WHERE uid = ?",
 			filter_uid
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_uid_or_create(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		uid: &EmgauwaUid,
 		new_name: &str,
 		new_relay_count: i64,
 	) -> Result<DbController, DatabaseError> {
-		match DbController::get_by_uid(conn, uid).await? {
+		match DbController::get_by_uid(tx, uid).await? {
 			Some(tag) => Ok(tag),
-			None => DbController::create(conn, uid, new_name, new_relay_count).await,
+			None => DbController::create(tx, uid, new_name, new_relay_count).await,
 		}
 	}
 
 	pub async fn get_by_tag(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		tag: &DbTag,
 	) -> Result<Vec<DbController>, DatabaseError> {
 		sqlx::query_as!(DbController, "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())
+			.fetch_all(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn delete_by_uid(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		filter_uid: EmgauwaUid,
 	) -> Result<(), DatabaseError> {
 		if sqlx::query_scalar!("SELECT 1 FROM controllers WHERE uid = ?", filter_uid)
-			.fetch_optional(conn.deref_mut())
+			.fetch_optional(tx.deref_mut())
 			.await?
 			.is_none()
 		{
@@ -88,7 +88,7 @@ impl DbController {
 		}
 
 		sqlx::query!("DELETE FROM controllers WHERE uid = ?", filter_uid)
-			.execute(conn.deref_mut())
+			.execute(tx.deref_mut())
 			.await
 			.map(|res| match res.rows_affected() {
 				0 => Err(DatabaseError::DeleteError),
@@ -97,7 +97,7 @@ impl DbController {
 	}
 
 	pub async fn create(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		new_uid: &EmgauwaUid,
 		new_name: &str,
 		new_relay_count: i64,
@@ -109,14 +109,14 @@ impl DbController {
 			new_name,
 			new_relay_count,
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
 
 	pub async fn update(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		new_name: &str,
 		new_relay_count: i64,
 	) -> Result<DbController, DatabaseError> {
@@ -126,17 +126,17 @@ impl DbController {
 			new_relay_count,
 			self.id,
 		)
-		.execute(conn.deref_mut())
+		.execute(tx.deref_mut())
 		.await?;
 
-		Self::get(conn, self.id)
+		Self::get(tx, self.id)
 			.await?
 			.ok_or(DatabaseError::UpdateGetError)
 	}
 
 	pub async fn update_active(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		new_active: bool,
 	) -> Result<DbController, DatabaseError> {
 		sqlx::query!(
@@ -144,40 +144,40 @@ impl DbController {
 			new_active,
 			self.id,
 		)
-		.execute(conn.deref_mut())
+		.execute(tx.deref_mut())
 		.await?;
 
-		Self::get(conn, self.id)
+		Self::get(tx, self.id)
 			.await?
 			.ok_or(DatabaseError::UpdateGetError)
 	}
 
 	pub async fn get_relays(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<Vec<DbRelay>, DatabaseError> {
 		sqlx::query_as!(
 			DbRelay,
 			"SELECT * FROM relays WHERE controller_id = ?",
 			self.id
 		)
-		.fetch_all(conn.deref_mut())
+		.fetch_all(tx.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
-	pub async fn all_inactive(conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
+	pub async fn all_inactive(tx: &mut Transaction<'_, Sqlite>) -> Result<(), DatabaseError> {
 		sqlx::query!("UPDATE controllers SET active = 0")
-			.execute(conn.deref_mut())
+			.execute(tx.deref_mut())
 			.await?;
 		Ok(())
 	}
 
 	pub async fn reload(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<DbController, DatabaseError> {
-		Self::get(conn, self.id)
+		Self::get(tx, self.id)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
diff --git a/src/db/junction_relay_schedule.rs b/src/db/junction_relay_schedule.rs
index f081a54..74710eb 100644
--- a/src/db/junction_relay_schedule.rs
+++ b/src/db/junction_relay_schedule.rs
@@ -1,6 +1,6 @@
 use std::ops::DerefMut;
 
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::db::{DbRelay, DbSchedule};
@@ -16,7 +16,7 @@ pub struct DbJunctionRelaySchedule {
 
 impl DbJunctionRelaySchedule {
 	pub async fn get(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		id: i64,
 	) -> Result<Option<DbJunctionRelaySchedule>, DatabaseError> {
 		sqlx::query_as!(
@@ -24,13 +24,13 @@ impl DbJunctionRelaySchedule {
 			"SELECT * FROM junction_relay_schedule WHERE id = ?",
 			id
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_junction_by_relay_and_weekday(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		relay: &DbRelay,
 		weekday: Weekday,
 	) -> Result<Option<DbJunctionRelaySchedule>, DatabaseError> {
@@ -40,13 +40,13 @@ impl DbJunctionRelaySchedule {
 			relay.id,
 			weekday
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_relays(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		schedule: &DbSchedule,
 	) -> Result<Vec<DbRelay>, DatabaseError> {
 		sqlx::query_as!(
@@ -57,13 +57,13 @@ impl DbJunctionRelaySchedule {
 			ORDER BY junction_relay_schedule.weekday"#,
 			schedule.id
 		)
-		.fetch_all(conn.deref_mut())
+		.fetch_all(tx.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_schedule(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		relay: &DbRelay,
 		weekday: Weekday,
 	) -> Result<Option<DbSchedule>, DatabaseError> {
@@ -75,13 +75,13 @@ impl DbJunctionRelaySchedule {
 			relay.id,
 			weekday
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_schedules(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		relay: &DbRelay,
 	) -> Result<Vec<DbSchedule>, DatabaseError> {
 		sqlx::query_as!(
@@ -92,18 +92,18 @@ impl DbJunctionRelaySchedule {
 			ORDER BY junction_relay_schedule.weekday"#,
 			relay.id
 		)
-		.fetch_all(conn.deref_mut())
+		.fetch_all(tx.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn set_schedule(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		relay: &DbRelay,
 		schedule: &DbSchedule,
 		weekday: Weekday,
 	) -> Result<DbJunctionRelaySchedule, DatabaseError> {
-		match Self::get_junction_by_relay_and_weekday(conn, relay, weekday).await? {
+		match Self::get_junction_by_relay_and_weekday(tx, relay, weekday).await? {
 			None => sqlx::query_as!(
 				DbJunctionRelaySchedule,
 				"INSERT INTO junction_relay_schedule (weekday, relay_id, schedule_id) VALUES (?, ?, ?) RETURNING *",
@@ -111,7 +111,7 @@ impl DbJunctionRelaySchedule {
 				relay.id,
 				schedule.id
 			)
-			.fetch_optional(conn.deref_mut())
+			.fetch_optional(tx.deref_mut())
 			.await?
 			.ok_or(DatabaseError::InsertGetError),
 
@@ -123,10 +123,10 @@ impl DbJunctionRelaySchedule {
                     schedule.id,
 					junction.id
                 )
-					.execute(conn.deref_mut())
+					.execute(tx.deref_mut())
 					.await?;
 
-				Self::get(conn, junction.id)
+				Self::get(tx, junction.id)
 					.await?
 					.ok_or(DatabaseError::UpdateGetError)
 			}
@@ -134,12 +134,12 @@ impl DbJunctionRelaySchedule {
 	}
 
 	pub async fn set_schedules(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		relay: &DbRelay,
 		schedules: Vec<&DbSchedule>,
 	) -> Result<(), DatabaseError> {
 		for (weekday, schedule) in schedules.iter().enumerate() {
-			Self::set_schedule(conn, relay, schedule, weekday as Weekday).await?;
+			Self::set_schedule(tx, relay, schedule, weekday as Weekday).await?;
 		}
 		Ok(())
 	}
diff --git a/src/db/junction_tag.rs b/src/db/junction_tag.rs
index f1b8816..51a3ba5 100644
--- a/src/db/junction_tag.rs
+++ b/src/db/junction_tag.rs
@@ -1,6 +1,6 @@
 use std::ops::DerefMut;
 
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::db::{DbRelay, DbSchedule, DbTag};
@@ -15,7 +15,7 @@ pub struct DbJunctionTag {
 
 impl DbJunctionTag {
 	pub async fn link_relay(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		tag: &DbTag,
 		relay: &DbRelay,
 	) -> Result<DbJunctionTag, DatabaseError> {
@@ -25,13 +25,13 @@ impl DbJunctionTag {
 			tag.id,
 			relay.id
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
 
 	pub async fn link_schedule(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		tag: &DbTag,
 		schedule: &DbSchedule,
 	) -> Result<DbJunctionTag, DatabaseError> {
@@ -41,7 +41,7 @@ impl DbJunctionTag {
 			tag.id,
 			schedule.id
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
diff --git a/src/db/macro.rs b/src/db/macro.rs
index 62092ea..6aa857c 100644
--- a/src/db/macro.rs
+++ b/src/db/macro.rs
@@ -1,7 +1,7 @@
 use std::ops::DerefMut;
 
 use serde_derive::{Deserialize, Serialize};
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::db::{DbController, DbMacroAction, DbRelay, DbSchedule};
@@ -18,35 +18,35 @@ pub struct DbMacro {
 }
 
 impl DbMacro {
-	pub async fn get_all(conn: &mut PoolConnection<Sqlite>) -> Result<Vec<DbMacro>, DatabaseError> {
+	pub async fn get_all(tx: &mut Transaction<'_, Sqlite>) -> Result<Vec<DbMacro>, DatabaseError> {
 		sqlx::query_as!(DbMacro, "SELECT * FROM macros")
-			.fetch_all(conn.deref_mut())
+			.fetch_all(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		id: i64,
 	) -> Result<Option<DbMacro>, DatabaseError> {
 		sqlx::query_as!(DbMacro, "SELECT * FROM macros WHERE id = ?", id)
-			.fetch_optional(conn.deref_mut())
+			.fetch_optional(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_uid(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		filter_uid: &EmgauwaUid,
 	) -> Result<Option<DbMacro>, DatabaseError> {
 		sqlx::query_as!(DbMacro, "SELECT * FROM macros WHERE uid = ?", filter_uid)
-			.fetch_optional(conn.deref_mut())
+			.fetch_optional(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn create(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		new_uid: EmgauwaUid,
 		new_name: &str,
 	) -> Result<DbMacro, DatabaseError> {
@@ -56,14 +56,14 @@ impl DbMacro {
 			new_uid,
 			new_name
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
 
-	pub async fn delete(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
+	pub async fn delete(&self, tx: &mut Transaction<'_, Sqlite>) -> Result<(), DatabaseError> {
 		sqlx::query!("DELETE FROM macros WHERE id = ?", self.id)
-			.execute(conn.deref_mut())
+			.execute(tx.deref_mut())
 			.await
 			.map(|res| match res.rows_affected() {
 				0 => Err(DatabaseError::DeleteError),
@@ -72,11 +72,11 @@ impl DbMacro {
 	}
 
 	pub async fn delete_by_uid(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		filter_uid: EmgauwaUid,
 	) -> Result<(), DatabaseError> {
 		if sqlx::query_scalar!("SELECT 1 FROM macros WHERE uid = ?", filter_uid)
-			.fetch_optional(conn.deref_mut())
+			.fetch_optional(tx.deref_mut())
 			.await?
 			.is_none()
 		{
@@ -84,7 +84,7 @@ impl DbMacro {
 		}
 
 		sqlx::query!("DELETE FROM macros WHERE uid = ?", filter_uid)
-			.execute(conn.deref_mut())
+			.execute(tx.deref_mut())
 			.await
 			.map(|res| match res.rows_affected() {
 				0 => Err(DatabaseError::DeleteError),
@@ -94,62 +94,62 @@ impl DbMacro {
 
 	pub async fn update(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		new_name: &str,
 	) -> Result<DbMacro, DatabaseError> {
 		sqlx::query!("UPDATE relays SET name = ? WHERE id = ?", new_name, self.id,)
-			.execute(conn.deref_mut())
+			.execute(tx.deref_mut())
 			.await?;
 
-		DbMacro::get(conn, self.id)
+		DbMacro::get(tx, self.id)
 			.await?
 			.ok_or(DatabaseError::UpdateGetError)
 	}
 
 	pub async fn set_actions(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		new_actions: &[RequestMacroAction],
 	) -> Result<(), DatabaseError> {
 		sqlx::query!("DELETE FROM macro_actions WHERE macro_id = ?", self.id)
-			.execute(conn.deref_mut())
+			.execute(tx.deref_mut())
 			.await?;
 
 		for new_action in new_actions {
-			let controller = DbController::get_by_uid(conn, &new_action.relay.controller_id)
+			let controller = DbController::get_by_uid(tx, &new_action.relay.controller_id)
 				.await?
 				.ok_or(DatabaseError::NotFound)?;
 			let relay =
-				DbRelay::get_by_controller_and_num(conn, &controller, new_action.relay.number)
+				DbRelay::get_by_controller_and_num(tx, &controller, new_action.relay.number)
 					.await?
 					.ok_or(DatabaseError::NotFound)?;
 
-			let schedule = DbSchedule::get_by_uid(conn, &new_action.schedule.id)
+			let schedule = DbSchedule::get_by_uid(tx, &new_action.schedule.id)
 				.await?
 				.ok_or(DatabaseError::NotFound)?;
 
-			DbMacroAction::create(conn, self, &relay, &schedule, new_action.weekday).await?;
+			DbMacroAction::create(tx, self, &relay, &schedule, new_action.weekday).await?;
 		}
 		Ok(())
 	}
 
 	pub async fn get_actions(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<Vec<DbMacroAction>, DatabaseError> {
 		sqlx::query_as!(
 			DbMacroAction,
 			"SELECT * FROM macro_actions WHERE macro_id = ?",
 			self.id
 		)
-		.fetch_all(conn.deref_mut())
+		.fetch_all(tx.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_actions_weekday(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		weekday: i64,
 	) -> Result<Vec<DbMacroAction>, DatabaseError> {
 		sqlx::query_as!(
@@ -158,7 +158,7 @@ impl DbMacro {
 			self.id,
 			weekday
 		)
-		.fetch_all(conn.deref_mut())
+		.fetch_all(tx.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
diff --git a/src/db/macro_action.rs b/src/db/macro_action.rs
index 81e0a01..a640dff 100644
--- a/src/db/macro_action.rs
+++ b/src/db/macro_action.rs
@@ -1,6 +1,6 @@
 use std::ops::DerefMut;
 
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::db::{DbMacro, DbRelay, DbSchedule};
@@ -17,16 +17,16 @@ pub struct DbMacroAction {
 
 impl DbMacroAction {
 	pub async fn get_all(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<Vec<DbMacroAction>, DatabaseError> {
 		sqlx::query_as!(DbMacroAction, "SELECT * FROM macro_actions")
-			.fetch_all(conn.deref_mut())
+			.fetch_all(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		id: i64,
 	) -> Result<Option<DbMacroAction>, DatabaseError> {
 		sqlx::query_as!(
@@ -34,13 +34,13 @@ impl DbMacroAction {
 			"SELECT * FROM macro_actions WHERE id = ?",
 			id
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn create(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		new_macro: &DbMacro,
 		new_relay: &DbRelay,
 		new_schedule: &DbSchedule,
@@ -54,14 +54,14 @@ impl DbMacroAction {
 			new_schedule.id,
 			new_weekday
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
 
-	pub async fn delete(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
+	pub async fn delete(&self, tx: &mut Transaction<'_, Sqlite>) -> Result<(), DatabaseError> {
 		sqlx::query!("DELETE FROM macro_actions WHERE id = ?", self.id)
-			.execute(conn.deref_mut())
+			.execute(tx.deref_mut())
 			.await
 			.map(|res| match res.rows_affected() {
 				0 => Err(DatabaseError::DeleteError),
@@ -71,27 +71,27 @@ impl DbMacroAction {
 
 	pub async fn get_relay(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<DbRelay, DatabaseError> {
-		DbRelay::get(conn, self.relay_id)
+		DbRelay::get(tx, self.relay_id)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
 
 	pub async fn get_schedule(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<DbSchedule, DatabaseError> {
-		DbSchedule::get(conn, self.schedule_id)
+		DbSchedule::get(tx, self.schedule_id)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
 
 	pub async fn get_macro(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<DbMacro, DatabaseError> {
-		DbMacro::get(conn, self.macro_id)
+		DbMacro::get(tx, self.macro_id)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
diff --git a/src/db/mod.rs b/src/db/mod.rs
index 7ebb6c3..7de0263 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -46,10 +46,12 @@ pub async fn init(db: &str) -> Result<Pool<Sqlite>, EmgauwaError> {
 
 	run_migrations(&pool).await?;
 
-	let mut pool_conn = pool.acquire().await?;
+	let mut tx = pool.begin().await?;
 
-	DbSchedule::get_on(&mut pool_conn).await?;
-	DbSchedule::get_off(&mut pool_conn).await?;
+	DbSchedule::get_on(&mut tx).await?;
+	DbSchedule::get_off(&mut tx).await?;
+
+	tx.commit().await?;
 
 	Ok(pool)
 }
diff --git a/src/db/relays.rs b/src/db/relays.rs
index c7f4ca3..faa82f0 100644
--- a/src/db/relays.rs
+++ b/src/db/relays.rs
@@ -1,7 +1,7 @@
 use std::ops::DerefMut;
 
 use serde_derive::{Deserialize, Serialize};
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::db::{DbController, DbJunctionRelaySchedule, DbJunctionTag, DbSchedule, DbTag};
@@ -20,25 +20,25 @@ pub struct DbRelay {
 }
 
 impl DbRelay {
-	pub async fn get_all(conn: &mut PoolConnection<Sqlite>) -> Result<Vec<DbRelay>, DatabaseError> {
+	pub async fn get_all(tx: &mut Transaction<'_, Sqlite>) -> Result<Vec<DbRelay>, DatabaseError> {
 		sqlx::query_as!(DbRelay, "SELECT * FROM relays")
-			.fetch_all(conn.deref_mut())
+			.fetch_all(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		id: i64,
 	) -> Result<Option<DbRelay>, DatabaseError> {
 		sqlx::query_as!(DbRelay, "SELECT * FROM relays WHERE id = ?", id)
-			.fetch_optional(conn.deref_mut())
+			.fetch_optional(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_controller_and_num(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		controller: &DbController,
 		number: i64,
 	) -> Result<Option<DbRelay>, DatabaseError> {
@@ -48,38 +48,38 @@ impl DbRelay {
 			controller.id,
 			number
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_controller_and_num_or_create(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		controller: &DbController,
 		number: i64,
 		new_name: &str,
 	) -> Result<(DbRelay, bool), DatabaseError> {
-		match DbRelay::get_by_controller_and_num(conn, controller, number).await? {
+		match DbRelay::get_by_controller_and_num(tx, controller, number).await? {
 			Some(relay) => Ok((relay, false)),
 			None => {
-				let relay = DbRelay::create(conn, new_name, number, controller).await?;
+				let relay = DbRelay::create(tx, new_name, number, controller).await?;
 				Ok((relay, true))
 			}
 		}
 	}
 
 	pub async fn get_by_tag(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		tag: &DbTag,
 	) -> Result<Vec<DbRelay>, DatabaseError> {
 		sqlx::query_as!(DbRelay, "SELECT relay.* FROM relays AS relay INNER JOIN junction_tag ON junction_tag.relay_id = relay.id WHERE junction_tag.tag_id = ?", tag.id)
-			.fetch_all(conn.deref_mut())
+			.fetch_all(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn create(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		new_name: &str,
 		new_number: i64,
 		new_controller: &DbController,
@@ -91,14 +91,14 @@ impl DbRelay {
 			new_number,
 			new_controller.id,
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
 
-	pub async fn delete(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
+	pub async fn delete(&self, tx: &mut Transaction<'_, Sqlite>) -> Result<(), DatabaseError> {
 		sqlx::query!("DELETE FROM relays WHERE id = ?", self.id)
-			.execute(conn.deref_mut())
+			.execute(tx.deref_mut())
 			.await
 			.map(|res| match res.rows_affected() {
 				0 => Err(DatabaseError::DeleteError),
@@ -108,68 +108,68 @@ impl DbRelay {
 
 	pub async fn update(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		new_name: &str,
 	) -> Result<DbRelay, DatabaseError> {
 		sqlx::query!("UPDATE relays SET name = ? WHERE id = ?", new_name, self.id,)
-			.execute(conn.deref_mut())
+			.execute(tx.deref_mut())
 			.await?;
 
-		DbRelay::get(conn, self.id)
+		DbRelay::get(tx, self.id)
 			.await?
 			.ok_or(DatabaseError::UpdateGetError)
 	}
 
 	pub async fn get_controller(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<DbController, DatabaseError> {
-		DbController::get(conn, self.controller_id)
+		DbController::get(tx, self.controller_id)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
 
 	pub async fn get_tags(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<Vec<String>, DatabaseError> {
 		sqlx::query_scalar!("SELECT tag FROM tags INNER JOIN junction_tag ON junction_tag.tag_id = tags.id WHERE junction_tag.relay_id = ?", self.id)
-			.fetch_all(conn.deref_mut())
+			.fetch_all(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn set_tags(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		new_tags: &[String],
 	) -> Result<(), DatabaseError> {
 		sqlx::query!("DELETE FROM junction_tag WHERE relay_id = ?", self.id)
-			.execute(conn.deref_mut())
+			.execute(tx.deref_mut())
 			.await?;
 
 		for new_tag in new_tags {
-			let tag: DbTag = DbTag::get_by_tag_or_create(conn, new_tag).await?;
-			DbJunctionTag::link_relay(conn, &tag, self).await?;
+			let tag: DbTag = DbTag::get_by_tag_or_create(tx, new_tag).await?;
+			DbJunctionTag::link_relay(tx, &tag, self).await?;
 		}
 		Ok(())
 	}
 
 	pub async fn reload(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<DbRelay, DatabaseError> {
-		Self::get(conn, self.id)
+		Self::get(tx, self.id)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
 
 	pub async fn get_active_schedule(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<DbSchedule, DatabaseError> {
 		let weekday = utils::get_weekday();
-		DbJunctionRelaySchedule::get_schedule(conn, self, weekday as Weekday)
+		DbJunctionRelaySchedule::get_schedule(tx, self, weekday as Weekday)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
diff --git a/src/db/schedules.rs b/src/db/schedules.rs
index 6a792fd..334bc11 100644
--- a/src/db/schedules.rs
+++ b/src/db/schedules.rs
@@ -3,7 +3,7 @@ use std::ops::DerefMut;
 
 use chrono::NaiveTime;
 use serde_derive::{Deserialize, Serialize};
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::db::model_utils::Period;
@@ -26,26 +26,26 @@ pub struct DbPeriods(pub Vec<Period>);
 
 impl DbSchedule {
 	pub async fn get_all(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<Vec<DbSchedule>, DatabaseError> {
 		sqlx::query_as!(DbSchedule, "SELECT * FROM schedules")
-			.fetch_all(conn.deref_mut())
+			.fetch_all(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		id: i64,
 	) -> Result<Option<DbSchedule>, DatabaseError> {
 		sqlx::query_as!(DbSchedule, "SELECT * FROM schedules WHERE id = ?", id)
-			.fetch_optional(conn.deref_mut())
+			.fetch_optional(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_uid(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		filter_uid: &ScheduleUid,
 	) -> Result<Option<DbSchedule>, DatabaseError> {
 		sqlx::query_as!(
@@ -53,23 +53,23 @@ impl DbSchedule {
 			"SELECT * FROM schedules WHERE uid = ?",
 			filter_uid
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_tag(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		tag: &DbTag,
 	) -> Result<Vec<DbSchedule>, DatabaseError> {
 		sqlx::query_as!(DbSchedule, "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())
+			.fetch_all(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn delete_by_uid(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		filter_uid: ScheduleUid,
 	) -> Result<(), DatabaseError> {
 		let filter_uid = match filter_uid {
@@ -79,7 +79,7 @@ impl DbSchedule {
 		}?;
 
 		if sqlx::query_scalar!("SELECT 1 FROM schedules WHERE uid = ?", filter_uid)
-			.fetch_optional(conn.deref_mut())
+			.fetch_optional(tx.deref_mut())
 			.await?
 			.is_none()
 		{
@@ -87,7 +87,7 @@ impl DbSchedule {
 		}
 
 		sqlx::query!("DELETE FROM schedules WHERE uid = ?", filter_uid)
-			.execute(conn.deref_mut())
+			.execute(tx.deref_mut())
 			.await
 			.map(|res| match res.rows_affected() {
 				0 => Err(DatabaseError::DeleteError),
@@ -96,7 +96,7 @@ impl DbSchedule {
 	}
 
 	pub async fn create(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		new_uid: ScheduleUid,
 		new_name: &str,
 		new_periods: &DbPeriods,
@@ -108,45 +108,45 @@ impl DbSchedule {
 			new_name,
 			new_periods,
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
 
 	pub async fn get_by_uid_or_create(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		uid: ScheduleUid,
 		name: &str,
 		periods: &DbPeriods,
 	) -> Result<(DbSchedule, bool), DatabaseError> {
-		match DbSchedule::get_by_uid(conn, &uid).await? {
+		match DbSchedule::get_by_uid(tx, &uid).await? {
 			Some(schedule) => Ok((schedule, false)),
 			None => {
-				let schedule = DbSchedule::create(conn, uid, name, periods).await?;
+				let schedule = DbSchedule::create(tx, uid, name, periods).await?;
 				Ok((schedule, true))
 			}
 		}
 	}
 
-	pub async fn get_on(conn: &mut PoolConnection<Sqlite>) -> Result<DbSchedule, DatabaseError> {
-		if let Some(schedule) = DbSchedule::get_by_uid(conn, &ScheduleUid::On).await? {
+	pub async fn get_on(tx: &mut Transaction<'_, Sqlite>) -> Result<DbSchedule, DatabaseError> {
+		if let Some(schedule) = DbSchedule::get_by_uid(tx, &ScheduleUid::On).await? {
 			return Ok(schedule);
 		}
 		let periods = DbPeriods(vec![Period::new_on()]);
-		Self::create(conn, ScheduleUid::On, "On", &periods).await
+		Self::create(tx, ScheduleUid::On, "On", &periods).await
 	}
 
-	pub async fn get_off(conn: &mut PoolConnection<Sqlite>) -> Result<DbSchedule, DatabaseError> {
-		if let Some(schedule) = DbSchedule::get_by_uid(conn, &ScheduleUid::Off).await? {
+	pub async fn get_off(tx: &mut Transaction<'_, Sqlite>) -> Result<DbSchedule, DatabaseError> {
+		if let Some(schedule) = DbSchedule::get_by_uid(tx, &ScheduleUid::Off).await? {
 			return Ok(schedule);
 		}
 		let periods = DbPeriods(vec![]);
-		Self::create(conn, ScheduleUid::Off, "Off", &periods).await
+		Self::create(tx, ScheduleUid::Off, "Off", &periods).await
 	}
 
 	pub async fn update(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		new_name: &str,
 		new_periods: &DbPeriods,
 	) -> Result<DbSchedule, DatabaseError> {
@@ -162,35 +162,35 @@ impl DbSchedule {
 			new_periods,
 			self.id,
 		)
-		.execute(conn.deref_mut())
+		.execute(tx.deref_mut())
 		.await?;
 
-		DbSchedule::get(conn, self.id)
+		DbSchedule::get(tx, self.id)
 			.await?
 			.ok_or(DatabaseError::UpdateGetError)
 	}
 
 	pub async fn get_tags(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<Vec<String>, 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())
+			.fetch_all(tx.deref_mut())
 			.await?)
 	}
 
 	pub async fn set_tags(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		new_tags: &[String],
 	) -> Result<(), DatabaseError> {
 		sqlx::query!("DELETE FROM junction_tag WHERE schedule_id = ?", self.id)
-			.execute(conn.deref_mut())
+			.execute(tx.deref_mut())
 			.await?;
 
 		for new_tag in new_tags {
-			let tag: DbTag = DbTag::get_by_tag_or_create(conn, new_tag).await?;
-			DbJunctionTag::link_schedule(conn, &tag, self).await?;
+			let tag: DbTag = DbTag::get_by_tag_or_create(tx, new_tag).await?;
+			DbJunctionTag::link_schedule(tx, &tag, self).await?;
 		}
 		Ok(())
 	}
diff --git a/src/db/tag.rs b/src/db/tag.rs
index 9c15f79..7093068 100644
--- a/src/db/tag.rs
+++ b/src/db/tag.rs
@@ -1,7 +1,7 @@
 use std::ops::DerefMut;
 
 use serde_derive::Serialize;
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::errors::DatabaseError;
@@ -14,7 +14,7 @@ pub struct DbTag {
 
 impl DbTag {
 	pub async fn create(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		new_tag: &str,
 	) -> Result<DbTag, DatabaseError> {
 		if new_tag.is_empty() {
@@ -26,54 +26,54 @@ impl DbTag {
 			"INSERT INTO tags (tag) VALUES (?) RETURNING *",
 			new_tag
 		)
-		.fetch_optional(conn.deref_mut())
+		.fetch_optional(tx.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
 
-	pub async fn get_all(conn: &mut PoolConnection<Sqlite>) -> Result<Vec<DbTag>, DatabaseError> {
+	pub async fn get_all(tx: &mut Transaction<'_, Sqlite>) -> Result<Vec<DbTag>, DatabaseError> {
 		sqlx::query_as!(DbTag, "SELECT * FROM tags")
-			.fetch_all(conn.deref_mut())
+			.fetch_all(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		id: i64,
 	) -> Result<Option<DbTag>, DatabaseError> {
 		sqlx::query_as!(DbTag, "SELECT * FROM tags WHERE id = ?", id)
-			.fetch_optional(conn.deref_mut())
+			.fetch_optional(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_tag_or_create(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		target_tag: &str,
 	) -> Result<DbTag, DatabaseError> {
-		match DbTag::get_by_tag(conn, target_tag).await? {
+		match DbTag::get_by_tag(tx, target_tag).await? {
 			Some(tag) => Ok(tag),
-			None => DbTag::create(conn, target_tag).await,
+			None => DbTag::create(tx, target_tag).await,
 		}
 	}
 
 	pub async fn get_by_tag(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		target_tag: &str,
 	) -> Result<Option<DbTag>, DatabaseError> {
 		sqlx::query_as!(DbTag, "SELECT * FROM tags WHERE tag = ?", target_tag)
-			.fetch_optional(conn.deref_mut())
+			.fetch_optional(tx.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn delete_by_tag(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		filter_tag: &str,
 	) -> Result<(), DatabaseError> {
 		if sqlx::query_scalar!("SELECT 1 FROM tags WHERE tag = ?", filter_tag)
-			.fetch_optional(conn.deref_mut())
+			.fetch_optional(tx.deref_mut())
 			.await?
 			.is_none()
 		{
@@ -81,7 +81,7 @@ impl DbTag {
 		}
 
 		sqlx::query!("DELETE FROM tags WHERE tag = ?", filter_tag)
-			.execute(conn.deref_mut())
+			.execute(tx.deref_mut())
 			.await
 			.map(|res| match res.rows_affected() {
 				0 => Err(DatabaseError::DeleteError),
diff --git a/src/models/controller.rs b/src/models/controller.rs
index 96d7b00..621a966 100644
--- a/src/models/controller.rs
+++ b/src/models/controller.rs
@@ -4,7 +4,7 @@ use actix::MessageResponse;
 use chrono::NaiveTime;
 use futures::executor::block_on;
 use serde_derive::{Deserialize, Serialize};
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::db::DbController;
@@ -24,16 +24,16 @@ impl FromDbModel for Controller {
 	type DbModelCache = Vec<Relay>;
 
 	fn from_db_model(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError> {
-		let relays_db = block_on(db_model.get_relays(conn))?;
-		let cache = convert_db_list_cache(conn, relays_db, db_model.clone())?;
-		Self::from_db_model_cache(conn, db_model, cache)
+		let relays_db = block_on(db_model.get_relays(tx))?;
+		let cache = convert_db_list_cache(tx, relays_db, db_model.clone())?;
+		Self::from_db_model_cache(tx, db_model, cache)
 	}
 
 	fn from_db_model_cache(
-		_conn: &mut PoolConnection<Sqlite>,
+		_tx: &mut Transaction<'_, Sqlite>,
 		db_model: Self::DbModel,
 		cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError> {
@@ -45,10 +45,10 @@ impl FromDbModel for Controller {
 }
 
 impl Controller {
-	pub fn reload(&mut self, conn: &mut PoolConnection<Sqlite>) -> Result<(), EmgauwaError> {
-		self.c = block_on(self.c.reload(conn))?;
+	pub fn reload(&mut self, tx: &mut Transaction<'_, Sqlite>) -> Result<(), EmgauwaError> {
+		self.c = block_on(self.c.reload(tx))?;
 		for relay in &mut self.relays {
-			relay.reload(conn)?;
+			relay.reload(tx)?;
 		}
 		Ok(())
 	}
diff --git a/src/models/macro.rs b/src/models/macro.rs
index 0598c0c..1ee8f1e 100644
--- a/src/models/macro.rs
+++ b/src/models/macro.rs
@@ -1,6 +1,6 @@
 use futures::executor::block_on;
 use serde_derive::{Deserialize, Serialize};
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::db::DbMacro;
@@ -19,19 +19,19 @@ impl FromDbModel for Macro {
 	type DbModelCache = ();
 
 	fn from_db_model(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError> {
-		Self::from_db_model_cache(conn, db_model, ())
+		Self::from_db_model_cache(tx, db_model, ())
 	}
 
 	fn from_db_model_cache(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		db_model: Self::DbModel,
 		_cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError> {
-		let actions_db = block_on(db_model.get_actions(conn))?;
-		let actions: Vec<MacroAction> = convert_db_list(conn, actions_db)?;
+		let actions_db = block_on(db_model.get_actions(tx))?;
+		let actions: Vec<MacroAction> = convert_db_list(tx, actions_db)?;
 
 		Ok(Macro {
 			m: db_model,
diff --git a/src/models/macro_action.rs b/src/models/macro_action.rs
index a36a45a..7b12428 100644
--- a/src/models/macro_action.rs
+++ b/src/models/macro_action.rs
@@ -1,6 +1,6 @@
 use futures::executor::block_on;
 use serde_derive::{Deserialize, Serialize};
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::db::{DbJunctionRelaySchedule, DbMacroAction};
@@ -19,22 +19,22 @@ impl FromDbModel for MacroAction {
 	type DbModelCache = ();
 
 	fn from_db_model(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError> {
-		Self::from_db_model_cache(conn, db_model, ())
+		Self::from_db_model_cache(tx, db_model, ())
 	}
 
 	fn from_db_model_cache(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		db_model: Self::DbModel,
 		_cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError> {
-		let schedule_db = block_on(db_model.get_schedule(conn))?;
-		let schedule = Schedule::from_db_model(conn, schedule_db)?;
+		let schedule_db = block_on(db_model.get_schedule(tx))?;
+		let schedule = Schedule::from_db_model(tx, schedule_db)?;
 
-		let relay_db = block_on(db_model.get_relay(conn))?;
-		let relay = Relay::from_db_model(conn, relay_db)?;
+		let relay_db = block_on(db_model.get_relay(tx))?;
+		let relay = Relay::from_db_model(tx, relay_db)?;
 
 		let weekday = db_model.weekday;
 
@@ -47,8 +47,8 @@ impl FromDbModel for MacroAction {
 }
 
 impl MacroAction {
-	pub async fn execute(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), EmgauwaError> {
-		DbJunctionRelaySchedule::set_schedule(conn, &self.relay.r, &self.schedule.s, self.weekday)
+	pub async fn execute(&self, tx: &mut Transaction<'_, Sqlite>) -> Result<(), EmgauwaError> {
+		DbJunctionRelaySchedule::set_schedule(tx, &self.relay.r, &self.schedule.s, self.weekday)
 			.await?;
 		Ok(())
 	}
diff --git a/src/models/mod.rs b/src/models/mod.rs
index 570bf48..58a40b9 100644
--- a/src/models/mod.rs
+++ b/src/models/mod.rs
@@ -10,7 +10,7 @@ pub use macro_action::MacroAction;
 pub use r#macro::Macro;
 pub use relay::Relay;
 pub use schedule::Schedule;
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 pub use tag::Tag;
 
@@ -21,14 +21,14 @@ pub trait FromDbModel {
 	type DbModelCache: Clone;
 
 	fn from_db_model(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError>
 	where
 		Self: Sized;
 
 	fn from_db_model_cache(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		db_model: Self::DbModel,
 		cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError>
@@ -37,15 +37,15 @@ pub trait FromDbModel {
 }
 
 fn convert_db_list_generic<T: FromDbModel>(
-	conn: &mut PoolConnection<Sqlite>,
+	tx: &mut Transaction<'_, Sqlite>,
 	db_models: Vec<T::DbModel>,
 	cache: Option<T::DbModelCache>,
 ) -> Result<Vec<T>, DatabaseError> {
 	let mut result: Vec<T> = Vec::new();
 	for db_model in db_models {
 		let new = match &cache {
-			Some(c) => T::from_db_model_cache(conn, db_model, c.clone()),
-			None => T::from_db_model(conn, db_model),
+			Some(c) => T::from_db_model_cache(tx, db_model, c.clone()),
+			None => T::from_db_model(tx, db_model),
 		}?;
 		result.push(new);
 	}
@@ -53,16 +53,16 @@ fn convert_db_list_generic<T: FromDbModel>(
 }
 
 pub fn convert_db_list<T: FromDbModel>(
-	conn: &mut PoolConnection<Sqlite>,
+	tx: &mut Transaction<'_, Sqlite>,
 	db_models: Vec<T::DbModel>,
 ) -> Result<Vec<T>, DatabaseError> {
-	convert_db_list_generic(conn, db_models, None)
+	convert_db_list_generic(tx, db_models, None)
 }
 
 pub fn convert_db_list_cache<T: FromDbModel>(
-	conn: &mut PoolConnection<Sqlite>,
+	tx: &mut Transaction<'_, Sqlite>,
 	db_models: Vec<T::DbModel>,
 	cache: T::DbModelCache,
 ) -> Result<Vec<T>, DatabaseError> {
-	convert_db_list_generic(conn, db_models, Some(cache))
+	convert_db_list_generic(tx, db_models, Some(cache))
 }
diff --git a/src/models/relay.rs b/src/models/relay.rs
index e6ecd0f..20c214d 100644
--- a/src/models/relay.rs
+++ b/src/models/relay.rs
@@ -3,7 +3,7 @@ use std::time::Instant;
 use chrono::NaiveTime;
 use futures::executor::block_on;
 use serde_derive::{Deserialize, Serialize};
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule};
@@ -32,23 +32,23 @@ impl FromDbModel for Relay {
 	type DbModelCache = DbController;
 
 	fn from_db_model(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError> {
-		let cache = block_on(db_model.get_controller(conn))?;
-		Self::from_db_model_cache(conn, db_model, cache)
+		let cache = block_on(db_model.get_controller(tx))?;
+		Self::from_db_model_cache(tx, db_model, cache)
 	}
 
 	fn from_db_model_cache(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		db_model: Self::DbModel,
 		cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError> {
-		let tags = block_on(db_model.get_tags(conn))?;
+		let tags = block_on(db_model.get_tags(tx))?;
 		let controller_id = cache.uid.clone();
 
-		let schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &db_model))?;
-		let active_schedule = block_on(db_model.get_active_schedule(conn))?;
+		let schedules = block_on(DbJunctionRelaySchedule::get_schedules(tx, &db_model))?;
+		let active_schedule = block_on(db_model.get_active_schedule(tx))?;
 
 		let is_on = None;
 
@@ -66,19 +66,19 @@ impl FromDbModel for Relay {
 }
 
 impl Relay {
-	pub fn reload(&mut self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
-		self.r = block_on(self.r.reload(conn))?;
-		self.schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &self.r))?;
-		self.reload_active_schedule(conn)?;
+	pub fn reload(&mut self, tx: &mut Transaction<'_, Sqlite>) -> Result<(), DatabaseError> {
+		self.r = block_on(self.r.reload(tx))?;
+		self.schedules = block_on(DbJunctionRelaySchedule::get_schedules(tx, &self.r))?;
+		self.reload_active_schedule(tx)?;
 
 		Ok(())
 	}
 
 	pub fn reload_active_schedule(
 		&mut self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<(), DatabaseError> {
-		self.active_schedule = block_on(self.r.get_active_schedule(conn))?;
+		self.active_schedule = block_on(self.r.get_active_schedule(tx))?;
 		Ok(())
 	}
 
diff --git a/src/models/schedule.rs b/src/models/schedule.rs
index 9bf07b4..7a7903f 100644
--- a/src/models/schedule.rs
+++ b/src/models/schedule.rs
@@ -1,6 +1,6 @@
 use futures::executor::block_on;
 use serde_derive::{Deserialize, Serialize};
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::db::DbSchedule;
@@ -19,15 +19,15 @@ impl FromDbModel for Schedule {
 	type DbModelCache = Vec<String>;
 
 	fn from_db_model(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError> {
-		let cache = block_on(db_model.get_tags(conn))?;
-		Self::from_db_model_cache(conn, db_model, cache)
+		let cache = block_on(db_model.get_tags(tx))?;
+		Self::from_db_model_cache(tx, db_model, cache)
 	}
 
 	fn from_db_model_cache(
-		_conn: &mut PoolConnection<Sqlite>,
+		_tx: &mut Transaction<'_, Sqlite>,
 		db_model: Self::DbModel,
 		cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError> {
diff --git a/src/models/tag.rs b/src/models/tag.rs
index 7e23fb4..a5370dc 100644
--- a/src/models/tag.rs
+++ b/src/models/tag.rs
@@ -1,7 +1,7 @@
 use actix::MessageResponse;
 use futures::executor::block_on;
 use serde_derive::{Deserialize, Serialize};
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::db::{DbRelay, DbSchedule, DbTag};
@@ -20,21 +20,21 @@ impl FromDbModel for Tag {
 	type DbModelCache = (Vec<Relay>, Vec<Schedule>);
 
 	fn from_db_model(
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError> {
-		let db_schedules = block_on(DbSchedule::get_by_tag(conn, &db_model))?;
-		let schedules: Vec<Schedule> = convert_db_list(conn, db_schedules)?;
+		let db_schedules = block_on(DbSchedule::get_by_tag(tx, &db_model))?;
+		let schedules: Vec<Schedule> = convert_db_list(tx, db_schedules)?;
 
-		let db_relays = block_on(DbRelay::get_by_tag(conn, &db_model))?;
-		let relays: Vec<Relay> = convert_db_list(conn, db_relays)?;
+		let db_relays = block_on(DbRelay::get_by_tag(tx, &db_model))?;
+		let relays: Vec<Relay> = convert_db_list(tx, db_relays)?;
 
 		let cache = (relays, schedules);
-		Self::from_db_model_cache(conn, db_model, cache)
+		Self::from_db_model_cache(tx, db_model, cache)
 	}
 
 	fn from_db_model_cache(
-		_conn: &mut PoolConnection<Sqlite>,
+		_tx: &mut Transaction<'_, Sqlite>,
 		db_model: Self::DbModel,
 		cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError> {
diff --git a/src/types/request.rs b/src/types/request.rs
index 5c9a927..eb09abe 100644
--- a/src/types/request.rs
+++ b/src/types/request.rs
@@ -1,5 +1,5 @@
 use serde_derive::{Deserialize, Serialize};
-use sqlx::pool::PoolConnection;
+use sqlx::Transaction;
 use sqlx::Sqlite;
 
 use crate::db::{DbPeriods, DbSchedule};
@@ -86,9 +86,9 @@ pub struct RequestMacroExecute {
 impl RequestScheduleId {
 	pub async fn get_schedule(
 		&self,
-		conn: &mut PoolConnection<Sqlite>,
+		tx: &mut Transaction<'_, Sqlite>,
 	) -> Result<DbSchedule, DatabaseError> {
-		DbSchedule::get_by_uid(conn, &self.id)
+		DbSchedule::get_by_uid(tx, &self.id)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}

From fc4c1df09a57add7f9f5860f0f169cfdf3c8b8f2 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Thu, 2 May 2024 19:35:22 +0200
Subject: [PATCH 02/18] Revert "Add sql transactions"

This caused the error "locked database".
This reverts commit 19e2ea003b577b738d4aa962c78bb28220af89ca.
---
 src/db/controllers.rs             | 58 ++++++++++++++--------------
 src/db/junction_relay_schedule.rs | 36 ++++++++---------
 src/db/junction_tag.rs            | 10 ++---
 src/db/macro.rs                   | 54 +++++++++++++-------------
 src/db/macro_action.rs            | 30 +++++++--------
 src/db/mod.rs                     |  8 ++--
 src/db/relays.rs                  | 62 +++++++++++++++---------------
 src/db/schedules.rs               | 64 +++++++++++++++----------------
 src/db/tag.rs                     | 30 +++++++--------
 src/models/controller.rs          | 18 ++++-----
 src/models/macro.rs               | 12 +++---
 src/models/macro_action.rs        | 20 +++++-----
 src/models/mod.rs                 | 20 +++++-----
 src/models/relay.rs               | 28 +++++++-------
 src/models/schedule.rs            | 10 ++---
 src/models/tag.rs                 | 16 ++++----
 src/types/request.rs              |  6 +--
 17 files changed, 240 insertions(+), 242 deletions(-)

diff --git a/src/db/controllers.rs b/src/db/controllers.rs
index 79cb0df..bf0a1f0 100644
--- a/src/db/controllers.rs
+++ b/src/db/controllers.rs
@@ -1,7 +1,7 @@
 use std::ops::DerefMut;
 
 use serde_derive::{Deserialize, Serialize};
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::db::{DbRelay, DbTag};
@@ -21,26 +21,26 @@ pub struct DbController {
 
 impl DbController {
 	pub async fn get_all(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<Vec<DbController>, DatabaseError> {
 		sqlx::query_as!(DbController, "SELECT * FROM controllers")
-			.fetch_all(tx.deref_mut())
+			.fetch_all(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		id: i64,
 	) -> Result<Option<DbController>, DatabaseError> {
 		sqlx::query_as!(DbController, "SELECT * FROM controllers WHERE id = ?", id)
-			.fetch_optional(tx.deref_mut())
+			.fetch_optional(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_uid(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		filter_uid: &EmgauwaUid,
 	) -> Result<Option<DbController>, DatabaseError> {
 		sqlx::query_as!(
@@ -48,39 +48,39 @@ impl DbController {
 			"SELECT * FROM controllers WHERE uid = ?",
 			filter_uid
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_uid_or_create(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		uid: &EmgauwaUid,
 		new_name: &str,
 		new_relay_count: i64,
 	) -> Result<DbController, DatabaseError> {
-		match DbController::get_by_uid(tx, uid).await? {
+		match DbController::get_by_uid(conn, uid).await? {
 			Some(tag) => Ok(tag),
-			None => DbController::create(tx, uid, new_name, new_relay_count).await,
+			None => DbController::create(conn, uid, new_name, new_relay_count).await,
 		}
 	}
 
 	pub async fn get_by_tag(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		tag: &DbTag,
 	) -> Result<Vec<DbController>, DatabaseError> {
 		sqlx::query_as!(DbController, "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(tx.deref_mut())
+			.fetch_all(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn delete_by_uid(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		filter_uid: EmgauwaUid,
 	) -> Result<(), DatabaseError> {
 		if sqlx::query_scalar!("SELECT 1 FROM controllers WHERE uid = ?", filter_uid)
-			.fetch_optional(tx.deref_mut())
+			.fetch_optional(conn.deref_mut())
 			.await?
 			.is_none()
 		{
@@ -88,7 +88,7 @@ impl DbController {
 		}
 
 		sqlx::query!("DELETE FROM controllers WHERE uid = ?", filter_uid)
-			.execute(tx.deref_mut())
+			.execute(conn.deref_mut())
 			.await
 			.map(|res| match res.rows_affected() {
 				0 => Err(DatabaseError::DeleteError),
@@ -97,7 +97,7 @@ impl DbController {
 	}
 
 	pub async fn create(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		new_uid: &EmgauwaUid,
 		new_name: &str,
 		new_relay_count: i64,
@@ -109,14 +109,14 @@ impl DbController {
 			new_name,
 			new_relay_count,
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
 
 	pub async fn update(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		new_name: &str,
 		new_relay_count: i64,
 	) -> Result<DbController, DatabaseError> {
@@ -126,17 +126,17 @@ impl DbController {
 			new_relay_count,
 			self.id,
 		)
-		.execute(tx.deref_mut())
+		.execute(conn.deref_mut())
 		.await?;
 
-		Self::get(tx, self.id)
+		Self::get(conn, self.id)
 			.await?
 			.ok_or(DatabaseError::UpdateGetError)
 	}
 
 	pub async fn update_active(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		new_active: bool,
 	) -> Result<DbController, DatabaseError> {
 		sqlx::query!(
@@ -144,40 +144,40 @@ impl DbController {
 			new_active,
 			self.id,
 		)
-		.execute(tx.deref_mut())
+		.execute(conn.deref_mut())
 		.await?;
 
-		Self::get(tx, self.id)
+		Self::get(conn, self.id)
 			.await?
 			.ok_or(DatabaseError::UpdateGetError)
 	}
 
 	pub async fn get_relays(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<Vec<DbRelay>, DatabaseError> {
 		sqlx::query_as!(
 			DbRelay,
 			"SELECT * FROM relays WHERE controller_id = ?",
 			self.id
 		)
-		.fetch_all(tx.deref_mut())
+		.fetch_all(conn.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
-	pub async fn all_inactive(tx: &mut Transaction<'_, Sqlite>) -> Result<(), DatabaseError> {
+	pub async fn all_inactive(conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
 		sqlx::query!("UPDATE controllers SET active = 0")
-			.execute(tx.deref_mut())
+			.execute(conn.deref_mut())
 			.await?;
 		Ok(())
 	}
 
 	pub async fn reload(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<DbController, DatabaseError> {
-		Self::get(tx, self.id)
+		Self::get(conn, self.id)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
diff --git a/src/db/junction_relay_schedule.rs b/src/db/junction_relay_schedule.rs
index 74710eb..f081a54 100644
--- a/src/db/junction_relay_schedule.rs
+++ b/src/db/junction_relay_schedule.rs
@@ -1,6 +1,6 @@
 use std::ops::DerefMut;
 
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::db::{DbRelay, DbSchedule};
@@ -16,7 +16,7 @@ pub struct DbJunctionRelaySchedule {
 
 impl DbJunctionRelaySchedule {
 	pub async fn get(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		id: i64,
 	) -> Result<Option<DbJunctionRelaySchedule>, DatabaseError> {
 		sqlx::query_as!(
@@ -24,13 +24,13 @@ impl DbJunctionRelaySchedule {
 			"SELECT * FROM junction_relay_schedule WHERE id = ?",
 			id
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_junction_by_relay_and_weekday(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		relay: &DbRelay,
 		weekday: Weekday,
 	) -> Result<Option<DbJunctionRelaySchedule>, DatabaseError> {
@@ -40,13 +40,13 @@ impl DbJunctionRelaySchedule {
 			relay.id,
 			weekday
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_relays(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		schedule: &DbSchedule,
 	) -> Result<Vec<DbRelay>, DatabaseError> {
 		sqlx::query_as!(
@@ -57,13 +57,13 @@ impl DbJunctionRelaySchedule {
 			ORDER BY junction_relay_schedule.weekday"#,
 			schedule.id
 		)
-		.fetch_all(tx.deref_mut())
+		.fetch_all(conn.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_schedule(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		relay: &DbRelay,
 		weekday: Weekday,
 	) -> Result<Option<DbSchedule>, DatabaseError> {
@@ -75,13 +75,13 @@ impl DbJunctionRelaySchedule {
 			relay.id,
 			weekday
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_schedules(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		relay: &DbRelay,
 	) -> Result<Vec<DbSchedule>, DatabaseError> {
 		sqlx::query_as!(
@@ -92,18 +92,18 @@ impl DbJunctionRelaySchedule {
 			ORDER BY junction_relay_schedule.weekday"#,
 			relay.id
 		)
-		.fetch_all(tx.deref_mut())
+		.fetch_all(conn.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn set_schedule(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		relay: &DbRelay,
 		schedule: &DbSchedule,
 		weekday: Weekday,
 	) -> Result<DbJunctionRelaySchedule, DatabaseError> {
-		match Self::get_junction_by_relay_and_weekday(tx, relay, weekday).await? {
+		match Self::get_junction_by_relay_and_weekday(conn, relay, weekday).await? {
 			None => sqlx::query_as!(
 				DbJunctionRelaySchedule,
 				"INSERT INTO junction_relay_schedule (weekday, relay_id, schedule_id) VALUES (?, ?, ?) RETURNING *",
@@ -111,7 +111,7 @@ impl DbJunctionRelaySchedule {
 				relay.id,
 				schedule.id
 			)
-			.fetch_optional(tx.deref_mut())
+			.fetch_optional(conn.deref_mut())
 			.await?
 			.ok_or(DatabaseError::InsertGetError),
 
@@ -123,10 +123,10 @@ impl DbJunctionRelaySchedule {
                     schedule.id,
 					junction.id
                 )
-					.execute(tx.deref_mut())
+					.execute(conn.deref_mut())
 					.await?;
 
-				Self::get(tx, junction.id)
+				Self::get(conn, junction.id)
 					.await?
 					.ok_or(DatabaseError::UpdateGetError)
 			}
@@ -134,12 +134,12 @@ impl DbJunctionRelaySchedule {
 	}
 
 	pub async fn set_schedules(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		relay: &DbRelay,
 		schedules: Vec<&DbSchedule>,
 	) -> Result<(), DatabaseError> {
 		for (weekday, schedule) in schedules.iter().enumerate() {
-			Self::set_schedule(tx, relay, schedule, weekday as Weekday).await?;
+			Self::set_schedule(conn, relay, schedule, weekday as Weekday).await?;
 		}
 		Ok(())
 	}
diff --git a/src/db/junction_tag.rs b/src/db/junction_tag.rs
index 51a3ba5..f1b8816 100644
--- a/src/db/junction_tag.rs
+++ b/src/db/junction_tag.rs
@@ -1,6 +1,6 @@
 use std::ops::DerefMut;
 
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::db::{DbRelay, DbSchedule, DbTag};
@@ -15,7 +15,7 @@ pub struct DbJunctionTag {
 
 impl DbJunctionTag {
 	pub async fn link_relay(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		tag: &DbTag,
 		relay: &DbRelay,
 	) -> Result<DbJunctionTag, DatabaseError> {
@@ -25,13 +25,13 @@ impl DbJunctionTag {
 			tag.id,
 			relay.id
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
 
 	pub async fn link_schedule(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		tag: &DbTag,
 		schedule: &DbSchedule,
 	) -> Result<DbJunctionTag, DatabaseError> {
@@ -41,7 +41,7 @@ impl DbJunctionTag {
 			tag.id,
 			schedule.id
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
diff --git a/src/db/macro.rs b/src/db/macro.rs
index 6aa857c..62092ea 100644
--- a/src/db/macro.rs
+++ b/src/db/macro.rs
@@ -1,7 +1,7 @@
 use std::ops::DerefMut;
 
 use serde_derive::{Deserialize, Serialize};
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::db::{DbController, DbMacroAction, DbRelay, DbSchedule};
@@ -18,35 +18,35 @@ pub struct DbMacro {
 }
 
 impl DbMacro {
-	pub async fn get_all(tx: &mut Transaction<'_, Sqlite>) -> Result<Vec<DbMacro>, DatabaseError> {
+	pub async fn get_all(conn: &mut PoolConnection<Sqlite>) -> Result<Vec<DbMacro>, DatabaseError> {
 		sqlx::query_as!(DbMacro, "SELECT * FROM macros")
-			.fetch_all(tx.deref_mut())
+			.fetch_all(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		id: i64,
 	) -> Result<Option<DbMacro>, DatabaseError> {
 		sqlx::query_as!(DbMacro, "SELECT * FROM macros WHERE id = ?", id)
-			.fetch_optional(tx.deref_mut())
+			.fetch_optional(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_uid(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		filter_uid: &EmgauwaUid,
 	) -> Result<Option<DbMacro>, DatabaseError> {
 		sqlx::query_as!(DbMacro, "SELECT * FROM macros WHERE uid = ?", filter_uid)
-			.fetch_optional(tx.deref_mut())
+			.fetch_optional(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn create(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		new_uid: EmgauwaUid,
 		new_name: &str,
 	) -> Result<DbMacro, DatabaseError> {
@@ -56,14 +56,14 @@ impl DbMacro {
 			new_uid,
 			new_name
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
 
-	pub async fn delete(&self, tx: &mut Transaction<'_, Sqlite>) -> Result<(), DatabaseError> {
+	pub async fn delete(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
 		sqlx::query!("DELETE FROM macros WHERE id = ?", self.id)
-			.execute(tx.deref_mut())
+			.execute(conn.deref_mut())
 			.await
 			.map(|res| match res.rows_affected() {
 				0 => Err(DatabaseError::DeleteError),
@@ -72,11 +72,11 @@ impl DbMacro {
 	}
 
 	pub async fn delete_by_uid(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		filter_uid: EmgauwaUid,
 	) -> Result<(), DatabaseError> {
 		if sqlx::query_scalar!("SELECT 1 FROM macros WHERE uid = ?", filter_uid)
-			.fetch_optional(tx.deref_mut())
+			.fetch_optional(conn.deref_mut())
 			.await?
 			.is_none()
 		{
@@ -84,7 +84,7 @@ impl DbMacro {
 		}
 
 		sqlx::query!("DELETE FROM macros WHERE uid = ?", filter_uid)
-			.execute(tx.deref_mut())
+			.execute(conn.deref_mut())
 			.await
 			.map(|res| match res.rows_affected() {
 				0 => Err(DatabaseError::DeleteError),
@@ -94,62 +94,62 @@ impl DbMacro {
 
 	pub async fn update(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		new_name: &str,
 	) -> Result<DbMacro, DatabaseError> {
 		sqlx::query!("UPDATE relays SET name = ? WHERE id = ?", new_name, self.id,)
-			.execute(tx.deref_mut())
+			.execute(conn.deref_mut())
 			.await?;
 
-		DbMacro::get(tx, self.id)
+		DbMacro::get(conn, self.id)
 			.await?
 			.ok_or(DatabaseError::UpdateGetError)
 	}
 
 	pub async fn set_actions(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		new_actions: &[RequestMacroAction],
 	) -> Result<(), DatabaseError> {
 		sqlx::query!("DELETE FROM macro_actions WHERE macro_id = ?", self.id)
-			.execute(tx.deref_mut())
+			.execute(conn.deref_mut())
 			.await?;
 
 		for new_action in new_actions {
-			let controller = DbController::get_by_uid(tx, &new_action.relay.controller_id)
+			let controller = DbController::get_by_uid(conn, &new_action.relay.controller_id)
 				.await?
 				.ok_or(DatabaseError::NotFound)?;
 			let relay =
-				DbRelay::get_by_controller_and_num(tx, &controller, new_action.relay.number)
+				DbRelay::get_by_controller_and_num(conn, &controller, new_action.relay.number)
 					.await?
 					.ok_or(DatabaseError::NotFound)?;
 
-			let schedule = DbSchedule::get_by_uid(tx, &new_action.schedule.id)
+			let schedule = DbSchedule::get_by_uid(conn, &new_action.schedule.id)
 				.await?
 				.ok_or(DatabaseError::NotFound)?;
 
-			DbMacroAction::create(tx, self, &relay, &schedule, new_action.weekday).await?;
+			DbMacroAction::create(conn, self, &relay, &schedule, new_action.weekday).await?;
 		}
 		Ok(())
 	}
 
 	pub async fn get_actions(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<Vec<DbMacroAction>, DatabaseError> {
 		sqlx::query_as!(
 			DbMacroAction,
 			"SELECT * FROM macro_actions WHERE macro_id = ?",
 			self.id
 		)
-		.fetch_all(tx.deref_mut())
+		.fetch_all(conn.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_actions_weekday(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		weekday: i64,
 	) -> Result<Vec<DbMacroAction>, DatabaseError> {
 		sqlx::query_as!(
@@ -158,7 +158,7 @@ impl DbMacro {
 			self.id,
 			weekday
 		)
-		.fetch_all(tx.deref_mut())
+		.fetch_all(conn.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
diff --git a/src/db/macro_action.rs b/src/db/macro_action.rs
index a640dff..81e0a01 100644
--- a/src/db/macro_action.rs
+++ b/src/db/macro_action.rs
@@ -1,6 +1,6 @@
 use std::ops::DerefMut;
 
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::db::{DbMacro, DbRelay, DbSchedule};
@@ -17,16 +17,16 @@ pub struct DbMacroAction {
 
 impl DbMacroAction {
 	pub async fn get_all(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<Vec<DbMacroAction>, DatabaseError> {
 		sqlx::query_as!(DbMacroAction, "SELECT * FROM macro_actions")
-			.fetch_all(tx.deref_mut())
+			.fetch_all(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		id: i64,
 	) -> Result<Option<DbMacroAction>, DatabaseError> {
 		sqlx::query_as!(
@@ -34,13 +34,13 @@ impl DbMacroAction {
 			"SELECT * FROM macro_actions WHERE id = ?",
 			id
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn create(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		new_macro: &DbMacro,
 		new_relay: &DbRelay,
 		new_schedule: &DbSchedule,
@@ -54,14 +54,14 @@ impl DbMacroAction {
 			new_schedule.id,
 			new_weekday
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
 
-	pub async fn delete(&self, tx: &mut Transaction<'_, Sqlite>) -> Result<(), DatabaseError> {
+	pub async fn delete(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
 		sqlx::query!("DELETE FROM macro_actions WHERE id = ?", self.id)
-			.execute(tx.deref_mut())
+			.execute(conn.deref_mut())
 			.await
 			.map(|res| match res.rows_affected() {
 				0 => Err(DatabaseError::DeleteError),
@@ -71,27 +71,27 @@ impl DbMacroAction {
 
 	pub async fn get_relay(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<DbRelay, DatabaseError> {
-		DbRelay::get(tx, self.relay_id)
+		DbRelay::get(conn, self.relay_id)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
 
 	pub async fn get_schedule(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<DbSchedule, DatabaseError> {
-		DbSchedule::get(tx, self.schedule_id)
+		DbSchedule::get(conn, self.schedule_id)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
 
 	pub async fn get_macro(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<DbMacro, DatabaseError> {
-		DbMacro::get(tx, self.macro_id)
+		DbMacro::get(conn, self.macro_id)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
diff --git a/src/db/mod.rs b/src/db/mod.rs
index 7de0263..7ebb6c3 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -46,12 +46,10 @@ pub async fn init(db: &str) -> Result<Pool<Sqlite>, EmgauwaError> {
 
 	run_migrations(&pool).await?;
 
-	let mut tx = pool.begin().await?;
+	let mut pool_conn = pool.acquire().await?;
 
-	DbSchedule::get_on(&mut tx).await?;
-	DbSchedule::get_off(&mut tx).await?;
-
-	tx.commit().await?;
+	DbSchedule::get_on(&mut pool_conn).await?;
+	DbSchedule::get_off(&mut pool_conn).await?;
 
 	Ok(pool)
 }
diff --git a/src/db/relays.rs b/src/db/relays.rs
index faa82f0..c7f4ca3 100644
--- a/src/db/relays.rs
+++ b/src/db/relays.rs
@@ -1,7 +1,7 @@
 use std::ops::DerefMut;
 
 use serde_derive::{Deserialize, Serialize};
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::db::{DbController, DbJunctionRelaySchedule, DbJunctionTag, DbSchedule, DbTag};
@@ -20,25 +20,25 @@ pub struct DbRelay {
 }
 
 impl DbRelay {
-	pub async fn get_all(tx: &mut Transaction<'_, Sqlite>) -> Result<Vec<DbRelay>, DatabaseError> {
+	pub async fn get_all(conn: &mut PoolConnection<Sqlite>) -> Result<Vec<DbRelay>, DatabaseError> {
 		sqlx::query_as!(DbRelay, "SELECT * FROM relays")
-			.fetch_all(tx.deref_mut())
+			.fetch_all(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		id: i64,
 	) -> Result<Option<DbRelay>, DatabaseError> {
 		sqlx::query_as!(DbRelay, "SELECT * FROM relays WHERE id = ?", id)
-			.fetch_optional(tx.deref_mut())
+			.fetch_optional(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_controller_and_num(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		controller: &DbController,
 		number: i64,
 	) -> Result<Option<DbRelay>, DatabaseError> {
@@ -48,38 +48,38 @@ impl DbRelay {
 			controller.id,
 			number
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_controller_and_num_or_create(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		controller: &DbController,
 		number: i64,
 		new_name: &str,
 	) -> Result<(DbRelay, bool), DatabaseError> {
-		match DbRelay::get_by_controller_and_num(tx, controller, number).await? {
+		match DbRelay::get_by_controller_and_num(conn, controller, number).await? {
 			Some(relay) => Ok((relay, false)),
 			None => {
-				let relay = DbRelay::create(tx, new_name, number, controller).await?;
+				let relay = DbRelay::create(conn, new_name, number, controller).await?;
 				Ok((relay, true))
 			}
 		}
 	}
 
 	pub async fn get_by_tag(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		tag: &DbTag,
 	) -> Result<Vec<DbRelay>, DatabaseError> {
 		sqlx::query_as!(DbRelay, "SELECT relay.* FROM relays AS relay INNER JOIN junction_tag ON junction_tag.relay_id = relay.id WHERE junction_tag.tag_id = ?", tag.id)
-			.fetch_all(tx.deref_mut())
+			.fetch_all(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn create(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		new_name: &str,
 		new_number: i64,
 		new_controller: &DbController,
@@ -91,14 +91,14 @@ impl DbRelay {
 			new_number,
 			new_controller.id,
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
 
-	pub async fn delete(&self, tx: &mut Transaction<'_, Sqlite>) -> Result<(), DatabaseError> {
+	pub async fn delete(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
 		sqlx::query!("DELETE FROM relays WHERE id = ?", self.id)
-			.execute(tx.deref_mut())
+			.execute(conn.deref_mut())
 			.await
 			.map(|res| match res.rows_affected() {
 				0 => Err(DatabaseError::DeleteError),
@@ -108,68 +108,68 @@ impl DbRelay {
 
 	pub async fn update(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		new_name: &str,
 	) -> Result<DbRelay, DatabaseError> {
 		sqlx::query!("UPDATE relays SET name = ? WHERE id = ?", new_name, self.id,)
-			.execute(tx.deref_mut())
+			.execute(conn.deref_mut())
 			.await?;
 
-		DbRelay::get(tx, self.id)
+		DbRelay::get(conn, self.id)
 			.await?
 			.ok_or(DatabaseError::UpdateGetError)
 	}
 
 	pub async fn get_controller(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<DbController, DatabaseError> {
-		DbController::get(tx, self.controller_id)
+		DbController::get(conn, self.controller_id)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
 
 	pub async fn get_tags(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<Vec<String>, DatabaseError> {
 		sqlx::query_scalar!("SELECT tag FROM tags INNER JOIN junction_tag ON junction_tag.tag_id = tags.id WHERE junction_tag.relay_id = ?", self.id)
-			.fetch_all(tx.deref_mut())
+			.fetch_all(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn set_tags(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		new_tags: &[String],
 	) -> Result<(), DatabaseError> {
 		sqlx::query!("DELETE FROM junction_tag WHERE relay_id = ?", self.id)
-			.execute(tx.deref_mut())
+			.execute(conn.deref_mut())
 			.await?;
 
 		for new_tag in new_tags {
-			let tag: DbTag = DbTag::get_by_tag_or_create(tx, new_tag).await?;
-			DbJunctionTag::link_relay(tx, &tag, self).await?;
+			let tag: DbTag = DbTag::get_by_tag_or_create(conn, new_tag).await?;
+			DbJunctionTag::link_relay(conn, &tag, self).await?;
 		}
 		Ok(())
 	}
 
 	pub async fn reload(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<DbRelay, DatabaseError> {
-		Self::get(tx, self.id)
+		Self::get(conn, self.id)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
 
 	pub async fn get_active_schedule(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<DbSchedule, DatabaseError> {
 		let weekday = utils::get_weekday();
-		DbJunctionRelaySchedule::get_schedule(tx, self, weekday as Weekday)
+		DbJunctionRelaySchedule::get_schedule(conn, self, weekday as Weekday)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
diff --git a/src/db/schedules.rs b/src/db/schedules.rs
index 334bc11..6a792fd 100644
--- a/src/db/schedules.rs
+++ b/src/db/schedules.rs
@@ -3,7 +3,7 @@ use std::ops::DerefMut;
 
 use chrono::NaiveTime;
 use serde_derive::{Deserialize, Serialize};
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::db::model_utils::Period;
@@ -26,26 +26,26 @@ pub struct DbPeriods(pub Vec<Period>);
 
 impl DbSchedule {
 	pub async fn get_all(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<Vec<DbSchedule>, DatabaseError> {
 		sqlx::query_as!(DbSchedule, "SELECT * FROM schedules")
-			.fetch_all(tx.deref_mut())
+			.fetch_all(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		id: i64,
 	) -> Result<Option<DbSchedule>, DatabaseError> {
 		sqlx::query_as!(DbSchedule, "SELECT * FROM schedules WHERE id = ?", id)
-			.fetch_optional(tx.deref_mut())
+			.fetch_optional(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_uid(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		filter_uid: &ScheduleUid,
 	) -> Result<Option<DbSchedule>, DatabaseError> {
 		sqlx::query_as!(
@@ -53,23 +53,23 @@ impl DbSchedule {
 			"SELECT * FROM schedules WHERE uid = ?",
 			filter_uid
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await
 		.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_tag(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		tag: &DbTag,
 	) -> Result<Vec<DbSchedule>, DatabaseError> {
 		sqlx::query_as!(DbSchedule, "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(tx.deref_mut())
+			.fetch_all(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn delete_by_uid(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		filter_uid: ScheduleUid,
 	) -> Result<(), DatabaseError> {
 		let filter_uid = match filter_uid {
@@ -79,7 +79,7 @@ impl DbSchedule {
 		}?;
 
 		if sqlx::query_scalar!("SELECT 1 FROM schedules WHERE uid = ?", filter_uid)
-			.fetch_optional(tx.deref_mut())
+			.fetch_optional(conn.deref_mut())
 			.await?
 			.is_none()
 		{
@@ -87,7 +87,7 @@ impl DbSchedule {
 		}
 
 		sqlx::query!("DELETE FROM schedules WHERE uid = ?", filter_uid)
-			.execute(tx.deref_mut())
+			.execute(conn.deref_mut())
 			.await
 			.map(|res| match res.rows_affected() {
 				0 => Err(DatabaseError::DeleteError),
@@ -96,7 +96,7 @@ impl DbSchedule {
 	}
 
 	pub async fn create(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		new_uid: ScheduleUid,
 		new_name: &str,
 		new_periods: &DbPeriods,
@@ -108,45 +108,45 @@ impl DbSchedule {
 			new_name,
 			new_periods,
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
 
 	pub async fn get_by_uid_or_create(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		uid: ScheduleUid,
 		name: &str,
 		periods: &DbPeriods,
 	) -> Result<(DbSchedule, bool), DatabaseError> {
-		match DbSchedule::get_by_uid(tx, &uid).await? {
+		match DbSchedule::get_by_uid(conn, &uid).await? {
 			Some(schedule) => Ok((schedule, false)),
 			None => {
-				let schedule = DbSchedule::create(tx, uid, name, periods).await?;
+				let schedule = DbSchedule::create(conn, uid, name, periods).await?;
 				Ok((schedule, true))
 			}
 		}
 	}
 
-	pub async fn get_on(tx: &mut Transaction<'_, Sqlite>) -> Result<DbSchedule, DatabaseError> {
-		if let Some(schedule) = DbSchedule::get_by_uid(tx, &ScheduleUid::On).await? {
+	pub async fn get_on(conn: &mut PoolConnection<Sqlite>) -> Result<DbSchedule, DatabaseError> {
+		if let Some(schedule) = DbSchedule::get_by_uid(conn, &ScheduleUid::On).await? {
 			return Ok(schedule);
 		}
 		let periods = DbPeriods(vec![Period::new_on()]);
-		Self::create(tx, ScheduleUid::On, "On", &periods).await
+		Self::create(conn, ScheduleUid::On, "On", &periods).await
 	}
 
-	pub async fn get_off(tx: &mut Transaction<'_, Sqlite>) -> Result<DbSchedule, DatabaseError> {
-		if let Some(schedule) = DbSchedule::get_by_uid(tx, &ScheduleUid::Off).await? {
+	pub async fn get_off(conn: &mut PoolConnection<Sqlite>) -> Result<DbSchedule, DatabaseError> {
+		if let Some(schedule) = DbSchedule::get_by_uid(conn, &ScheduleUid::Off).await? {
 			return Ok(schedule);
 		}
 		let periods = DbPeriods(vec![]);
-		Self::create(tx, ScheduleUid::Off, "Off", &periods).await
+		Self::create(conn, ScheduleUid::Off, "Off", &periods).await
 	}
 
 	pub async fn update(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		new_name: &str,
 		new_periods: &DbPeriods,
 	) -> Result<DbSchedule, DatabaseError> {
@@ -162,35 +162,35 @@ impl DbSchedule {
 			new_periods,
 			self.id,
 		)
-		.execute(tx.deref_mut())
+		.execute(conn.deref_mut())
 		.await?;
 
-		DbSchedule::get(tx, self.id)
+		DbSchedule::get(conn, self.id)
 			.await?
 			.ok_or(DatabaseError::UpdateGetError)
 	}
 
 	pub async fn get_tags(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<Vec<String>, 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(tx.deref_mut())
+			.fetch_all(conn.deref_mut())
 			.await?)
 	}
 
 	pub async fn set_tags(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		new_tags: &[String],
 	) -> Result<(), DatabaseError> {
 		sqlx::query!("DELETE FROM junction_tag WHERE schedule_id = ?", self.id)
-			.execute(tx.deref_mut())
+			.execute(conn.deref_mut())
 			.await?;
 
 		for new_tag in new_tags {
-			let tag: DbTag = DbTag::get_by_tag_or_create(tx, new_tag).await?;
-			DbJunctionTag::link_schedule(tx, &tag, self).await?;
+			let tag: DbTag = DbTag::get_by_tag_or_create(conn, new_tag).await?;
+			DbJunctionTag::link_schedule(conn, &tag, self).await?;
 		}
 		Ok(())
 	}
diff --git a/src/db/tag.rs b/src/db/tag.rs
index 7093068..9c15f79 100644
--- a/src/db/tag.rs
+++ b/src/db/tag.rs
@@ -1,7 +1,7 @@
 use std::ops::DerefMut;
 
 use serde_derive::Serialize;
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::errors::DatabaseError;
@@ -14,7 +14,7 @@ pub struct DbTag {
 
 impl DbTag {
 	pub async fn create(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		new_tag: &str,
 	) -> Result<DbTag, DatabaseError> {
 		if new_tag.is_empty() {
@@ -26,54 +26,54 @@ impl DbTag {
 			"INSERT INTO tags (tag) VALUES (?) RETURNING *",
 			new_tag
 		)
-		.fetch_optional(tx.deref_mut())
+		.fetch_optional(conn.deref_mut())
 		.await?
 		.ok_or(DatabaseError::InsertGetError)
 	}
 
-	pub async fn get_all(tx: &mut Transaction<'_, Sqlite>) -> Result<Vec<DbTag>, DatabaseError> {
+	pub async fn get_all(conn: &mut PoolConnection<Sqlite>) -> Result<Vec<DbTag>, DatabaseError> {
 		sqlx::query_as!(DbTag, "SELECT * FROM tags")
-			.fetch_all(tx.deref_mut())
+			.fetch_all(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		id: i64,
 	) -> Result<Option<DbTag>, DatabaseError> {
 		sqlx::query_as!(DbTag, "SELECT * FROM tags WHERE id = ?", id)
-			.fetch_optional(tx.deref_mut())
+			.fetch_optional(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn get_by_tag_or_create(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		target_tag: &str,
 	) -> Result<DbTag, DatabaseError> {
-		match DbTag::get_by_tag(tx, target_tag).await? {
+		match DbTag::get_by_tag(conn, target_tag).await? {
 			Some(tag) => Ok(tag),
-			None => DbTag::create(tx, target_tag).await,
+			None => DbTag::create(conn, target_tag).await,
 		}
 	}
 
 	pub async fn get_by_tag(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		target_tag: &str,
 	) -> Result<Option<DbTag>, DatabaseError> {
 		sqlx::query_as!(DbTag, "SELECT * FROM tags WHERE tag = ?", target_tag)
-			.fetch_optional(tx.deref_mut())
+			.fetch_optional(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
 	}
 
 	pub async fn delete_by_tag(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		filter_tag: &str,
 	) -> Result<(), DatabaseError> {
 		if sqlx::query_scalar!("SELECT 1 FROM tags WHERE tag = ?", filter_tag)
-			.fetch_optional(tx.deref_mut())
+			.fetch_optional(conn.deref_mut())
 			.await?
 			.is_none()
 		{
@@ -81,7 +81,7 @@ impl DbTag {
 		}
 
 		sqlx::query!("DELETE FROM tags WHERE tag = ?", filter_tag)
-			.execute(tx.deref_mut())
+			.execute(conn.deref_mut())
 			.await
 			.map(|res| match res.rows_affected() {
 				0 => Err(DatabaseError::DeleteError),
diff --git a/src/models/controller.rs b/src/models/controller.rs
index 621a966..96d7b00 100644
--- a/src/models/controller.rs
+++ b/src/models/controller.rs
@@ -4,7 +4,7 @@ use actix::MessageResponse;
 use chrono::NaiveTime;
 use futures::executor::block_on;
 use serde_derive::{Deserialize, Serialize};
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::db::DbController;
@@ -24,16 +24,16 @@ impl FromDbModel for Controller {
 	type DbModelCache = Vec<Relay>;
 
 	fn from_db_model(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError> {
-		let relays_db = block_on(db_model.get_relays(tx))?;
-		let cache = convert_db_list_cache(tx, relays_db, db_model.clone())?;
-		Self::from_db_model_cache(tx, db_model, cache)
+		let relays_db = block_on(db_model.get_relays(conn))?;
+		let cache = convert_db_list_cache(conn, relays_db, db_model.clone())?;
+		Self::from_db_model_cache(conn, db_model, cache)
 	}
 
 	fn from_db_model_cache(
-		_tx: &mut Transaction<'_, Sqlite>,
+		_conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 		cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError> {
@@ -45,10 +45,10 @@ impl FromDbModel for Controller {
 }
 
 impl Controller {
-	pub fn reload(&mut self, tx: &mut Transaction<'_, Sqlite>) -> Result<(), EmgauwaError> {
-		self.c = block_on(self.c.reload(tx))?;
+	pub fn reload(&mut self, conn: &mut PoolConnection<Sqlite>) -> Result<(), EmgauwaError> {
+		self.c = block_on(self.c.reload(conn))?;
 		for relay in &mut self.relays {
-			relay.reload(tx)?;
+			relay.reload(conn)?;
 		}
 		Ok(())
 	}
diff --git a/src/models/macro.rs b/src/models/macro.rs
index 1ee8f1e..0598c0c 100644
--- a/src/models/macro.rs
+++ b/src/models/macro.rs
@@ -1,6 +1,6 @@
 use futures::executor::block_on;
 use serde_derive::{Deserialize, Serialize};
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::db::DbMacro;
@@ -19,19 +19,19 @@ impl FromDbModel for Macro {
 	type DbModelCache = ();
 
 	fn from_db_model(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError> {
-		Self::from_db_model_cache(tx, db_model, ())
+		Self::from_db_model_cache(conn, db_model, ())
 	}
 
 	fn from_db_model_cache(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 		_cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError> {
-		let actions_db = block_on(db_model.get_actions(tx))?;
-		let actions: Vec<MacroAction> = convert_db_list(tx, actions_db)?;
+		let actions_db = block_on(db_model.get_actions(conn))?;
+		let actions: Vec<MacroAction> = convert_db_list(conn, actions_db)?;
 
 		Ok(Macro {
 			m: db_model,
diff --git a/src/models/macro_action.rs b/src/models/macro_action.rs
index 7b12428..a36a45a 100644
--- a/src/models/macro_action.rs
+++ b/src/models/macro_action.rs
@@ -1,6 +1,6 @@
 use futures::executor::block_on;
 use serde_derive::{Deserialize, Serialize};
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::db::{DbJunctionRelaySchedule, DbMacroAction};
@@ -19,22 +19,22 @@ impl FromDbModel for MacroAction {
 	type DbModelCache = ();
 
 	fn from_db_model(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError> {
-		Self::from_db_model_cache(tx, db_model, ())
+		Self::from_db_model_cache(conn, db_model, ())
 	}
 
 	fn from_db_model_cache(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 		_cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError> {
-		let schedule_db = block_on(db_model.get_schedule(tx))?;
-		let schedule = Schedule::from_db_model(tx, schedule_db)?;
+		let schedule_db = block_on(db_model.get_schedule(conn))?;
+		let schedule = Schedule::from_db_model(conn, schedule_db)?;
 
-		let relay_db = block_on(db_model.get_relay(tx))?;
-		let relay = Relay::from_db_model(tx, relay_db)?;
+		let relay_db = block_on(db_model.get_relay(conn))?;
+		let relay = Relay::from_db_model(conn, relay_db)?;
 
 		let weekday = db_model.weekday;
 
@@ -47,8 +47,8 @@ impl FromDbModel for MacroAction {
 }
 
 impl MacroAction {
-	pub async fn execute(&self, tx: &mut Transaction<'_, Sqlite>) -> Result<(), EmgauwaError> {
-		DbJunctionRelaySchedule::set_schedule(tx, &self.relay.r, &self.schedule.s, self.weekday)
+	pub async fn execute(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), EmgauwaError> {
+		DbJunctionRelaySchedule::set_schedule(conn, &self.relay.r, &self.schedule.s, self.weekday)
 			.await?;
 		Ok(())
 	}
diff --git a/src/models/mod.rs b/src/models/mod.rs
index 58a40b9..570bf48 100644
--- a/src/models/mod.rs
+++ b/src/models/mod.rs
@@ -10,7 +10,7 @@ pub use macro_action::MacroAction;
 pub use r#macro::Macro;
 pub use relay::Relay;
 pub use schedule::Schedule;
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 pub use tag::Tag;
 
@@ -21,14 +21,14 @@ pub trait FromDbModel {
 	type DbModelCache: Clone;
 
 	fn from_db_model(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError>
 	where
 		Self: Sized;
 
 	fn from_db_model_cache(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 		cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError>
@@ -37,15 +37,15 @@ pub trait FromDbModel {
 }
 
 fn convert_db_list_generic<T: FromDbModel>(
-	tx: &mut Transaction<'_, Sqlite>,
+	conn: &mut PoolConnection<Sqlite>,
 	db_models: Vec<T::DbModel>,
 	cache: Option<T::DbModelCache>,
 ) -> Result<Vec<T>, DatabaseError> {
 	let mut result: Vec<T> = Vec::new();
 	for db_model in db_models {
 		let new = match &cache {
-			Some(c) => T::from_db_model_cache(tx, db_model, c.clone()),
-			None => T::from_db_model(tx, db_model),
+			Some(c) => T::from_db_model_cache(conn, db_model, c.clone()),
+			None => T::from_db_model(conn, db_model),
 		}?;
 		result.push(new);
 	}
@@ -53,16 +53,16 @@ fn convert_db_list_generic<T: FromDbModel>(
 }
 
 pub fn convert_db_list<T: FromDbModel>(
-	tx: &mut Transaction<'_, Sqlite>,
+	conn: &mut PoolConnection<Sqlite>,
 	db_models: Vec<T::DbModel>,
 ) -> Result<Vec<T>, DatabaseError> {
-	convert_db_list_generic(tx, db_models, None)
+	convert_db_list_generic(conn, db_models, None)
 }
 
 pub fn convert_db_list_cache<T: FromDbModel>(
-	tx: &mut Transaction<'_, Sqlite>,
+	conn: &mut PoolConnection<Sqlite>,
 	db_models: Vec<T::DbModel>,
 	cache: T::DbModelCache,
 ) -> Result<Vec<T>, DatabaseError> {
-	convert_db_list_generic(tx, db_models, Some(cache))
+	convert_db_list_generic(conn, db_models, Some(cache))
 }
diff --git a/src/models/relay.rs b/src/models/relay.rs
index 20c214d..e6ecd0f 100644
--- a/src/models/relay.rs
+++ b/src/models/relay.rs
@@ -3,7 +3,7 @@ use std::time::Instant;
 use chrono::NaiveTime;
 use futures::executor::block_on;
 use serde_derive::{Deserialize, Serialize};
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule};
@@ -32,23 +32,23 @@ impl FromDbModel for Relay {
 	type DbModelCache = DbController;
 
 	fn from_db_model(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError> {
-		let cache = block_on(db_model.get_controller(tx))?;
-		Self::from_db_model_cache(tx, db_model, cache)
+		let cache = block_on(db_model.get_controller(conn))?;
+		Self::from_db_model_cache(conn, db_model, cache)
 	}
 
 	fn from_db_model_cache(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 		cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError> {
-		let tags = block_on(db_model.get_tags(tx))?;
+		let tags = block_on(db_model.get_tags(conn))?;
 		let controller_id = cache.uid.clone();
 
-		let schedules = block_on(DbJunctionRelaySchedule::get_schedules(tx, &db_model))?;
-		let active_schedule = block_on(db_model.get_active_schedule(tx))?;
+		let schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &db_model))?;
+		let active_schedule = block_on(db_model.get_active_schedule(conn))?;
 
 		let is_on = None;
 
@@ -66,19 +66,19 @@ impl FromDbModel for Relay {
 }
 
 impl Relay {
-	pub fn reload(&mut self, tx: &mut Transaction<'_, Sqlite>) -> Result<(), DatabaseError> {
-		self.r = block_on(self.r.reload(tx))?;
-		self.schedules = block_on(DbJunctionRelaySchedule::get_schedules(tx, &self.r))?;
-		self.reload_active_schedule(tx)?;
+	pub fn reload(&mut self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
+		self.r = block_on(self.r.reload(conn))?;
+		self.schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &self.r))?;
+		self.reload_active_schedule(conn)?;
 
 		Ok(())
 	}
 
 	pub fn reload_active_schedule(
 		&mut self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<(), DatabaseError> {
-		self.active_schedule = block_on(self.r.get_active_schedule(tx))?;
+		self.active_schedule = block_on(self.r.get_active_schedule(conn))?;
 		Ok(())
 	}
 
diff --git a/src/models/schedule.rs b/src/models/schedule.rs
index 7a7903f..9bf07b4 100644
--- a/src/models/schedule.rs
+++ b/src/models/schedule.rs
@@ -1,6 +1,6 @@
 use futures::executor::block_on;
 use serde_derive::{Deserialize, Serialize};
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::db::DbSchedule;
@@ -19,15 +19,15 @@ impl FromDbModel for Schedule {
 	type DbModelCache = Vec<String>;
 
 	fn from_db_model(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError> {
-		let cache = block_on(db_model.get_tags(tx))?;
-		Self::from_db_model_cache(tx, db_model, cache)
+		let cache = block_on(db_model.get_tags(conn))?;
+		Self::from_db_model_cache(conn, db_model, cache)
 	}
 
 	fn from_db_model_cache(
-		_tx: &mut Transaction<'_, Sqlite>,
+		_conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 		cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError> {
diff --git a/src/models/tag.rs b/src/models/tag.rs
index a5370dc..7e23fb4 100644
--- a/src/models/tag.rs
+++ b/src/models/tag.rs
@@ -1,7 +1,7 @@
 use actix::MessageResponse;
 use futures::executor::block_on;
 use serde_derive::{Deserialize, Serialize};
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::db::{DbRelay, DbSchedule, DbTag};
@@ -20,21 +20,21 @@ impl FromDbModel for Tag {
 	type DbModelCache = (Vec<Relay>, Vec<Schedule>);
 
 	fn from_db_model(
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError> {
-		let db_schedules = block_on(DbSchedule::get_by_tag(tx, &db_model))?;
-		let schedules: Vec<Schedule> = convert_db_list(tx, db_schedules)?;
+		let db_schedules = block_on(DbSchedule::get_by_tag(conn, &db_model))?;
+		let schedules: Vec<Schedule> = convert_db_list(conn, db_schedules)?;
 
-		let db_relays = block_on(DbRelay::get_by_tag(tx, &db_model))?;
-		let relays: Vec<Relay> = convert_db_list(tx, db_relays)?;
+		let db_relays = block_on(DbRelay::get_by_tag(conn, &db_model))?;
+		let relays: Vec<Relay> = convert_db_list(conn, db_relays)?;
 
 		let cache = (relays, schedules);
-		Self::from_db_model_cache(tx, db_model, cache)
+		Self::from_db_model_cache(conn, db_model, cache)
 	}
 
 	fn from_db_model_cache(
-		_tx: &mut Transaction<'_, Sqlite>,
+		_conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 		cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError> {
diff --git a/src/types/request.rs b/src/types/request.rs
index eb09abe..5c9a927 100644
--- a/src/types/request.rs
+++ b/src/types/request.rs
@@ -1,5 +1,5 @@
 use serde_derive::{Deserialize, Serialize};
-use sqlx::Transaction;
+use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
 use crate::db::{DbPeriods, DbSchedule};
@@ -86,9 +86,9 @@ pub struct RequestMacroExecute {
 impl RequestScheduleId {
 	pub async fn get_schedule(
 		&self,
-		tx: &mut Transaction<'_, Sqlite>,
+		conn: &mut PoolConnection<Sqlite>,
 	) -> Result<DbSchedule, DatabaseError> {
-		DbSchedule::get_by_uid(tx, &self.id)
+		DbSchedule::get_by_uid(conn, &self.id)
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}

From 98db89ce0357b36a4b9a604d7a43953a99e31aeb Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Thu, 2 May 2024 20:12:43 +0200
Subject: [PATCH 03/18] Fix is_on function for Periods

---
 src/db/model_utils.rs | 28 ++++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/src/db/model_utils.rs b/src/db/model_utils.rs
index 4116365..e178d8e 100644
--- a/src/db/model_utils.rs
+++ b/src/db/model_utils.rs
@@ -1,10 +1,10 @@
 use chrono::{NaiveTime, Timelike};
 use serde::{Deserialize, Serialize};
+use sqlx::{Decode, Encode, Sqlite, Type};
 use sqlx::database::HasArguments;
 use sqlx::encode::IsNull;
 use sqlx::error::BoxDynError;
 use sqlx::sqlite::{SqliteTypeInfo, SqliteValueRef};
-use sqlx::{Decode, Encode, Sqlite, Type};
 
 use crate::db::DbPeriods;
 
@@ -51,13 +51,33 @@ impl Period {
 		}
 	}
 
+	pub fn is_always_on(&self) -> bool {
+		self.start.eq(&self.end)
+	}
+
 	pub fn is_on(&self, now: &NaiveTime) -> bool {
-		self.start.eq(&self.end) || (self.start.le(now) && self.end.gt(now))
+		if self.is_always_on() {
+			return true;
+		}
+
+		let start_after_now = self.start.gt(now);
+		// add check for end time being 00:00 because end being 00:00 would cause end_after_now to always be false
+		// this will handle end like 24:00 and end_after_now will be true
+		let end_after_now = self.end.gt(now) || self.end.eq(&NaiveTime::MIN);
+		let start_before_end = self.start.lt(&self.end);
+
+		match (start_after_now, end_after_now, start_before_end) {
+			(false, false, true) => false,  // both before now; start before end means "normal" period before now
+			(false, false, false) => true,  // both before now; end before start means "inversed" period around now
+			(true, false, _) => false,   // only start after now
+			(false, true, _) => true,    // only end after now
+			(true, true, true) => false, // both after now but start first
+			(true, true, false) => true, // both after now but end first
+		}
 	}
 
 	pub fn get_next_time(&self, now: &NaiveTime) -> Option<NaiveTime> {
-		if self.start.eq(&self.end) {
-			// this period is always on
+		if self.is_always_on() {
 			return None;
 		}
 

From cacd740bd91f87186f51bd33734919a222320ff5 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Thu, 2 May 2024 21:14:28 +0200
Subject: [PATCH 04/18] Fix wrong table in macro update

---
 ...dcc4dc5cc79a52299cb74018e36212809288a.json |   12 +
 Cargo.lock                                    | 1106 +++++++----------
 Cargo.toml                                    |    2 +-
 Makefile                                      |    2 +
 src/db/macro.rs                               |    2 +-
 5 files changed, 440 insertions(+), 684 deletions(-)
 create mode 100644 .sqlx/query-2b34934e10005378c331f489751dcc4dc5cc79a52299cb74018e36212809288a.json

diff --git a/.sqlx/query-2b34934e10005378c331f489751dcc4dc5cc79a52299cb74018e36212809288a.json b/.sqlx/query-2b34934e10005378c331f489751dcc4dc5cc79a52299cb74018e36212809288a.json
new file mode 100644
index 0000000..66aaaa5
--- /dev/null
+++ b/.sqlx/query-2b34934e10005378c331f489751dcc4dc5cc79a52299cb74018e36212809288a.json
@@ -0,0 +1,12 @@
+{
+  "db_name": "SQLite",
+  "query": "UPDATE macros SET name = ? WHERE id = ?",
+  "describe": {
+    "columns": [],
+    "parameters": {
+      "Right": 2
+    },
+    "nullable": []
+  },
+  "hash": "2b34934e10005378c331f489751dcc4dc5cc79a52299cb74018e36212809288a"
+}
diff --git a/Cargo.lock b/Cargo.lock
index 3f48599..97554c4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,14 +4,14 @@ version = 3
 
 [[package]]
 name = "actix"
-version = "0.13.1"
+version = "0.13.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cba56612922b907719d4a01cf11c8d5b458e7d3dba946d0435f20f58d6795ed2"
+checksum = "fb72882332b6d6282f428b77ba0358cb2687e61a6f6df6a6d3871e8a177c2d4f"
 dependencies = [
  "actix-macros",
  "actix-rt",
  "actix_derive",
- "bitflags 2.4.1",
+ "bitflags 2.5.0",
  "bytes",
  "crossbeam-channel",
  "futures-core",
@@ -29,11 +29,11 @@ dependencies = [
 
 [[package]]
 name = "actix-codec"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8"
+checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.5.0",
  "bytes",
  "futures-core",
  "futures-sink",
@@ -44,34 +44,19 @@ dependencies = [
  "tracing",
 ]
 
-[[package]]
-name = "actix-cors"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9e772b3bcafe335042b5db010ab7c09013dad6eac4915c91d8d50902769f331"
-dependencies = [
- "actix-utils",
- "actix-web",
- "derive_more",
- "futures-util",
- "log",
- "once_cell",
- "smallvec",
-]
-
 [[package]]
 name = "actix-http"
-version = "3.4.0"
+version = "3.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92ef85799cba03f76e4f7c10f533e66d87c9a7e7055f3391f09000ad8351bc9"
+checksum = "d223b13fd481fc0d1f83bb12659ae774d9e3601814c68a0bc539731698cca743"
 dependencies = [
  "actix-codec",
  "actix-rt",
  "actix-service",
  "actix-utils",
- "ahash 0.8.6",
- "base64 0.21.5",
- "bitflags 2.4.1",
+ "ahash 0.8.11",
+ "base64 0.21.7",
+ "bitflags 2.5.0",
  "brotli",
  "bytes",
  "bytestring",
@@ -80,7 +65,7 @@ dependencies = [
  "flate2",
  "futures-core",
  "h2",
- "http 0.2.9",
+ "http",
  "httparse",
  "httpdate",
  "itoa",
@@ -105,17 +90,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
 dependencies = [
  "quote",
- "syn 2.0.38",
+ "syn 2.0.60",
 ]
 
 [[package]]
 name = "actix-router"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799"
+checksum = "d22475596539443685426b6bdadb926ad0ecaefdfc5fb05e5e3441f15463c511"
 dependencies = [
  "bytestring",
- "http 0.2.9",
+ "http",
  "regex",
  "serde",
  "tracing",
@@ -171,9 +156,9 @@ dependencies = [
 
 [[package]]
 name = "actix-web"
-version = "4.4.0"
+version = "4.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e4a5b5e29603ca8c94a77c65cf874718ceb60292c5a5c3e5f4ace041af462b9"
+checksum = "43a6556ddebb638c2358714d853257ed226ece6023ef9364f23f0c70737ea984"
 dependencies = [
  "actix-codec",
  "actix-http",
@@ -184,7 +169,7 @@ dependencies = [
  "actix-service",
  "actix-utils",
  "actix-web-codegen",
- "ahash 0.8.6",
+ "ahash 0.8.11",
  "bytes",
  "bytestring",
  "cfg-if",
@@ -211,9 +196,9 @@ dependencies = [
 
 [[package]]
 name = "actix-web-actors"
-version = "4.2.0"
+version = "4.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf6e9ccc371cfddbed7aa842256a4abc7a6dcac9f3fce392fe1d0f68cfd136b2"
+checksum = "420b001bb709d8510c3e2659dae046e54509ff9528018d09c78381e765a1f9fa"
 dependencies = [
  "actix",
  "actix-codec",
@@ -236,7 +221,7 @@ dependencies = [
  "actix-router",
  "proc-macro2",
  "quote",
- "syn 2.0.38",
+ "syn 2.0.60",
 ]
 
 [[package]]
@@ -247,7 +232,7 @@ checksum = "7c7db3d5a9718568e4cf4a537cfd7070e6e6ff7481510d0237fb529ac850f6d3"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.38",
+ "syn 2.0.60",
 ]
 
 [[package]]
@@ -267,9 +252,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
 [[package]]
 name = "ahash"
-version = "0.7.7"
+version = "0.7.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd"
+checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
 dependencies = [
  "getrandom",
  "once_cell",
@@ -278,9 +263,9 @@ dependencies = [
 
 [[package]]
 name = "ahash"
-version = "0.8.6"
+version = "0.8.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
+checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
 dependencies = [
  "cfg-if",
  "getrandom",
@@ -291,9 +276,9 @@ dependencies = [
 
 [[package]]
 name = "aho-corasick"
-version = "1.1.2"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
 dependencies = [
  "memchr",
 ]
@@ -315,9 +300,9 @@ dependencies = [
 
 [[package]]
 name = "allocator-api2"
-version = "0.2.16"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
+checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
 
 [[package]]
 name = "android-tzdata"
@@ -336,13 +321,13 @@ dependencies = [
 
 [[package]]
 name = "async-trait"
-version = "0.1.74"
+version = "0.1.80"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
+checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.38",
+ "syn 2.0.60",
 ]
 
 [[package]]
@@ -356,15 +341,15 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.1.0"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
 
 [[package]]
 name = "backtrace"
-version = "0.3.69"
+version = "0.3.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
 dependencies = [
  "addr2line",
  "cc",
@@ -383,9 +368,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 
 [[package]]
 name = "base64"
-version = "0.21.5"
+version = "0.21.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
 
 [[package]]
 name = "base64ct"
@@ -401,9 +386,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bitflags"
-version = "2.4.1"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
 dependencies = [
  "serde",
 ]
@@ -419,9 +404,9 @@ dependencies = [
 
 [[package]]
 name = "brotli"
-version = "3.4.0"
+version = "3.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f"
+checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391"
 dependencies = [
  "alloc-no-stdlib",
  "alloc-stdlib",
@@ -440,9 +425,9 @@ dependencies = [
 
 [[package]]
 name = "bumpalo"
-version = "3.14.0"
+version = "3.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
 
 [[package]]
 name = "byteorder"
@@ -452,9 +437,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "bytes"
-version = "1.5.0"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
 
 [[package]]
 name = "bytestring"
@@ -467,12 +452,13 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.0.83"
+version = "1.0.96"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd"
 dependencies = [
  "jobserver",
  "libc",
+ "once_cell",
 ]
 
 [[package]]
@@ -483,9 +469,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chrono"
-version = "0.4.31"
+version = "0.4.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
+checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
@@ -493,18 +479,17 @@ dependencies = [
  "num-traits",
  "serde",
  "wasm-bindgen",
- "windows-targets",
+ "windows-targets 0.52.5",
 ]
 
 [[package]]
 name = "colored"
-version = "2.0.4"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6"
+checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
 dependencies = [
- "is-terminal",
  "lazy_static",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -528,9 +513,9 @@ dependencies = [
 
 [[package]]
 name = "const-oid"
-version = "0.9.5"
+version = "0.9.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
 
 [[package]]
 name = "convert_case"
@@ -551,24 +536,24 @@ dependencies = [
 
 [[package]]
 name = "core-foundation-sys"
-version = "0.8.4"
+version = "0.8.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.11"
+version = "0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "crc"
-version = "3.0.1"
+version = "3.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
+checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
 dependencies = [
  "crc-catalog",
 ]
@@ -581,41 +566,36 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
 
 [[package]]
 name = "crc32fast"
-version = "1.3.2"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
 dependencies = [
  "cfg-if",
 ]
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.5.8"
+version = "0.5.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
 dependencies = [
- "cfg-if",
  "crossbeam-utils",
 ]
 
 [[package]]
 name = "crossbeam-queue"
-version = "0.3.8"
+version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
+checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
 dependencies = [
- "cfg-if",
  "crossbeam-utils",
 ]
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.16"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
-dependencies = [
- "cfg-if",
-]
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
 
 [[package]]
 name = "crypto-common"
@@ -627,17 +607,11 @@ dependencies = [
  "typenum",
 ]
 
-[[package]]
-name = "data-encoding"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
-
 [[package]]
 name = "der"
-version = "0.7.8"
+version = "0.7.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
+checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
 dependencies = [
  "const-oid",
  "pem-rfc7468",
@@ -646,9 +620,9 @@ dependencies = [
 
 [[package]]
 name = "deranged"
-version = "0.3.9"
+version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
 dependencies = [
  "powerfmt",
 ]
@@ -692,9 +666,9 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
 
 [[package]]
 name = "either"
-version = "1.9.0"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
 dependencies = [
  "serde",
 ]
@@ -723,57 +697,11 @@ dependencies = [
  "uuid",
 ]
 
-[[package]]
-name = "emgauwa-controller"
-version = "0.5.0"
-dependencies = [
- "actix",
- "chrono",
- "emgauwa-common",
- "futures",
- "futures-channel",
- "log",
- "rppal 0.17.1",
- "rppal-mcp23s17",
- "rppal-pfd",
- "serde",
- "serde_derive",
- "serde_json",
- "simple_logger",
- "sqlx",
- "tokio",
- "tokio-tungstenite",
- "uuid",
-]
-
-[[package]]
-name = "emgauwa-core"
-version = "0.5.0"
-dependencies = [
- "actix",
- "actix-cors",
- "actix-web",
- "actix-web-actors",
- "chrono",
- "emgauwa-common",
- "futures",
- "itertools 0.12.1",
- "log",
- "serde",
- "serde_derive",
- "serde_json",
- "sqlx",
- "tokio",
- "utoipa",
- "utoipa-swagger-ui",
- "uuid",
-]
-
 [[package]]
 name = "encoding_rs"
-version = "0.8.33"
+version = "0.8.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
+checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
 dependencies = [
  "cfg-if",
 ]
@@ -786,12 +714,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
 [[package]]
 name = "errno"
-version = "0.3.5"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
 dependencies = [
  "libc",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -802,7 +730,7 @@ checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
 dependencies = [
  "cfg-if",
  "home",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -813,9 +741,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
 
 [[package]]
 name = "fastrand"
-version = "2.0.1"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
+checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
 
 [[package]]
 name = "finl_unicode"
@@ -825,9 +753,9 @@ checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6"
 
 [[package]]
 name = "flate2"
-version = "1.0.28"
+version = "1.0.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
+checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
 dependencies = [
  "crc32fast",
  "miniz_oxide",
@@ -852,18 +780,18 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
 name = "form_urlencoded"
-version = "1.2.0"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
 dependencies = [
  "percent-encoding",
 ]
 
 [[package]]
 name = "futures"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
+checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -876,9 +804,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -886,15 +814,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"
+checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -914,38 +842,38 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.38",
+ "syn 2.0.60",
 ]
 
 [[package]]
 name = "futures-sink"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
 
 [[package]]
 name = "futures-task"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
 
 [[package]]
 name = "futures-util"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -971,9 +899,9 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.10"
+version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
 dependencies = [
  "cfg-if",
  "libc",
@@ -982,23 +910,23 @@ dependencies = [
 
 [[package]]
 name = "gimli"
-version = "0.28.0"
+version = "0.28.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
 
 [[package]]
 name = "h2"
-version = "0.3.21"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833"
+checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
 dependencies = [
  "bytes",
  "fnv",
  "futures-core",
  "futures-sink",
  "futures-util",
- "http 0.2.9",
- "indexmap 1.9.3",
+ "http",
+ "indexmap",
  "slab",
  "tokio",
  "tokio-util",
@@ -1011,16 +939,16 @@ version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 dependencies = [
- "ahash 0.7.7",
+ "ahash 0.7.8",
 ]
 
 [[package]]
 name = "hashbrown"
-version = "0.14.2"
+version = "0.14.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
 dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.11",
  "allocator-api2",
 ]
 
@@ -1030,7 +958,7 @@ version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
 dependencies = [
- "hashbrown 0.14.2",
+ "hashbrown 0.14.5",
 ]
 
 [[package]]
@@ -1042,12 +970,6 @@ dependencies = [
  "unicode-segmentation",
 ]
 
-[[package]]
-name = "hermit-abi"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
-
 [[package]]
 name = "hex"
 version = "0.4.3"
@@ -1056,9 +978,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
 
 [[package]]
 name = "hkdf"
-version = "0.12.3"
+version = "0.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
+checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
 dependencies = [
  "hmac",
 ]
@@ -1074,29 +996,18 @@ dependencies = [
 
 [[package]]
 name = "home"
-version = "0.5.5"
+version = "0.5.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
+checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
 dependencies = [
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "http"
-version = "0.2.9"
+version = "0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
-dependencies = [
- "bytes",
- "fnv",
- "itoa",
-]
-
-[[package]]
-name = "http"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea"
+checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
 dependencies = [
  "bytes",
  "fnv",
@@ -1117,9 +1028,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
 
 [[package]]
 name = "iana-time-zone"
-version = "0.1.58"
+version = "0.1.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
 dependencies = [
  "android_system_properties",
  "core-foundation-sys",
@@ -1140,9 +1051,9 @@ dependencies = [
 
 [[package]]
 name = "idna"
-version = "0.4.0"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
 dependencies = [
  "unicode-bidi",
  "unicode-normalization",
@@ -1150,43 +1061,12 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "1.9.3"
+version = "2.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
-dependencies = [
- "autocfg",
- "hashbrown 0.12.3",
-]
-
-[[package]]
-name = "indexmap"
-version = "2.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
+checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
 dependencies = [
  "equivalent",
- "hashbrown 0.14.2",
- "serde",
-]
-
-[[package]]
-name = "is-terminal"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
-dependencies = [
- "hermit-abi",
- "rustix",
- "windows-sys",
-]
-
-[[package]]
-name = "itertools"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
-dependencies = [
- "either",
+ "hashbrown 0.14.5",
 ]
 
 [[package]]
@@ -1200,24 +1080,24 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "1.0.9"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
 
 [[package]]
 name = "jobserver"
-version = "0.1.27"
+version = "0.1.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
+checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "js-sys"
-version = "0.3.64"
+version = "0.3.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -1250,9 +1130,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.149"
+version = "0.2.154"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
+checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
 
 [[package]]
 name = "libm"
@@ -1262,9 +1142,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
 
 [[package]]
 name = "libsqlite3-sys"
-version = "0.26.0"
+version = "0.27.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326"
+checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716"
 dependencies = [
  "cc",
  "pkg-config",
@@ -1279,9 +1159,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.4.10"
+version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
 
 [[package]]
 name = "local-channel"
@@ -1302,9 +1182,9 @@ checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487"
 
 [[package]]
 name = "lock_api"
-version = "0.4.11"
+version = "0.4.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
 dependencies = [
  "autocfg",
  "scopeguard",
@@ -1312,9 +1192,9 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.20"
+version = "0.4.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
 
 [[package]]
 name = "md-5"
@@ -1328,9 +1208,9 @@ dependencies = [
 
 [[package]]
 name = "memchr"
-version = "2.6.4"
+version = "2.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
 
 [[package]]
 name = "mime"
@@ -1338,16 +1218,6 @@ version = "0.3.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
 
-[[package]]
-name = "mime_guess"
-version = "2.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
-dependencies = [
- "mime",
- "unicase",
-]
-
 [[package]]
 name = "minimal-lexical"
 version = "0.2.1"
@@ -1356,23 +1226,23 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.7.1"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
 dependencies = [
  "adler",
 ]
 
 [[package]]
 name = "mio"
-version = "0.8.9"
+version = "0.8.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
 dependencies = [
  "libc",
  "log",
  "wasi",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -1403,20 +1273,25 @@ dependencies = [
 ]
 
 [[package]]
-name = "num-integer"
-version = "0.1.45"
+name = "num-conv"
+version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
 dependencies = [
- "autocfg",
  "num-traits",
 ]
 
 [[package]]
 name = "num-iter"
-version = "0.1.43"
+version = "0.1.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
+checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9"
 dependencies = [
  "autocfg",
  "num-integer",
@@ -1425,47 +1300,37 @@ dependencies = [
 
 [[package]]
 name = "num-traits"
-version = "0.2.17"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
 dependencies = [
  "autocfg",
  "libm",
 ]
 
-[[package]]
-name = "num_cpus"
-version = "1.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
-dependencies = [
- "hermit-abi",
- "libc",
-]
-
 [[package]]
 name = "num_threads"
-version = "0.1.6"
+version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "object"
-version = "0.32.1"
+version = "0.32.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
+checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "once_cell"
-version = "1.18.0"
+version = "1.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
 [[package]]
 name = "ordered-multimap"
@@ -1479,9 +1344,9 @@ dependencies = [
 
 [[package]]
 name = "parking_lot"
-version = "0.12.1"
+version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
 dependencies = [
  "lock_api",
  "parking_lot_core",
@@ -1489,15 +1354,15 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.9.9"
+version = "0.9.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.5.1",
  "smallvec",
- "windows-targets",
+ "windows-targets 0.52.5",
 ]
 
 [[package]]
@@ -1523,15 +1388,15 @@ dependencies = [
 
 [[package]]
 name = "percent-encoding"
-version = "2.3.0"
+version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
 [[package]]
 name = "pest"
-version = "2.7.5"
+version = "2.7.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5"
+checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8"
 dependencies = [
  "memchr",
  "thiserror",
@@ -1540,9 +1405,9 @@ dependencies = [
 
 [[package]]
 name = "pest_derive"
-version = "2.7.5"
+version = "2.7.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2"
+checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459"
 dependencies = [
  "pest",
  "pest_generator",
@@ -1550,22 +1415,22 @@ dependencies = [
 
 [[package]]
 name = "pest_generator"
-version = "2.7.5"
+version = "2.7.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227"
+checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687"
 dependencies = [
  "pest",
  "pest_meta",
  "proc-macro2",
  "quote",
- "syn 2.0.38",
+ "syn 2.0.60",
 ]
 
 [[package]]
 name = "pest_meta"
-version = "2.7.5"
+version = "2.7.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6"
+checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd"
 dependencies = [
  "once_cell",
  "pest",
@@ -1574,9 +1439,9 @@ dependencies = [
 
 [[package]]
 name = "pin-project-lite"
-version = "0.2.13"
+version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
 
 [[package]]
 name = "pin-utils"
@@ -1607,9 +1472,9 @@ dependencies = [
 
 [[package]]
 name = "pkg-config"
-version = "0.3.27"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
 
 [[package]]
 name = "powerfmt"
@@ -1623,44 +1488,20 @@ version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
-[[package]]
-name = "proc-macro-error"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
-dependencies = [
- "proc-macro-error-attr",
- "proc-macro2",
- "quote",
- "syn 1.0.109",
- "version_check",
-]
-
-[[package]]
-name = "proc-macro-error-attr"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
-dependencies = [
- "proc-macro2",
- "quote",
- "version_check",
-]
-
 [[package]]
 name = "proc-macro2"
-version = "1.0.69"
+version = "1.0.81"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.33"
+version = "1.0.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
 dependencies = [
  "proc-macro2",
 ]
@@ -1705,10 +1546,19 @@ dependencies = [
 ]
 
 [[package]]
-name = "regex"
-version = "1.10.2"
+name = "redox_syscall"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1718,9 +1568,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.3"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1729,9 +1579,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.8.2"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
 
 [[package]]
 name = "ron"
@@ -1788,9 +1638,9 @@ dependencies = [
 
 [[package]]
 name = "rsa"
-version = "0.9.3"
+version = "0.9.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86ef35bf3e7fe15a53c4ab08a998e42271eab13eb0db224126bc7bc4c4bad96d"
+checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc"
 dependencies = [
  "const-oid",
  "digest",
@@ -1806,40 +1656,6 @@ dependencies = [
  "zeroize",
 ]
 
-[[package]]
-name = "rust-embed"
-version = "8.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb78f46d0066053d16d4ca7b898e9343bc3530f71c61d5ad84cd404ada068745"
-dependencies = [
- "rust-embed-impl",
- "rust-embed-utils",
- "walkdir",
-]
-
-[[package]]
-name = "rust-embed-impl"
-version = "8.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b91ac2a3c6c0520a3fb3dd89321177c3c692937c4eb21893378219da10c44fc8"
-dependencies = [
- "proc-macro2",
- "quote",
- "rust-embed-utils",
- "syn 2.0.38",
- "walkdir",
-]
-
-[[package]]
-name = "rust-embed-utils"
-version = "8.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86f69089032567ffff4eada41c573fc43ff466c7db7c5688b2e7969584345581"
-dependencies = [
- "sha2",
- "walkdir",
-]
-
 [[package]]
 name = "rust-ini"
 version = "0.18.0"
@@ -1867,31 +1683,22 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.38.21"
+version = "0.38.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.5.0",
  "errno",
  "libc",
  "linux-raw-sys",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "ryu"
-version = "1.0.15"
+version = "1.0.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
-
-[[package]]
-name = "same-file"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
-dependencies = [
- "winapi-util",
-]
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
 
 [[package]]
 name = "scopeguard"
@@ -1901,35 +1708,35 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
 [[package]]
 name = "semver"
-version = "1.0.20"
+version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
+checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
 
 [[package]]
 name = "serde"
-version = "1.0.190"
+version = "1.0.200"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
+checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.190"
+version = "1.0.200"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
+checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.38",
+ "syn 2.0.60",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.108"
+version = "1.0.116"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
 dependencies = [
  "itoa",
  "ryu",
@@ -1972,9 +1779,9 @@ dependencies = [
 
 [[package]]
 name = "signal-hook-registry"
-version = "1.4.1"
+version = "1.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
 dependencies = [
  "libc",
 ]
@@ -1998,7 +1805,7 @@ dependencies = [
  "colored",
  "log",
  "time",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -2012,18 +1819,18 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
-version = "1.11.1"
+version = "1.13.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
 
 [[package]]
 name = "socket2"
-version = "0.5.5"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
+checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
 dependencies = [
  "libc",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -2043,9 +1850,9 @@ dependencies = [
 
 [[package]]
 name = "spki"
-version = "0.7.2"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
 dependencies = [
  "base64ct",
  "der",
@@ -2053,20 +1860,20 @@ dependencies = [
 
 [[package]]
 name = "sqlformat"
-version = "0.2.2"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85"
+checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c"
 dependencies = [
- "itertools 0.11.0",
+ "itertools",
  "nom",
  "unicode_categories",
 ]
 
 [[package]]
 name = "sqlx"
-version = "0.7.2"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e50c216e3624ec8e7ecd14c6a6a6370aad6ee5d8cfc3ab30b5162eeeef2ed33"
+checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa"
 dependencies = [
  "sqlx-core",
  "sqlx-macros",
@@ -2077,18 +1884,17 @@ dependencies = [
 
 [[package]]
 name = "sqlx-core"
-version = "0.7.2"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d6753e460c998bbd4cd8c6f0ed9a64346fcca0723d6e75e52fdc351c5d2169d"
+checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6"
 dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.11",
  "atoi",
  "byteorder",
  "bytes",
  "chrono",
  "crc",
  "crossbeam-queue",
- "dotenvy",
  "either",
  "event-listener",
  "futures-channel",
@@ -2098,7 +1904,7 @@ dependencies = [
  "futures-util",
  "hashlink",
  "hex",
- "indexmap 2.0.2",
+ "indexmap",
  "log",
  "memchr",
  "once_cell",
@@ -2118,9 +1924,9 @@ dependencies = [
 
 [[package]]
 name = "sqlx-macros"
-version = "0.7.2"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a793bb3ba331ec8359c1853bd39eed32cdd7baaf22c35ccf5c92a7e8d1189ec"
+checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2131,9 +1937,9 @@ dependencies = [
 
 [[package]]
 name = "sqlx-macros-core"
-version = "0.7.2"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a4ee1e104e00dedb6aa5ffdd1343107b0a4702e862a84320ee7cc74782d96fc"
+checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8"
 dependencies = [
  "dotenvy",
  "either",
@@ -2157,13 +1963,13 @@ dependencies = [
 
 [[package]]
 name = "sqlx-mysql"
-version = "0.7.2"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db"
+checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
 dependencies = [
  "atoi",
- "base64 0.21.5",
- "bitflags 2.4.1",
+ "base64 0.21.7",
+ "bitflags 2.5.0",
  "byteorder",
  "bytes",
  "chrono",
@@ -2200,13 +2006,13 @@ dependencies = [
 
 [[package]]
 name = "sqlx-postgres"
-version = "0.7.2"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624"
+checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
 dependencies = [
  "atoi",
- "base64 0.21.5",
- "bitflags 2.4.1",
+ "base64 0.21.7",
+ "bitflags 2.5.0",
  "byteorder",
  "chrono",
  "crc",
@@ -2228,7 +2034,6 @@ dependencies = [
  "rand",
  "serde",
  "serde_json",
- "sha1",
  "sha2",
  "smallvec",
  "sqlx-core",
@@ -2240,9 +2045,9 @@ dependencies = [
 
 [[package]]
 name = "sqlx-sqlite"
-version = "0.7.2"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d59dc83cf45d89c555a577694534fcd1b55c545a816c816ce51f20bbe56a4f3f"
+checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa"
 dependencies = [
  "atoi",
  "chrono",
@@ -2259,6 +2064,7 @@ dependencies = [
  "sqlx-core",
  "tracing",
  "url",
+ "urlencoding",
 ]
 
 [[package]]
@@ -2291,9 +2097,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.38"
+version = "2.0.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
+checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2302,46 +2108,46 @@ dependencies = [
 
 [[package]]
 name = "tempfile"
-version = "3.8.1"
+version = "3.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
 dependencies = [
  "cfg-if",
  "fastrand",
- "redox_syscall",
  "rustix",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "thiserror"
-version = "1.0.50"
+version = "1.0.59"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
+checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.50"
+version = "1.0.59"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
+checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.38",
+ "syn 2.0.60",
 ]
 
 [[package]]
 name = "time"
-version = "0.3.30"
+version = "0.3.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
 dependencies = [
  "deranged",
  "itoa",
  "libc",
+ "num-conv",
  "num_threads",
  "powerfmt",
  "serde",
@@ -2357,10 +2163,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
 [[package]]
 name = "time-macros"
-version = "0.2.15"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
 dependencies = [
+ "num-conv",
  "time-core",
 ]
 
@@ -2381,57 +2188,32 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "tokio"
-version = "1.36.0"
+version = "1.37.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
+checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
 dependencies = [
  "backtrace",
  "bytes",
  "libc",
  "mio",
- "num_cpus",
  "parking_lot",
  "pin-project-lite",
  "signal-hook-registry",
  "socket2",
- "tokio-macros",
- "windows-sys",
-]
-
-[[package]]
-name = "tokio-macros"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.38",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
 name = "tokio-stream"
-version = "0.1.14"
+version = "0.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
 dependencies = [
  "futures-core",
  "pin-project-lite",
  "tokio",
 ]
 
-[[package]]
-name = "tokio-tungstenite"
-version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38"
-dependencies = [
- "futures-util",
- "log",
- "tokio",
- "tungstenite",
-]
-
 [[package]]
 name = "tokio-util"
 version = "0.7.10"
@@ -2475,7 +2257,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.38",
+ "syn 2.0.60",
 ]
 
 [[package]]
@@ -2487,25 +2269,6 @@ dependencies = [
  "once_cell",
 ]
 
-[[package]]
-name = "tungstenite"
-version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1"
-dependencies = [
- "byteorder",
- "bytes",
- "data-encoding",
- "http 1.0.0",
- "httparse",
- "log",
- "rand",
- "sha1",
- "thiserror",
- "url",
- "utf-8",
-]
-
 [[package]]
 name = "typenum"
 version = "1.17.0"
@@ -2518,20 +2281,11 @@ version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
 
-[[package]]
-name = "unicase"
-version = "2.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
-dependencies = [
- "version_check",
-]
-
 [[package]]
 name = "unicode-bidi"
-version = "0.3.13"
+version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
 
 [[package]]
 name = "unicode-ident"
@@ -2541,18 +2295,18 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 [[package]]
 name = "unicode-normalization"
-version = "0.1.22"
+version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
 dependencies = [
  "tinyvec",
 ]
 
 [[package]]
 name = "unicode-segmentation"
-version = "1.10.1"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
+checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
 
 [[package]]
 name = "unicode_categories"
@@ -2562,9 +2316,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
 
 [[package]]
 name = "url"
-version = "2.4.1"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
+checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
 dependencies = [
  "form_urlencoded",
  "idna",
@@ -2572,59 +2326,18 @@ dependencies = [
 ]
 
 [[package]]
-name = "utf-8"
-version = "0.7.6"
+name = "urlencoding"
+version = "2.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
-
-[[package]]
-name = "utoipa"
-version = "4.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "272ebdfbc99111033031d2f10e018836056e4d2c8e2acda76450ec7974269fa7"
-dependencies = [
- "indexmap 2.0.2",
- "serde",
- "serde_json",
- "utoipa-gen",
-]
-
-[[package]]
-name = "utoipa-gen"
-version = "4.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3c9f4d08338c1bfa70dde39412a040a884c6f318b3d09aaaf3437a1e52027fc"
-dependencies = [
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn 2.0.38",
-]
-
-[[package]]
-name = "utoipa-swagger-ui"
-version = "6.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b39868d43c011961e04b41623e050aedf2cc93652562ff7935ce0f819aaf2da"
-dependencies = [
- "actix-web",
- "mime_guess",
- "regex",
- "rust-embed",
- "serde",
- "serde_json",
- "utoipa",
- "zip",
-]
+checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
 
 [[package]]
 name = "uuid"
-version = "1.6.1"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
+checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
 dependencies = [
  "getrandom",
- "serde",
 ]
 
 [[package]]
@@ -2639,16 +2352,6 @@ version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
-[[package]]
-name = "walkdir"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
-dependencies = [
- "same-file",
- "winapi-util",
-]
-
 [[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
@@ -2656,10 +2359,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
-name = "wasm-bindgen"
-version = "0.2.87"
+name = "wasite"
+version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
+checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
 dependencies = [
  "cfg-if",
  "wasm-bindgen-macro",
@@ -2667,24 +2376,24 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.87"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
 dependencies = [
  "bumpalo",
  "log",
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.38",
+ "syn 2.0.60",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.87"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -2692,67 +2401,40 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.87"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.38",
+ "syn 2.0.60",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.87"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
 
 [[package]]
 name = "whoami"
-version = "1.4.1"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50"
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9"
 dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
+ "redox_syscall 0.4.1",
+ "wasite",
 ]
 
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-util"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
 [[package]]
 name = "windows-core"
-version = "0.51.1"
+version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.52.5",
 ]
 
 [[package]]
@@ -2761,7 +2443,16 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.5",
 ]
 
 [[package]]
@@ -2770,13 +2461,29 @@ version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
 dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.5",
+ "windows_aarch64_msvc 0.52.5",
+ "windows_i686_gnu 0.52.5",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.5",
+ "windows_x86_64_gnu 0.52.5",
+ "windows_x86_64_gnullvm 0.52.5",
+ "windows_x86_64_msvc 0.52.5",
 ]
 
 [[package]]
@@ -2785,42 +2492,90 @@ version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
 
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+
 [[package]]
 name = "windows_i686_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
 
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+
 [[package]]
 name = "windows_i686_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
 
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
 
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
 
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+
 [[package]]
 name = "yaml-rust"
 version = "0.4.5"
@@ -2832,22 +2587,22 @@ dependencies = [
 
 [[package]]
 name = "zerocopy"
-version = "0.7.20"
+version = "0.7.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd66a62464e3ffd4e37bd09950c2b9dd6c4f8767380fabba0d523f9a775bc85a"
+checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
 dependencies = [
  "zerocopy-derive",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.7.20"
+version = "0.7.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "255c4596d41e6916ced49cfafea18727b24d67878fa180ddfd69b9df34fd1726"
+checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.38",
+ "syn 2.0.60",
 ]
 
 [[package]]
@@ -2856,42 +2611,29 @@ version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
 
-[[package]]
-name = "zip"
-version = "0.6.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
-dependencies = [
- "byteorder",
- "crc32fast",
- "crossbeam-utils",
- "flate2",
-]
-
 [[package]]
 name = "zstd"
-version = "0.12.4"
+version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c"
+checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a"
 dependencies = [
  "zstd-safe",
 ]
 
 [[package]]
 name = "zstd-safe"
-version = "6.0.6"
+version = "7.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581"
+checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a"
 dependencies = [
- "libc",
  "zstd-sys",
 ]
 
 [[package]]
 name = "zstd-sys"
-version = "2.0.9+zstd.1.5.5"
+version = "2.0.10+zstd.1.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656"
+checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa"
 dependencies = [
  "cc",
  "pkg-config",
diff --git a/Cargo.toml b/Cargo.toml
index e11518a..c189eed 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,7 +23,7 @@ chrono = { version = "0.4", features = ["serde"] }
 
 sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros", "chrono"] }
 libsqlite3-sys = { version = "*", features = ["bundled"] }
-uuid = "1.6"
+uuid = { version = "1.8", features = ["v4"] }
 futures = "0.3"
 libc = "0.2"
 
diff --git a/Makefile b/Makefile
index 8654a1e..e290b69 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,5 @@
+export DATABASE_URL=sqlite://${PWD}/emgauwa-dev.sqlite
+
 sqlx:
 	cargo sqlx database drop -y
 	cargo sqlx database create
diff --git a/src/db/macro.rs b/src/db/macro.rs
index 62092ea..a849fd0 100644
--- a/src/db/macro.rs
+++ b/src/db/macro.rs
@@ -97,7 +97,7 @@ impl DbMacro {
 		conn: &mut PoolConnection<Sqlite>,
 		new_name: &str,
 	) -> Result<DbMacro, DatabaseError> {
-		sqlx::query!("UPDATE relays SET name = ? WHERE id = ?", new_name, self.id,)
+		sqlx::query!("UPDATE macros SET name = ? WHERE id = ?", new_name, self.id,)
 			.execute(conn.deref_mut())
 			.await?;
 

From e9b09cd709e8dd8e0e4343b3e7d2689d5aed919d Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Sat, 4 May 2024 18:32:47 +0200
Subject: [PATCH 05/18] Fix minor issues

---
 Cargo.toml                  | 6 +++---
 src/errors/emgauwa_error.rs | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index c189eed..d8778b5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,14 +14,14 @@ serde = "1.0"
 serde_json = "1.0"
 serde_derive = "1.0"
 
-simple_logger = "4.2"
+simple_logger = "5.0"
 log = "0.4"
 
-config = "0.13"
+config = "0.14"
 
 chrono = { version = "0.4", features = ["serde"] }
 
-sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros", "chrono"] }
+sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros"] }
 libsqlite3-sys = { version = "*", features = ["bundled"] }
 uuid = { version = "1.8", features = ["v4"] }
 futures = "0.3"
diff --git a/src/errors/emgauwa_error.rs b/src/errors/emgauwa_error.rs
index 0e8ce65..ba7825a 100644
--- a/src/errors/emgauwa_error.rs
+++ b/src/errors/emgauwa_error.rs
@@ -139,7 +139,7 @@ impl Serialize for EmgauwaError {
 	{
 		let mut s = serializer.serialize_struct("error", 2)?;
 		s.serialize_field("code", &self.get_code().as_u16())?;
-		s.serialize_field("description", &String::from(self))?;
+		s.serialize_field("message", &String::from(self))?;
 		s.end()
 	}
 }

From 228b3663205d68b3bf35d519bae49a7f80321d14 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Sun, 5 May 2024 23:46:49 +0200
Subject: [PATCH 06/18] Move relay drivers from common to controller

---
 Cargo.lock                  | 241 ++++++++++++++++++++----------------
 Cargo.toml                  |   6 +-
 src/drivers/gpio.rs         |  35 ------
 src/drivers/mod.rs          |  19 ---
 src/drivers/null.rs         |  26 ----
 src/drivers/piface.rs       |  52 --------
 src/errors/emgauwa_error.rs |  22 ----
 src/lib.rs                  |   1 -
 8 files changed, 132 insertions(+), 270 deletions(-)
 delete mode 100644 src/drivers/gpio.rs
 delete mode 100644 src/drivers/mod.rs
 delete mode 100644 src/drivers/null.rs
 delete mode 100644 src/drivers/piface.rs

diff --git a/Cargo.lock b/Cargo.lock
index 97554c4..18d357b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -54,8 +54,8 @@ dependencies = [
  "actix-rt",
  "actix-service",
  "actix-utils",
- "ahash 0.8.11",
- "base64 0.21.7",
+ "ahash",
+ "base64",
  "bitflags 2.5.0",
  "brotli",
  "bytes",
@@ -169,7 +169,7 @@ dependencies = [
  "actix-service",
  "actix-utils",
  "actix-web-codegen",
- "ahash 0.8.11",
+ "ahash",
  "bytes",
  "bytestring",
  "cfg-if",
@@ -250,17 +250,6 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
-[[package]]
-name = "ahash"
-version = "0.7.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
-dependencies = [
- "getrandom",
- "once_cell",
- "version_check",
-]
-
 [[package]]
 name = "ahash"
 version = "0.8.11"
@@ -341,9 +330,9 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.2.0"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
 
 [[package]]
 name = "backtrace"
@@ -360,12 +349,6 @@ dependencies = [
  "rustc-demangle",
 ]
 
-[[package]]
-name = "base64"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
-
 [[package]]
 name = "base64"
 version = "0.21.7"
@@ -494,11 +477,12 @@ dependencies = [
 
 [[package]]
 name = "config"
-version = "0.13.4"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23738e11972c7643e4ec947840fc463b6a571afcd3e735bdfce7d03c7a784aca"
+checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be"
 dependencies = [
  "async-trait",
+ "convert_case 0.6.0",
  "json5",
  "lazy_static",
  "nom",
@@ -517,12 +501,41 @@ version = "0.9.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
 
+[[package]]
+name = "const-random"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
+dependencies = [
+ "const-random-macro",
+]
+
+[[package]]
+name = "const-random-macro"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "tiny-keccak",
+]
+
 [[package]]
 name = "convert_case"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
 
+[[package]]
+name = "convert_case"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
+dependencies = [
+ "unicode-segmentation",
+]
+
 [[package]]
 name = "cookie"
 version = "0.16.2"
@@ -597,6 +610,12 @@ version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
 
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
 [[package]]
 name = "crypto-common"
 version = "0.1.6"
@@ -633,7 +652,7 @@ version = "0.99.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
 dependencies = [
- "convert_case",
+ "convert_case 0.4.0",
  "proc-macro2",
  "quote",
  "rustc_version",
@@ -654,9 +673,12 @@ dependencies = [
 
 [[package]]
 name = "dlv-list"
-version = "0.3.0"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
+checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
+dependencies = [
+ "const-random",
+]
 
 [[package]]
 name = "dotenvy"
@@ -686,9 +708,6 @@ dependencies = [
  "libc",
  "libsqlite3-sys",
  "log",
- "rppal 0.17.1",
- "rppal-mcp23s17",
- "rppal-pfd",
  "serde",
  "serde_derive",
  "serde_json",
@@ -935,12 +954,9 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.12.3"
+version = "0.13.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
-dependencies = [
- "ahash 0.7.8",
-]
+checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
 
 [[package]]
 name = "hashbrown"
@@ -948,7 +964,7 @@ version = "0.14.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
 dependencies = [
- "ahash 0.8.11",
+ "ahash",
  "allocator-api2",
 ]
 
@@ -1289,9 +1305,9 @@ dependencies = [
 
 [[package]]
 name = "num-iter"
-version = "0.1.44"
+version = "0.1.45"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9"
+checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
 dependencies = [
  "autocfg",
  "num-integer",
@@ -1300,9 +1316,9 @@ dependencies = [
 
 [[package]]
 name = "num-traits"
-version = "0.2.18"
+version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
 dependencies = [
  "autocfg",
  "libm",
@@ -1334,12 +1350,12 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
 [[package]]
 name = "ordered-multimap"
-version = "0.4.3"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
+checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
 dependencies = [
  "dlv-list",
- "hashbrown 0.12.3",
+ "hashbrown 0.13.2",
 ]
 
 [[package]]
@@ -1585,55 +1601,14 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
 
 [[package]]
 name = "ron"
-version = "0.7.1"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a"
+checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
 dependencies = [
- "base64 0.13.1",
- "bitflags 1.3.2",
+ "base64",
+ "bitflags 2.5.0",
  "serde",
-]
-
-[[package]]
-name = "rppal"
-version = "0.14.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "612e1a22e21f08a246657c6433fe52b773ae43d07c9ef88ccfc433cc8683caba"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "rppal"
-version = "0.17.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1dc171bbe325b04172e18d917c58c2cf1fb5adfd9ffabb1d6b3d62ba4c1c1331"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "rppal-mcp23s17"
-version = "0.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66119979a7ae3d743517c63b26e039080bd8d3b9c782bad5103c06877293b1cf"
-dependencies = [
- "bitflags 1.3.2",
- "log",
- "rppal 0.14.1",
- "thiserror",
-]
-
-[[package]]
-name = "rppal-pfd"
-version = "0.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1804948e00d57085912f87c724be9aa4fdb6c00aa54ef8cf1eae40dd76053088"
-dependencies = [
- "log",
- "rppal 0.14.1",
- "rppal-mcp23s17",
- "thiserror",
+ "serde_derive",
 ]
 
 [[package]]
@@ -1658,9 +1633,9 @@ dependencies = [
 
 [[package]]
 name = "rust-ini"
-version = "0.18.0"
+version = "0.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df"
+checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
 dependencies = [
  "cfg-if",
  "ordered-multimap",
@@ -1743,6 +1718,15 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde_spanned"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "serde_urlencoded"
 version = "0.7.1"
@@ -1798,9 +1782,9 @@ dependencies = [
 
 [[package]]
 name = "simple_logger"
-version = "4.3.3"
+version = "5.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1"
+checksum = "e8c5dfa5e08767553704aa0ffd9d9794d527103c736aba9854773851fd7497eb"
 dependencies = [
  "colored",
  "log",
@@ -1888,11 +1872,10 @@ version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6"
 dependencies = [
- "ahash 0.8.11",
+ "ahash",
  "atoi",
  "byteorder",
  "bytes",
- "chrono",
  "crc",
  "crossbeam-queue",
  "either",
@@ -1953,7 +1936,6 @@ dependencies = [
  "sha2",
  "sqlx-core",
  "sqlx-mysql",
- "sqlx-postgres",
  "sqlx-sqlite",
  "syn 1.0.109",
  "tempfile",
@@ -1968,11 +1950,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
 dependencies = [
  "atoi",
- "base64 0.21.7",
+ "base64",
  "bitflags 2.5.0",
  "byteorder",
  "bytes",
- "chrono",
  "crc",
  "digest",
  "dotenvy",
@@ -2011,10 +1992,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
 dependencies = [
  "atoi",
- "base64 0.21.7",
+ "base64",
  "bitflags 2.5.0",
  "byteorder",
- "chrono",
  "crc",
  "dotenvy",
  "etcetera",
@@ -2050,7 +2030,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa"
 dependencies = [
  "atoi",
- "chrono",
  "flume",
  "futures-channel",
  "futures-core",
@@ -2171,6 +2150,15 @@ dependencies = [
  "time-core",
 ]
 
+[[package]]
+name = "tiny-keccak"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
+dependencies = [
+ "crunchy",
+]
+
 [[package]]
 name = "tinyvec"
 version = "1.6.0"
@@ -2216,25 +2204,49 @@ dependencies = [
 
 [[package]]
 name = "tokio-util"
-version = "0.7.10"
+version = "0.7.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
 dependencies = [
  "bytes",
  "futures-core",
  "futures-sink",
  "pin-project-lite",
  "tokio",
- "tracing",
 ]
 
 [[package]]
 name = "toml"
-version = "0.5.11"
+version = "0.8.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
 dependencies = [
  "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
 ]
 
 [[package]]
@@ -2576,6 +2588,15 @@ version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
 
+[[package]]
+name = "winnow"
+version = "0.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "yaml-rust"
 version = "0.4.5"
@@ -2587,18 +2608,18 @@ dependencies = [
 
 [[package]]
 name = "zerocopy"
-version = "0.7.32"
+version = "0.7.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
+checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c"
 dependencies = [
  "zerocopy-derive",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.7.32"
+version = "0.7.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
+checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/Cargo.toml b/Cargo.toml
index d8778b5..30ed02a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,8 +25,4 @@ sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros"] }
 libsqlite3-sys = { version = "*", features = ["bundled"] }
 uuid = { version = "1.8", features = ["v4"] }
 futures = "0.3"
-libc = "0.2"
-
-rppal = "0.17"
-rppal-pfd = "0.0.5"
-rppal-mcp23s17 = "0.0.3"
+libc = "0.2"
\ No newline at end of file
diff --git a/src/drivers/gpio.rs b/src/drivers/gpio.rs
deleted file mode 100644
index 7847d43..0000000
--- a/src/drivers/gpio.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-use rppal::gpio::{Gpio, OutputPin};
-
-use crate::drivers::RelayDriver;
-use crate::errors::EmgauwaError;
-
-pub struct GpioDriver {
-	pub gpio: OutputPin,
-	pub inverted: bool,
-}
-
-impl GpioDriver {
-	pub fn new(pin: u8, inverted: bool) -> Result<Self, EmgauwaError> {
-		let gpio = Gpio::new()?.get(pin)?.into_output();
-		Ok(Self { gpio, inverted })
-	}
-}
-
-impl RelayDriver for GpioDriver {
-	fn set(&mut self, value: bool) -> Result<(), EmgauwaError> {
-		if self.get_high(value) {
-			self.gpio.set_high();
-		} else {
-			self.gpio.set_low();
-		}
-		Ok(())
-	}
-
-	fn get_pin(&self) -> u8 {
-		self.gpio.pin()
-	}
-
-	fn get_inverted(&self) -> bool {
-		self.inverted
-	}
-}
diff --git a/src/drivers/mod.rs b/src/drivers/mod.rs
deleted file mode 100644
index abae75a..0000000
--- a/src/drivers/mod.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-mod gpio;
-mod null;
-mod piface;
-
-pub use gpio::GpioDriver;
-pub use null::NullDriver;
-pub use piface::PiFaceDriver;
-
-use crate::errors::EmgauwaError;
-
-pub trait RelayDriver {
-	fn get_high(&self, value: bool) -> bool {
-		value ^ self.get_inverted()
-	}
-
-	fn set(&mut self, value: bool) -> Result<(), EmgauwaError>;
-	fn get_pin(&self) -> u8;
-	fn get_inverted(&self) -> bool;
-}
diff --git a/src/drivers/null.rs b/src/drivers/null.rs
deleted file mode 100644
index be4e8b9..0000000
--- a/src/drivers/null.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use crate::drivers::RelayDriver;
-use crate::errors::EmgauwaError;
-
-pub struct NullDriver {
-	pub pin: u8,
-}
-
-impl NullDriver {
-	pub fn new(pin: u8) -> Self {
-		Self { pin }
-	}
-}
-
-impl RelayDriver for NullDriver {
-	fn set(&mut self, _value: bool) -> Result<(), EmgauwaError> {
-		Ok(())
-	}
-
-	fn get_pin(&self) -> u8 {
-		self.pin
-	}
-
-	fn get_inverted(&self) -> bool {
-		false
-	}
-}
diff --git a/src/drivers/piface.rs b/src/drivers/piface.rs
deleted file mode 100644
index 7bc20da..0000000
--- a/src/drivers/piface.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use rppal_pfd::{
-	ChipSelect, HardwareAddress, OutputPin, PiFaceDigital, PiFaceDigitalError, SpiBus, SpiMode,
-};
-
-use crate::drivers::RelayDriver;
-use crate::errors::EmgauwaError;
-
-pub struct PiFaceDriver {
-	pub pfd_pin: OutputPin,
-}
-
-impl PiFaceDriver {
-	pub fn new(pin: u8, pfd: &Option<PiFaceDigital>) -> Result<Self, EmgauwaError> {
-		let pfd = pfd.as_ref().ok_or(EmgauwaError::Hardware(String::from(
-			"PiFaceDigital not initialized",
-		)))?;
-		let pfd_pin = pfd.get_output_pin(pin)?;
-		Ok(Self { pfd_pin })
-	}
-
-	pub fn init_piface() -> Result<PiFaceDigital, EmgauwaError> {
-		let mut pfd = PiFaceDigital::new(
-			HardwareAddress::new(0)?,
-			SpiBus::Spi0,
-			ChipSelect::Cs0,
-			100_000,
-			SpiMode::Mode0,
-		)?;
-		pfd.init()?;
-
-		Ok(pfd)
-	}
-}
-
-impl RelayDriver for PiFaceDriver {
-	fn set(&mut self, value: bool) -> Result<(), EmgauwaError> {
-		if self.get_high(value) {
-			self.pfd_pin.set_high().map_err(PiFaceDigitalError::from)?;
-		} else {
-			self.pfd_pin.set_low().map_err(PiFaceDigitalError::from)?;
-		}
-		Ok(())
-	}
-
-	fn get_pin(&self) -> u8 {
-		self.pfd_pin.get_pin_number()
-	}
-
-	fn get_inverted(&self) -> bool {
-		false
-	}
-}
diff --git a/src/errors/emgauwa_error.rs b/src/errors/emgauwa_error.rs
index ba7825a..bd7f04e 100644
--- a/src/errors/emgauwa_error.rs
+++ b/src/errors/emgauwa_error.rs
@@ -6,9 +6,6 @@ use actix::MailboxError;
 use actix_web::http::StatusCode;
 use actix_web::HttpResponse;
 use config::ConfigError;
-use rppal::gpio;
-use rppal_mcp23s17::Mcp23s17Error;
-use rppal_pfd::PiFaceDigitalError;
 use serde::ser::SerializeStruct;
 use serde::{Serialize, Serializer};
 
@@ -99,25 +96,6 @@ impl From<ConfigError> for EmgauwaError {
 	}
 }
 
-impl From<gpio::Error> for EmgauwaError {
-	fn from(value: gpio::Error) -> Self {
-		Self::Hardware(value.to_string())
-	}
-}
-
-impl From<PiFaceDigitalError> for EmgauwaError {
-	fn from(value: PiFaceDigitalError) -> Self {
-		match value {
-			PiFaceDigitalError::Mcp23s17Error { source } => match source {
-				Mcp23s17Error::SpiError { source } => Self::Hardware(source.to_string()),
-				_ => Self::Hardware(source.to_string()),
-			},
-			PiFaceDigitalError::GpioError { source } => Self::Hardware(source.to_string()),
-			_ => Self::Hardware(value.to_string()),
-		}
-	}
-}
-
 impl From<&EmgauwaError> for HttpResponse {
 	fn from(err: &EmgauwaError) -> Self {
 		HttpResponse::build(err.get_code()).json(err)
diff --git a/src/lib.rs b/src/lib.rs
index 14609b9..f939d0c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,5 @@
 pub mod constants;
 pub mod db;
-pub mod drivers;
 pub mod errors;
 pub mod models;
 pub mod settings;

From b14049b3f6e8aa6a748e4d185bd52ca3f7a38f38 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Mon, 6 May 2024 16:28:10 +0200
Subject: [PATCH 07/18] Add request model for GET tagged schedule

---
 src/types/request.rs | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/types/request.rs b/src/types/request.rs
index 5c9a927..4956733 100644
--- a/src/types/request.rs
+++ b/src/types/request.rs
@@ -28,6 +28,11 @@ pub struct RequestRelayUpdate {
 	pub tags: Option<Vec<String>>,
 }
 
+#[derive(Debug, Deserialize)]
+pub struct RequestScheduleGetTagged {
+	pub strict: Option<bool>,
+}
+
 #[derive(Debug, Serialize, Deserialize)]
 pub struct RequestRelayPulse {
 	pub duration: Option<u32>,

From f26e66d68796bcd3e391a36100c12c3bb9fa210a Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Fri, 10 May 2024 17:43:29 +0200
Subject: [PATCH 08/18] Add parameter for db pool size

---
 src/db/mod.rs               | 4 ++--
 src/errors/emgauwa_error.rs | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/db/mod.rs b/src/db/mod.rs
index 7ebb6c3..d406c94 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -33,14 +33,14 @@ pub async fn run_migrations(pool: &Pool<Sqlite>) -> Result<(), EmgauwaError> {
 	Ok(())
 }
 
-pub async fn init(db: &str) -> Result<Pool<Sqlite>, EmgauwaError> {
+pub async fn init(db: &str, pool_size: u32) -> Result<Pool<Sqlite>, EmgauwaError> {
 	let options = SqliteConnectOptions::from_str(db)?
 		.create_if_missing(true)
 		.log_statements(log::LevelFilter::Trace);
 
 	let pool: Pool<Sqlite> = SqlitePoolOptions::new()
 		.acquire_timeout(std::time::Duration::from_secs(1))
-		.max_connections(5)
+		.max_connections(pool_size)
 		.connect_with(options)
 		.await?;
 
diff --git a/src/errors/emgauwa_error.rs b/src/errors/emgauwa_error.rs
index bd7f04e..f37c38e 100644
--- a/src/errors/emgauwa_error.rs
+++ b/src/errors/emgauwa_error.rs
@@ -47,7 +47,7 @@ impl From<&EmgauwaError> for String {
 			EmgauwaError::Database(err) => String::from(err),
 			EmgauwaError::Uid(_) => String::from("the uid is in a bad format"),
 			EmgauwaError::Internal(_) => String::from("internal error"),
-			EmgauwaError::Connection(_) => String::from("the target controller is not connected"),
+			EmgauwaError::Connection(uid) => format!("unable to connect to controller with uid: {}", uid),
 			EmgauwaError::Other(err) => format!("other error: {}", err),
 			EmgauwaError::Hardware(err) => format!("hardware error: {}", err),
 		}

From 9326b66007f61cb3c92cf3774f2c6a8d238a3b7d Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Mon, 13 May 2024 19:17:35 +0200
Subject: [PATCH 09/18] Simplify schedule in macro action

---
 src/models/macro_action.rs | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/models/macro_action.rs b/src/models/macro_action.rs
index a36a45a..786bf81 100644
--- a/src/models/macro_action.rs
+++ b/src/models/macro_action.rs
@@ -3,13 +3,13 @@ use serde_derive::{Deserialize, Serialize};
 use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
-use crate::db::{DbJunctionRelaySchedule, DbMacroAction};
+use crate::db::{DbJunctionRelaySchedule, DbMacroAction, DbSchedule};
 use crate::errors::{DatabaseError, EmgauwaError};
-use crate::models::{FromDbModel, Relay, Schedule};
+use crate::models::{FromDbModel, Relay};
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
 pub struct MacroAction {
-	pub schedule: Schedule,
+	pub schedule: DbSchedule,
 	pub relay: Relay,
 	pub weekday: i64,
 }
@@ -30,8 +30,7 @@ impl FromDbModel for MacroAction {
 		db_model: Self::DbModel,
 		_cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError> {
-		let schedule_db = block_on(db_model.get_schedule(conn))?;
-		let schedule = Schedule::from_db_model(conn, schedule_db)?;
+		let schedule = block_on(db_model.get_schedule(conn))?;
 
 		let relay_db = block_on(db_model.get_relay(conn))?;
 		let relay = Relay::from_db_model(conn, relay_db)?;
@@ -48,7 +47,7 @@ impl FromDbModel for MacroAction {
 
 impl MacroAction {
 	pub async fn execute(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), EmgauwaError> {
-		DbJunctionRelaySchedule::set_schedule(conn, &self.relay.r, &self.schedule.s, self.weekday)
+		DbJunctionRelaySchedule::set_schedule(conn, &self.relay.r, &self.schedule, self.weekday)
 			.await?;
 		Ok(())
 	}

From 473832f58a258e404d7bba0ca20a3ff58e6ba11c Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Sun, 26 May 2024 22:48:22 +0200
Subject: [PATCH 10/18] Rename active_schedule to override_schedule and add
 EmgauwaNow

---
 Cargo.lock                  | 117 +++++++++++++++++++++++++++++++++---
 Cargo.toml                  |   1 +
 src/db/model_utils.rs       |  14 ++---
 src/errors/emgauwa_error.rs |   4 +-
 src/models/controller.rs    |  26 +++++---
 src/models/relay.rs         |  46 +++++++++++---
 src/types/emgauwa_now.rs    |  26 ++++++++
 src/types/mod.rs            |  10 ++-
 src/types/request.rs        |   7 ++-
 src/utils.rs                |   2 +-
 10 files changed, 218 insertions(+), 35 deletions(-)
 create mode 100644 src/types/emgauwa_now.rs

diff --git a/Cargo.lock b/Cargo.lock
index 18d357b..1bee2db 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -55,7 +55,7 @@ dependencies = [
  "actix-service",
  "actix-utils",
  "ahash",
- "base64",
+ "base64 0.21.7",
  "bitflags 2.5.0",
  "brotli",
  "bytes",
@@ -355,6 +355,12 @@ version = "0.21.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
 
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
 [[package]]
 name = "base64ct"
 version = "1.6.0"
@@ -626,6 +632,41 @@ dependencies = [
  "typenum",
 ]
 
+[[package]]
+name = "darling"
+version = "0.20.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 2.0.60",
+]
+
 [[package]]
 name = "der"
 version = "0.7.9"
@@ -644,6 +685,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
 dependencies = [
  "powerfmt",
+ "serde",
 ]
 
 [[package]]
@@ -711,6 +753,7 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
+ "serde_with",
  "simple_logger",
  "sqlx",
  "uuid",
@@ -945,13 +988,19 @@ dependencies = [
  "futures-sink",
  "futures-util",
  "http",
- "indexmap",
+ "indexmap 2.2.6",
  "slab",
  "tokio",
  "tokio-util",
  "tracing",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
 [[package]]
 name = "hashbrown"
 version = "0.13.2"
@@ -1065,6 +1114,12 @@ dependencies = [
  "cc",
 ]
 
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
 [[package]]
 name = "idna"
 version = "0.5.0"
@@ -1075,6 +1130,17 @@ dependencies = [
  "unicode-normalization",
 ]
 
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+ "serde",
+]
+
 [[package]]
 name = "indexmap"
 version = "2.2.6"
@@ -1083,6 +1149,7 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
 dependencies = [
  "equivalent",
  "hashbrown 0.14.5",
+ "serde",
 ]
 
 [[package]]
@@ -1605,7 +1672,7 @@ version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
 dependencies = [
- "base64",
+ "base64 0.21.7",
  "bitflags 2.5.0",
  "serde",
  "serde_derive",
@@ -1739,6 +1806,36 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde_with"
+version = "3.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20"
+dependencies = [
+ "base64 0.22.1",
+ "chrono",
+ "hex",
+ "indexmap 1.9.3",
+ "indexmap 2.2.6",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "serde_with_macros",
+ "time",
+]
+
+[[package]]
+name = "serde_with_macros"
+version = "3.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
 [[package]]
 name = "sha1"
 version = "0.10.6"
@@ -1887,7 +1984,7 @@ dependencies = [
  "futures-util",
  "hashlink",
  "hex",
- "indexmap",
+ "indexmap 2.2.6",
  "log",
  "memchr",
  "once_cell",
@@ -1950,7 +2047,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
 dependencies = [
  "atoi",
- "base64",
+ "base64 0.21.7",
  "bitflags 2.5.0",
  "byteorder",
  "bytes",
@@ -1992,7 +2089,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
 dependencies = [
  "atoi",
- "base64",
+ "base64 0.21.7",
  "bitflags 2.5.0",
  "byteorder",
  "crc",
@@ -2057,6 +2154,12 @@ dependencies = [
  "unicode-normalization",
 ]
 
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
 [[package]]
 name = "subtle"
 version = "2.5.0"
@@ -2242,7 +2345,7 @@ version = "0.22.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef"
 dependencies = [
- "indexmap",
+ "indexmap 2.2.6",
  "serde",
  "serde_spanned",
  "toml_datetime",
diff --git a/Cargo.toml b/Cargo.toml
index 30ed02a..464e9b2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,7 @@ actix-web-actors = "4.2"
 serde = "1.0"
 serde_json = "1.0"
 serde_derive = "1.0"
+serde_with = "3.8"
 
 simple_logger = "5.0"
 log = "0.4"
diff --git a/src/db/model_utils.rs b/src/db/model_utils.rs
index e178d8e..db845bc 100644
--- a/src/db/model_utils.rs
+++ b/src/db/model_utils.rs
@@ -1,10 +1,10 @@
 use chrono::{NaiveTime, Timelike};
 use serde::{Deserialize, Serialize};
-use sqlx::{Decode, Encode, Sqlite, Type};
 use sqlx::database::HasArguments;
 use sqlx::encode::IsNull;
 use sqlx::error::BoxDynError;
 use sqlx::sqlite::{SqliteTypeInfo, SqliteValueRef};
+use sqlx::{Decode, Encode, Sqlite, Type};
 
 use crate::db::DbPeriods;
 
@@ -67,12 +67,12 @@ impl Period {
 		let start_before_end = self.start.lt(&self.end);
 
 		match (start_after_now, end_after_now, start_before_end) {
-			(false, false, true) => false,  // both before now; start before end means "normal" period before now
-			(false, false, false) => true,  // both before now; end before start means "inversed" period around now
-			(true, false, _) => false,   // only start after now
-			(false, true, _) => true,    // only end after now
-			(true, true, true) => false, // both after now but start first
-			(true, true, false) => true, // both after now but end first
+			(false, false, true) => false, // both before now; start before end means "normal" period before now
+			(false, false, false) => true, // both before now; end before start means "inversed" period around now
+			(true, false, _) => false,     // only start after now
+			(false, true, _) => true,      // only end after now
+			(true, true, true) => false,   // both after now but start first
+			(true, true, false) => true,   // both after now but end first
 		}
 	}
 
diff --git a/src/errors/emgauwa_error.rs b/src/errors/emgauwa_error.rs
index f37c38e..a833c78 100644
--- a/src/errors/emgauwa_error.rs
+++ b/src/errors/emgauwa_error.rs
@@ -47,7 +47,9 @@ impl From<&EmgauwaError> for String {
 			EmgauwaError::Database(err) => String::from(err),
 			EmgauwaError::Uid(_) => String::from("the uid is in a bad format"),
 			EmgauwaError::Internal(_) => String::from("internal error"),
-			EmgauwaError::Connection(uid) => format!("unable to connect to controller with uid: {}", uid),
+			EmgauwaError::Connection(uid) => {
+				format!("unable to connect to controller with uid: {}", uid)
+			}
 			EmgauwaError::Other(err) => format!("other error: {}", err),
 			EmgauwaError::Hardware(err) => format!("hardware error: {}", err),
 		}
diff --git a/src/models/controller.rs b/src/models/controller.rs
index 96d7b00..ebc556a 100644
--- a/src/models/controller.rs
+++ b/src/models/controller.rs
@@ -10,7 +10,7 @@ use sqlx::Sqlite;
 use crate::db::DbController;
 use crate::errors::{DatabaseError, EmgauwaError};
 use crate::models::{convert_db_list_cache, FromDbModel, Relay};
-use crate::types::RelayStates;
+use crate::types::{EmgauwaNow, RelayState, RelayStates};
 
 #[derive(Serialize, Deserialize, Debug, Clone, MessageResponse)]
 pub struct Controller {
@@ -57,19 +57,29 @@ impl Controller {
 		self.relays
 			.iter_mut()
 			.zip(relay_states.iter())
-			.for_each(|(relay, is_on)| {
-				relay.is_on = *is_on;
+			.for_each(|(relay, state)| {
+				relay.active_schedule = state.active_schedule.clone();
+				relay.is_on = state.is_on;
 			});
 	}
 
 	pub fn get_relay_states(&self) -> RelayStates {
-		self.relays.iter().map(|r| r.is_on).collect()
-	}
-
-	pub fn get_next_time(&self, now: &NaiveTime) -> Option<NaiveTime> {
 		self.relays
 			.iter()
-			.filter_map(|r| r.active_schedule.get_next_time(now))
+			.map(|r| RelayState {
+				active_schedule: r.active_schedule.clone(),
+				is_on: r.is_on,
+			})
+			.collect()
+	}
+
+	pub fn check_next_time(&mut self, now: &EmgauwaNow) -> Option<NaiveTime> {
+		self.relays
+			.iter_mut()
+			.filter_map(|r| {
+				r.reload_active_schedule(now.weekday);
+				r.active_schedule.get_next_time(&now.time)
+			})
 			.min()
 	}
 
diff --git a/src/models/relay.rs b/src/models/relay.rs
index e6ecd0f..f44b290 100644
--- a/src/models/relay.rs
+++ b/src/models/relay.rs
@@ -9,7 +9,8 @@ use sqlx::Sqlite;
 use crate::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule};
 use crate::errors::DatabaseError;
 use crate::models::FromDbModel;
-use crate::types::EmgauwaUid;
+use crate::types::{EmgauwaUid, Weekday};
+use crate::utils;
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
 pub struct Relay {
@@ -19,12 +20,23 @@ pub struct Relay {
 	pub controller_id: EmgauwaUid,
 	pub schedules: Vec<DbSchedule>,
 	pub active_schedule: DbSchedule,
+	#[serde(
+		default,                                    // <- important for deserialization
+		skip_serializing_if = "Option::is_none",    // <- important for serialization
+		with = "::serde_with::rust::double_option",
+	)]
+	pub override_schedule: Option<Option<DbSchedule>>,
 	pub is_on: Option<bool>,
 	pub tags: Vec<String>,
 
 	// for internal use only.
 	#[serde(skip)]
 	pub pulsing: Option<Instant>,
+	#[serde(
+		skip,
+		default = "utils::get_weekday",
+	)]
+	pub override_schedule_weekday: Weekday,
 }
 
 impl FromDbModel for Relay {
@@ -58,9 +70,11 @@ impl FromDbModel for Relay {
 			controller_id,
 			schedules,
 			active_schedule,
+			override_schedule: None,
 			is_on,
 			tags,
 			pulsing: None,
+			override_schedule_weekday: Weekday::default(),
 		})
 	}
 }
@@ -69,16 +83,9 @@ impl Relay {
 	pub fn reload(&mut self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
 		self.r = block_on(self.r.reload(conn))?;
 		self.schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &self.r))?;
-		self.reload_active_schedule(conn)?;
 
-		Ok(())
-	}
+		self.reload_active_schedule(utils::get_weekday());
 
-	pub fn reload_active_schedule(
-		&mut self,
-		conn: &mut PoolConnection<Sqlite>,
-	) -> Result<(), DatabaseError> {
-		self.active_schedule = block_on(self.r.get_active_schedule(conn))?;
 		Ok(())
 	}
 
@@ -103,4 +110,25 @@ impl Relay {
 			None => None,
 		}
 	}
+
+	pub fn reload_active_schedule(&mut self, weekday: Weekday) {
+		if let Some((Some(schedule), schedule_weekday)) = self.unwrap_override_schedule() {
+			if schedule_weekday == weekday {
+				self.active_schedule = schedule.clone();
+				return;
+			}
+			if schedule_weekday != weekday {
+				self.override_schedule = None;
+			}
+		}
+
+		self.active_schedule = self.schedules.get(weekday as usize).unwrap().clone()
+	}
+
+	pub fn unwrap_override_schedule(&self) -> Option<(&Option<DbSchedule>, Weekday)> {
+		if let Some(schedule) = &self.override_schedule {
+			return Some((schedule, self.override_schedule_weekday));
+		}
+		None
+	}
 }
diff --git a/src/types/emgauwa_now.rs b/src/types/emgauwa_now.rs
new file mode 100644
index 0000000..40f4907
--- /dev/null
+++ b/src/types/emgauwa_now.rs
@@ -0,0 +1,26 @@
+use std::time::Instant;
+
+use chrono::{Local, NaiveTime, Timelike};
+
+use crate::types::Weekday;
+use crate::utils;
+
+pub struct EmgauwaNow {
+	pub time: NaiveTime,
+	pub instant: Instant,
+	pub weekday: Weekday,
+}
+
+impl EmgauwaNow {
+	pub fn now() -> EmgauwaNow {
+		EmgauwaNow {
+			time: Local::now().time(),
+			instant: Instant::now(),
+			weekday: utils::get_weekday(),
+		}
+	}
+
+	pub fn time_in_s(&self) -> u32 {
+		self.time.num_seconds_from_midnight()
+	}
+}
diff --git a/src/types/mod.rs b/src/types/mod.rs
index dbbfb77..24f3c78 100644
--- a/src/types/mod.rs
+++ b/src/types/mod.rs
@@ -1,8 +1,10 @@
+mod emgauwa_now;
 mod emgauwa_uid;
 mod request;
 mod schedule_uid;
 
 use actix::Message;
+pub use emgauwa_now::EmgauwaNow;
 pub use emgauwa_uid::EmgauwaUid;
 pub use request::*;
 pub use schedule_uid::ScheduleUid;
@@ -14,7 +16,13 @@ use crate::models::{Controller, Relay};
 
 pub type Weekday = i64;
 
-pub type RelayStates = Vec<Option<bool>>;
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct RelayState {
+	pub active_schedule: DbSchedule,
+	pub is_on: Option<bool>
+}
+
+pub type RelayStates = Vec<RelayState>;
 
 #[derive(Debug, Serialize, Deserialize, Message)]
 #[rtype(result = "Result<(), EmgauwaError>")]
diff --git a/src/types/request.rs b/src/types/request.rs
index 4956733..d452b31 100644
--- a/src/types/request.rs
+++ b/src/types/request.rs
@@ -23,7 +23,12 @@ pub struct RequestScheduleUpdate {
 #[derive(Debug, Serialize, Deserialize)]
 pub struct RequestRelayUpdate {
 	pub name: Option<String>,
-	pub active_schedule: Option<RequestScheduleId>,
+	#[serde(
+		default,                                    // <- important for deserialization
+		skip_serializing_if = "Option::is_none",    // <- important for serialization
+		with = "::serde_with::rust::double_option",
+	)]
+	pub override_schedule: Option<Option<RequestScheduleId>>,
 	pub schedules: Option<Vec<RequestScheduleId>>,
 	pub tags: Option<Vec<String>>,
 }
diff --git a/src/utils.rs b/src/utils.rs
index e9ee62e..3b20d9a 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -103,7 +103,7 @@ pub fn get_weekday() -> Weekday {
 pub fn printable_relay_states(relay_states: &RelayStates) -> String {
 	let mut relay_debug = String::new();
 	relay_states.iter().for_each(|state| {
-		relay_debug.push_str(match state {
+		relay_debug.push_str(match state.is_on {
 			Some(true) => "+",
 			Some(false) => "-",
 			None => "?",

From 929985c64af0a69db30c89e3c03d22294fc0935c Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Tue, 28 May 2024 21:17:21 +0200
Subject: [PATCH 11/18] Improve handling of override_schedule

---
 src/models/controller.rs | 15 ++++-----------
 src/models/relay.rs      | 31 ++++++++++++++++---------------
 src/types/mod.rs         | 10 ++--------
 src/types/relay_state.rs | 22 ++++++++++++++++++++++
 4 files changed, 44 insertions(+), 34 deletions(-)
 create mode 100644 src/types/relay_state.rs

diff --git a/src/models/controller.rs b/src/models/controller.rs
index ebc556a..e1a272b 100644
--- a/src/models/controller.rs
+++ b/src/models/controller.rs
@@ -57,20 +57,11 @@ impl Controller {
 		self.relays
 			.iter_mut()
 			.zip(relay_states.iter())
-			.for_each(|(relay, state)| {
-				relay.active_schedule = state.active_schedule.clone();
-				relay.is_on = state.is_on;
-			});
+			.for_each(|(relay, state)| relay.apply_state(state));
 	}
 
 	pub fn get_relay_states(&self) -> RelayStates {
-		self.relays
-			.iter()
-			.map(|r| RelayState {
-				active_schedule: r.active_schedule.clone(),
-				is_on: r.is_on,
-			})
-			.collect()
+		self.relays.iter().map(RelayState::from).collect()
 	}
 
 	pub fn check_next_time(&mut self, now: &EmgauwaNow) -> Option<NaiveTime> {
@@ -90,6 +81,8 @@ impl Controller {
 			.find(|r| r.r.number == relay_num)
 			.ok_or(EmgauwaError::Other(String::from("Relay not found")))?;
 
+		log::debug!("Pulsing relay {} until {:?}", relay_num, until);
+
 		relay.pulsing = Some(until);
 		Ok(())
 	}
diff --git a/src/models/relay.rs b/src/models/relay.rs
index f44b290..51289ca 100644
--- a/src/models/relay.rs
+++ b/src/models/relay.rs
@@ -9,7 +9,7 @@ use sqlx::Sqlite;
 use crate::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule};
 use crate::errors::DatabaseError;
 use crate::models::FromDbModel;
-use crate::types::{EmgauwaUid, Weekday};
+use crate::types::{EmgauwaUid, RelayState, Weekday};
 use crate::utils;
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
@@ -20,12 +20,7 @@ pub struct Relay {
 	pub controller_id: EmgauwaUid,
 	pub schedules: Vec<DbSchedule>,
 	pub active_schedule: DbSchedule,
-	#[serde(
-		default,                                    // <- important for deserialization
-		skip_serializing_if = "Option::is_none",    // <- important for serialization
-		with = "::serde_with::rust::double_option",
-	)]
-	pub override_schedule: Option<Option<DbSchedule>>,
+	pub override_schedule: Option<DbSchedule>,
 	pub is_on: Option<bool>,
 	pub tags: Vec<String>,
 
@@ -112,12 +107,12 @@ impl Relay {
 	}
 
 	pub fn reload_active_schedule(&mut self, weekday: Weekday) {
-		if let Some((Some(schedule), schedule_weekday)) = self.unwrap_override_schedule() {
-			if schedule_weekday == weekday {
+		if let Some(schedule) = &self.override_schedule {
+			if self.override_schedule_weekday == weekday {
 				self.active_schedule = schedule.clone();
 				return;
 			}
-			if schedule_weekday != weekday {
+			if self.override_schedule_weekday != weekday {
 				self.override_schedule = None;
 			}
 		}
@@ -125,10 +120,16 @@ impl Relay {
 		self.active_schedule = self.schedules.get(weekday as usize).unwrap().clone()
 	}
 
-	pub fn unwrap_override_schedule(&self) -> Option<(&Option<DbSchedule>, Weekday)> {
-		if let Some(schedule) = &self.override_schedule {
-			return Some((schedule, self.override_schedule_weekday));
-		}
-		None
+	pub fn apply_state(&mut self, state: &RelayState) {
+		self.active_schedule = state.active_schedule.clone();
+		self.override_schedule.clone_from(&state.override_schedule);
+		self.is_on = state.is_on;
 	}
+
+	pub fn find_and_apply_state(&mut self, stated_relays: &[Relay]) {
+		if let Some(stated_relay) = stated_relays.iter().find(|r| r.r.id == self.r.id) {
+			self.apply_state(&stated_relay.into());
+		}
+	}
+
 }
diff --git a/src/types/mod.rs b/src/types/mod.rs
index 24f3c78..44d116c 100644
--- a/src/types/mod.rs
+++ b/src/types/mod.rs
@@ -2,10 +2,12 @@ mod emgauwa_now;
 mod emgauwa_uid;
 mod request;
 mod schedule_uid;
+mod relay_state;
 
 use actix::Message;
 pub use emgauwa_now::EmgauwaNow;
 pub use emgauwa_uid::EmgauwaUid;
+pub use relay_state::{RelayState, RelayStates};
 pub use request::*;
 pub use schedule_uid::ScheduleUid;
 use serde_derive::{Deserialize, Serialize};
@@ -16,14 +18,6 @@ use crate::models::{Controller, Relay};
 
 pub type Weekday = i64;
 
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct RelayState {
-	pub active_schedule: DbSchedule,
-	pub is_on: Option<bool>
-}
-
-pub type RelayStates = Vec<RelayState>;
-
 #[derive(Debug, Serialize, Deserialize, Message)]
 #[rtype(result = "Result<(), EmgauwaError>")]
 pub enum ControllerWsAction {
diff --git a/src/types/relay_state.rs b/src/types/relay_state.rs
new file mode 100644
index 0000000..f4a2e3a
--- /dev/null
+++ b/src/types/relay_state.rs
@@ -0,0 +1,22 @@
+use serde_derive::{Deserialize, Serialize};
+use crate::db::DbSchedule;
+use crate::models::Relay;
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct RelayState {
+    pub active_schedule: DbSchedule,
+    pub override_schedule: Option<DbSchedule>,
+    pub is_on: Option<bool>
+}
+
+pub type RelayStates = Vec<RelayState>;
+
+impl From<&Relay> for RelayState {
+    fn from(relay: &Relay) -> Self {
+        RelayState {
+            active_schedule: relay.active_schedule.clone(),
+            override_schedule: relay.override_schedule.clone(),
+            is_on: relay.is_on
+        }
+    }
+}
\ No newline at end of file

From ce7a79d1de579aac9a8c43a8763cc56b88427934 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Wed, 29 May 2024 15:51:53 +0200
Subject: [PATCH 12/18] Remove guessing of active_schedule

---
 src/db/relays.rs         | 10 ----------
 src/models/controller.rs |  2 +-
 src/models/relay.rs      | 25 +++++++++++++++----------
 src/types/relay_state.rs |  2 +-
 4 files changed, 17 insertions(+), 22 deletions(-)

diff --git a/src/db/relays.rs b/src/db/relays.rs
index c7f4ca3..9045002 100644
--- a/src/db/relays.rs
+++ b/src/db/relays.rs
@@ -163,14 +163,4 @@ impl DbRelay {
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
-
-	pub async fn get_active_schedule(
-		&self,
-		conn: &mut PoolConnection<Sqlite>,
-	) -> Result<DbSchedule, DatabaseError> {
-		let weekday = utils::get_weekday();
-		DbJunctionRelaySchedule::get_schedule(conn, self, weekday as Weekday)
-			.await?
-			.ok_or(DatabaseError::NotFound)
-	}
 }
diff --git a/src/models/controller.rs b/src/models/controller.rs
index e1a272b..6bb04e3 100644
--- a/src/models/controller.rs
+++ b/src/models/controller.rs
@@ -69,7 +69,7 @@ impl Controller {
 			.iter_mut()
 			.filter_map(|r| {
 				r.reload_active_schedule(now.weekday);
-				r.active_schedule.get_next_time(&now.time)
+				r.get_next_time(&now.time)
 			})
 			.min()
 	}
diff --git a/src/models/relay.rs b/src/models/relay.rs
index 51289ca..bc2b48b 100644
--- a/src/models/relay.rs
+++ b/src/models/relay.rs
@@ -19,7 +19,7 @@ pub struct Relay {
 	pub controller: DbController,
 	pub controller_id: EmgauwaUid,
 	pub schedules: Vec<DbSchedule>,
-	pub active_schedule: DbSchedule,
+	pub active_schedule: Option<DbSchedule>,
 	pub override_schedule: Option<DbSchedule>,
 	pub is_on: Option<bool>,
 	pub tags: Vec<String>,
@@ -55,7 +55,6 @@ impl FromDbModel for Relay {
 		let controller_id = cache.uid.clone();
 
 		let schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &db_model))?;
-		let active_schedule = block_on(db_model.get_active_schedule(conn))?;
 
 		let is_on = None;
 
@@ -64,7 +63,7 @@ impl FromDbModel for Relay {
 			controller: cache,
 			controller_id,
 			schedules,
-			active_schedule,
+			active_schedule: None,
 			override_schedule: None,
 			is_on,
 			tags,
@@ -79,17 +78,23 @@ impl Relay {
 		self.r = block_on(self.r.reload(conn))?;
 		self.schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &self.r))?;
 
-		self.reload_active_schedule(utils::get_weekday());
-
 		Ok(())
 	}
 
 	pub fn is_on(&self, now: &NaiveTime) -> bool {
-		self.active_schedule.is_on(now)
+		if let Some(active_schedule) = &self.active_schedule {
+			active_schedule.is_on(now)
+		} else {
+			false
+		}
 	}
 
 	pub fn get_next_time(&self, now: &NaiveTime) -> Option<NaiveTime> {
-		self.active_schedule.get_next_time(now)
+		if let Some(active_schedule) = &self.active_schedule {
+			active_schedule.get_next_time(now)
+		} else {
+			None
+		}
 	}
 
 	pub fn check_pulsing(&mut self, now: &Instant) -> Option<Instant> {
@@ -109,7 +114,7 @@ impl Relay {
 	pub fn reload_active_schedule(&mut self, weekday: Weekday) {
 		if let Some(schedule) = &self.override_schedule {
 			if self.override_schedule_weekday == weekday {
-				self.active_schedule = schedule.clone();
+				self.active_schedule = Some(schedule.clone());
 				return;
 			}
 			if self.override_schedule_weekday != weekday {
@@ -117,11 +122,11 @@ impl Relay {
 			}
 		}
 
-		self.active_schedule = self.schedules.get(weekday as usize).unwrap().clone()
+		self.active_schedule = Some(self.schedules.get(weekday as usize).unwrap().clone())
 	}
 
 	pub fn apply_state(&mut self, state: &RelayState) {
-		self.active_schedule = state.active_schedule.clone();
+		self.active_schedule.clone_from(&state.active_schedule);
 		self.override_schedule.clone_from(&state.override_schedule);
 		self.is_on = state.is_on;
 	}
diff --git a/src/types/relay_state.rs b/src/types/relay_state.rs
index f4a2e3a..e215253 100644
--- a/src/types/relay_state.rs
+++ b/src/types/relay_state.rs
@@ -4,7 +4,7 @@ use crate::models::Relay;
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct RelayState {
-    pub active_schedule: DbSchedule,
+    pub active_schedule: Option<DbSchedule>,
     pub override_schedule: Option<DbSchedule>,
     pub is_on: Option<bool>
 }

From 277b1592004d6743d034310c9a0bc9022bdaa202 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Thu, 30 May 2024 02:37:37 +0200
Subject: [PATCH 13/18] Add setting to change "midnight" of day

---
 src/db/junction_relay_schedule.rs | 11 +++++------
 src/db/relays.rs                  |  4 +---
 src/models/relay.rs               |  8 ++++----
 src/types/emgauwa_now.rs          | 11 ++++++-----
 src/types/mod.rs                  |  2 --
 src/utils.rs                      | 22 ++++++++++++++--------
 6 files changed, 30 insertions(+), 28 deletions(-)

diff --git a/src/db/junction_relay_schedule.rs b/src/db/junction_relay_schedule.rs
index f081a54..b8e0611 100644
--- a/src/db/junction_relay_schedule.rs
+++ b/src/db/junction_relay_schedule.rs
@@ -5,11 +5,10 @@ use sqlx::Sqlite;
 
 use crate::db::{DbRelay, DbSchedule};
 use crate::errors::DatabaseError;
-use crate::types::Weekday;
 
 pub struct DbJunctionRelaySchedule {
 	pub id: i64,
-	pub weekday: Weekday,
+	pub weekday: i64,
 	pub relay_id: i64,
 	pub schedule_id: i64,
 }
@@ -32,7 +31,7 @@ impl DbJunctionRelaySchedule {
 	pub async fn get_junction_by_relay_and_weekday(
 		conn: &mut PoolConnection<Sqlite>,
 		relay: &DbRelay,
-		weekday: Weekday,
+		weekday: i64,
 	) -> Result<Option<DbJunctionRelaySchedule>, DatabaseError> {
 		sqlx::query_as!(
 			DbJunctionRelaySchedule,
@@ -65,7 +64,7 @@ impl DbJunctionRelaySchedule {
 	pub async fn get_schedule(
 		conn: &mut PoolConnection<Sqlite>,
 		relay: &DbRelay,
-		weekday: Weekday,
+		weekday: i64,
 	) -> Result<Option<DbSchedule>, DatabaseError> {
 		sqlx::query_as!(
 			DbSchedule,
@@ -101,7 +100,7 @@ impl DbJunctionRelaySchedule {
 		conn: &mut PoolConnection<Sqlite>,
 		relay: &DbRelay,
 		schedule: &DbSchedule,
-		weekday: Weekday,
+		weekday: i64,
 	) -> Result<DbJunctionRelaySchedule, DatabaseError> {
 		match Self::get_junction_by_relay_and_weekday(conn, relay, weekday).await? {
 			None => sqlx::query_as!(
@@ -139,7 +138,7 @@ impl DbJunctionRelaySchedule {
 		schedules: Vec<&DbSchedule>,
 	) -> Result<(), DatabaseError> {
 		for (weekday, schedule) in schedules.iter().enumerate() {
-			Self::set_schedule(conn, relay, schedule, weekday as Weekday).await?;
+			Self::set_schedule(conn, relay, schedule, weekday as i64).await?;
 		}
 		Ok(())
 	}
diff --git a/src/db/relays.rs b/src/db/relays.rs
index 9045002..8fe39e6 100644
--- a/src/db/relays.rs
+++ b/src/db/relays.rs
@@ -4,10 +4,8 @@ use serde_derive::{Deserialize, Serialize};
 use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
-use crate::db::{DbController, DbJunctionRelaySchedule, DbJunctionTag, DbSchedule, DbTag};
+use crate::db::{DbController, DbJunctionTag, DbTag};
 use crate::errors::DatabaseError;
-use crate::types::Weekday;
-use crate::utils;
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct DbRelay {
diff --git a/src/models/relay.rs b/src/models/relay.rs
index bc2b48b..b67c4d3 100644
--- a/src/models/relay.rs
+++ b/src/models/relay.rs
@@ -1,6 +1,6 @@
 use std::time::Instant;
 
-use chrono::NaiveTime;
+use chrono::{NaiveTime, Weekday};
 use futures::executor::block_on;
 use serde_derive::{Deserialize, Serialize};
 use sqlx::pool::PoolConnection;
@@ -9,7 +9,7 @@ use sqlx::Sqlite;
 use crate::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule};
 use crate::errors::DatabaseError;
 use crate::models::FromDbModel;
-use crate::types::{EmgauwaUid, RelayState, Weekday};
+use crate::types::{EmgauwaUid, RelayState};
 use crate::utils;
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
@@ -29,7 +29,7 @@ pub struct Relay {
 	pub pulsing: Option<Instant>,
 	#[serde(
 		skip,
-		default = "utils::get_weekday",
+		default = "utils::default_weekday",
 	)]
 	pub override_schedule_weekday: Weekday,
 }
@@ -68,7 +68,7 @@ impl FromDbModel for Relay {
 			is_on,
 			tags,
 			pulsing: None,
-			override_schedule_weekday: Weekday::default(),
+			override_schedule_weekday: utils::default_weekday(),
 		})
 	}
 }
diff --git a/src/types/emgauwa_now.rs b/src/types/emgauwa_now.rs
index 40f4907..315be8e 100644
--- a/src/types/emgauwa_now.rs
+++ b/src/types/emgauwa_now.rs
@@ -1,26 +1,27 @@
 use std::time::Instant;
 
-use chrono::{Local, NaiveTime, Timelike};
+use chrono::{Local, NaiveTime, Timelike, Weekday};
 
-use crate::types::Weekday;
 use crate::utils;
 
 pub struct EmgauwaNow {
 	pub time: NaiveTime,
 	pub instant: Instant,
 	pub weekday: Weekday,
+	pub midnight: NaiveTime,
 }
 
 impl EmgauwaNow {
-	pub fn now() -> EmgauwaNow {
+	pub fn now(midnight: &NaiveTime) -> EmgauwaNow {
 		EmgauwaNow {
 			time: Local::now().time(),
 			instant: Instant::now(),
-			weekday: utils::get_weekday(),
+			weekday: utils::get_weekday(midnight),
+			midnight: *midnight,
 		}
 	}
 
-	pub fn time_in_s(&self) -> u32 {
+	pub fn num_seconds_from_midnight(&self) -> u32 {
 		self.time.num_seconds_from_midnight()
 	}
 }
diff --git a/src/types/mod.rs b/src/types/mod.rs
index 44d116c..8325110 100644
--- a/src/types/mod.rs
+++ b/src/types/mod.rs
@@ -16,8 +16,6 @@ use crate::db::DbSchedule;
 use crate::errors::EmgauwaError;
 use crate::models::{Controller, Relay};
 
-pub type Weekday = i64;
-
 #[derive(Debug, Serialize, Deserialize, Message)]
 #[rtype(result = "Result<(), EmgauwaError>")]
 pub enum ControllerWsAction {
diff --git a/src/utils.rs b/src/utils.rs
index 3b20d9a..67e705b 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -2,13 +2,13 @@ use std::ffi::CString;
 use std::io::{Error, ErrorKind};
 use std::str::FromStr;
 
-use chrono::Datelike;
+use chrono::{Datelike, NaiveTime, Weekday};
 use log::LevelFilter;
 use simple_logger::SimpleLogger;
 
 use crate::errors::EmgauwaError;
 use crate::settings::Permissions;
-use crate::types::{RelayStates, Weekday};
+use crate::types::RelayStates;
 
 pub fn init_logging(level: &str) -> Result<(), EmgauwaError> {
 	let log_level: LevelFilter = LevelFilter::from_str(level)
@@ -92,12 +92,18 @@ fn drop_privileges_user(user: &str) -> Result<(), Error> {
 	Ok(())
 }
 
-pub fn get_weekday() -> Weekday {
-	(chrono::offset::Local::now()
-		.date_naive()
-		.weekday()
-		.number_from_monday()
-		- 1) as Weekday
+pub fn get_weekday(midnight: &NaiveTime) -> Weekday {
+	let dt = chrono::offset::Local::now().naive_local();
+	let weekday = dt.weekday();
+	if dt.time().lt(midnight) {
+		weekday.pred()
+	} else {
+		weekday
+	}
+}
+
+pub fn default_weekday() -> Weekday {
+	Weekday::Mon
 }
 
 pub fn printable_relay_states(relay_states: &RelayStates) -> String {

From d4ff664f742879c6c2734b2aba2266b4e87f8f95 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Tue, 11 Jun 2024 14:10:25 +0200
Subject: [PATCH 14/18] Add relay view to faster load controller_uid

---
 ...90be8091daa97b0af548b6ebf60cdc03dfba.json} | 10 ++++-
 ...b3255a5426d54901f4f3aa96ad152b05ffd0.json} | 10 ++++-
 ...f5cdd699bfe7600389e37ca980b6fad12bb5.json} | 10 ++++-
 ...085677c09be8be903804120c2d52579afdbb.json} | 10 ++++-
 ...b9bbb0e6c203b721d29cf9149f60bfdd93465.json | 38 ----------------
 ...a27f690020f89958dfc8dd6044a31afdb31d.json} | 10 ++++-
 ...e63322d3bb1579e52d0f053307e24bd039ef9.json | 44 +++++++++++++++++++
 ...4ca2ef93958f9975c585c6de3c437782995d.json} | 10 ++++-
 ...552812362c3141738b60bc63d673a7f552506.json | 12 +++++
 .../20240611000000_add_relays_view.down.sql   |  1 +
 .../20240611000000_add_relays_view.up.sql     |  8 ++++
 src/db/controllers.rs                         |  2 +-
 src/db/junction_relay_schedule.rs             |  4 +-
 src/db/relays.rs                              | 32 +++++++++-----
 src/models/controller.rs                      |  4 +-
 src/models/relay.rs                           | 22 +++-------
 16 files changed, 146 insertions(+), 81 deletions(-)
 rename .sqlx/{query-c9437ff0c3014b269dcb21304fbad12237b9cb69ea6aa4686df6d5262065faa2.json => query-2b5ac2227f48be1483f4097da6f890be8091daa97b0af548b6ebf60cdc03dfba.json} (70%)
 rename .sqlx/{query-2551c285e3e223311cff8e32022d8b11e95d56b2f166326301a0b6722fc1fd44.json => query-493ad91be9ce523e9d0f03f5caa9b3255a5426d54901f4f3aa96ad152b05ffd0.json} (63%)
 rename .sqlx/{query-e94ef5bc8b267d493375bb371dcfb7b09f6355ecbc8b6e1085d5f2f9a08cac3f.json => query-4a99db9678cf8d1bdb082c4a13a1f5cdd699bfe7600389e37ca980b6fad12bb5.json} (68%)
 rename .sqlx/{query-ee7da56331bece2efe21b55dbd5f420d3abb08358a1abe301dc7e08693fbef4d.json => query-5056b625241d9cbe63d98e00ac39085677c09be8be903804120c2d52579afdbb.json} (71%)
 delete mode 100644 .sqlx/query-5865f27b97487b6dfd956a3d260b9bbb0e6c203b721d29cf9149f60bfdd93465.json
 rename .sqlx/{query-b41855e635ac409559fa63cba4c1285034c573b86e3193da3995606dee412153.json => query-9224ad423f2c86f3d95f2b0b7d99a27f690020f89958dfc8dd6044a31afdb31d.json} (66%)
 create mode 100644 .sqlx/query-adbce2c94ac0b54d0826b28f99fe63322d3bb1579e52d0f053307e24bd039ef9.json
 rename .sqlx/{query-4f5408e64f5e6a8dd923c3b147f993ce9e4cafc90204b06977481130ec06d111.json => query-d57c388bf6c26fe6cadad35d0f254ca2ef93958f9975c585c6de3c437782995d.json} (69%)
 create mode 100644 .sqlx/query-f85f0a96bb98d20e47677b0679d552812362c3141738b60bc63d673a7f552506.json
 create mode 100644 migrations/20240611000000_add_relays_view.down.sql
 create mode 100644 migrations/20240611000000_add_relays_view.up.sql

diff --git a/.sqlx/query-c9437ff0c3014b269dcb21304fbad12237b9cb69ea6aa4686df6d5262065faa2.json b/.sqlx/query-2b5ac2227f48be1483f4097da6f890be8091daa97b0af548b6ebf60cdc03dfba.json
similarity index 70%
rename from .sqlx/query-c9437ff0c3014b269dcb21304fbad12237b9cb69ea6aa4686df6d5262065faa2.json
rename to .sqlx/query-2b5ac2227f48be1483f4097da6f890be8091daa97b0af548b6ebf60cdc03dfba.json
index 10fe1a3..5b01214 100644
--- a/.sqlx/query-c9437ff0c3014b269dcb21304fbad12237b9cb69ea6aa4686df6d5262065faa2.json
+++ b/.sqlx/query-2b5ac2227f48be1483f4097da6f890be8091daa97b0af548b6ebf60cdc03dfba.json
@@ -1,6 +1,6 @@
 {
   "db_name": "SQLite",
-  "query": "SELECT * FROM relays WHERE controller_id = ?",
+  "query": "SELECT * FROM v_relays WHERE id = ?",
   "describe": {
     "columns": [
       {
@@ -22,6 +22,11 @@
         "name": "controller_id",
         "ordinal": 3,
         "type_info": "Int64"
+      },
+      {
+        "name": "controller_uid",
+        "ordinal": 4,
+        "type_info": "Blob"
       }
     ],
     "parameters": {
@@ -31,8 +36,9 @@
       false,
       false,
       false,
+      false,
       false
     ]
   },
-  "hash": "c9437ff0c3014b269dcb21304fbad12237b9cb69ea6aa4686df6d5262065faa2"
+  "hash": "2b5ac2227f48be1483f4097da6f890be8091daa97b0af548b6ebf60cdc03dfba"
 }
diff --git a/.sqlx/query-2551c285e3e223311cff8e32022d8b11e95d56b2f166326301a0b6722fc1fd44.json b/.sqlx/query-493ad91be9ce523e9d0f03f5caa9b3255a5426d54901f4f3aa96ad152b05ffd0.json
similarity index 63%
rename from .sqlx/query-2551c285e3e223311cff8e32022d8b11e95d56b2f166326301a0b6722fc1fd44.json
rename to .sqlx/query-493ad91be9ce523e9d0f03f5caa9b3255a5426d54901f4f3aa96ad152b05ffd0.json
index f6f2724..a480728 100644
--- a/.sqlx/query-2551c285e3e223311cff8e32022d8b11e95d56b2f166326301a0b6722fc1fd44.json
+++ b/.sqlx/query-493ad91be9ce523e9d0f03f5caa9b3255a5426d54901f4f3aa96ad152b05ffd0.json
@@ -1,6 +1,6 @@
 {
   "db_name": "SQLite",
-  "query": "SELECT relays.* FROM relays INNER JOIN junction_relay_schedule\n\t\t\tON junction_relay_schedule.relay_id = relays.id\n\t\t\tWHERE junction_relay_schedule.schedule_id = ?\n\t\t\tORDER BY junction_relay_schedule.weekday",
+  "query": "SELECT v_relays.* FROM v_relays INNER JOIN junction_tag ON junction_tag.relay_id = v_relays.id WHERE junction_tag.tag_id = ?",
   "describe": {
     "columns": [
       {
@@ -22,6 +22,11 @@
         "name": "controller_id",
         "ordinal": 3,
         "type_info": "Int64"
+      },
+      {
+        "name": "controller_uid",
+        "ordinal": 4,
+        "type_info": "Blob"
       }
     ],
     "parameters": {
@@ -31,8 +36,9 @@
       false,
       false,
       false,
+      false,
       false
     ]
   },
-  "hash": "2551c285e3e223311cff8e32022d8b11e95d56b2f166326301a0b6722fc1fd44"
+  "hash": "493ad91be9ce523e9d0f03f5caa9b3255a5426d54901f4f3aa96ad152b05ffd0"
 }
diff --git a/.sqlx/query-e94ef5bc8b267d493375bb371dcfb7b09f6355ecbc8b6e1085d5f2f9a08cac3f.json b/.sqlx/query-4a99db9678cf8d1bdb082c4a13a1f5cdd699bfe7600389e37ca980b6fad12bb5.json
similarity index 68%
rename from .sqlx/query-e94ef5bc8b267d493375bb371dcfb7b09f6355ecbc8b6e1085d5f2f9a08cac3f.json
rename to .sqlx/query-4a99db9678cf8d1bdb082c4a13a1f5cdd699bfe7600389e37ca980b6fad12bb5.json
index 391ca72..8776c67 100644
--- a/.sqlx/query-e94ef5bc8b267d493375bb371dcfb7b09f6355ecbc8b6e1085d5f2f9a08cac3f.json
+++ b/.sqlx/query-4a99db9678cf8d1bdb082c4a13a1f5cdd699bfe7600389e37ca980b6fad12bb5.json
@@ -1,6 +1,6 @@
 {
   "db_name": "SQLite",
-  "query": "SELECT relay.* FROM relays AS relay INNER JOIN junction_tag ON junction_tag.relay_id = relay.id WHERE junction_tag.tag_id = ?",
+  "query": "SELECT * FROM v_relays WHERE v_relays.controller_id = ?",
   "describe": {
     "columns": [
       {
@@ -22,6 +22,11 @@
         "name": "controller_id",
         "ordinal": 3,
         "type_info": "Int64"
+      },
+      {
+        "name": "controller_uid",
+        "ordinal": 4,
+        "type_info": "Blob"
       }
     ],
     "parameters": {
@@ -31,8 +36,9 @@
       false,
       false,
       false,
+      false,
       false
     ]
   },
-  "hash": "e94ef5bc8b267d493375bb371dcfb7b09f6355ecbc8b6e1085d5f2f9a08cac3f"
+  "hash": "4a99db9678cf8d1bdb082c4a13a1f5cdd699bfe7600389e37ca980b6fad12bb5"
 }
diff --git a/.sqlx/query-ee7da56331bece2efe21b55dbd5f420d3abb08358a1abe301dc7e08693fbef4d.json b/.sqlx/query-5056b625241d9cbe63d98e00ac39085677c09be8be903804120c2d52579afdbb.json
similarity index 71%
rename from .sqlx/query-ee7da56331bece2efe21b55dbd5f420d3abb08358a1abe301dc7e08693fbef4d.json
rename to .sqlx/query-5056b625241d9cbe63d98e00ac39085677c09be8be903804120c2d52579afdbb.json
index c4ec08d..fa990c6 100644
--- a/.sqlx/query-ee7da56331bece2efe21b55dbd5f420d3abb08358a1abe301dc7e08693fbef4d.json
+++ b/.sqlx/query-5056b625241d9cbe63d98e00ac39085677c09be8be903804120c2d52579afdbb.json
@@ -1,6 +1,6 @@
 {
   "db_name": "SQLite",
-  "query": "SELECT * FROM relays",
+  "query": "SELECT * FROM v_relays",
   "describe": {
     "columns": [
       {
@@ -22,6 +22,11 @@
         "name": "controller_id",
         "ordinal": 3,
         "type_info": "Int64"
+      },
+      {
+        "name": "controller_uid",
+        "ordinal": 4,
+        "type_info": "Blob"
       }
     ],
     "parameters": {
@@ -31,8 +36,9 @@
       false,
       false,
       false,
+      false,
       false
     ]
   },
-  "hash": "ee7da56331bece2efe21b55dbd5f420d3abb08358a1abe301dc7e08693fbef4d"
+  "hash": "5056b625241d9cbe63d98e00ac39085677c09be8be903804120c2d52579afdbb"
 }
diff --git a/.sqlx/query-5865f27b97487b6dfd956a3d260b9bbb0e6c203b721d29cf9149f60bfdd93465.json b/.sqlx/query-5865f27b97487b6dfd956a3d260b9bbb0e6c203b721d29cf9149f60bfdd93465.json
deleted file mode 100644
index 67ea7e8..0000000
--- a/.sqlx/query-5865f27b97487b6dfd956a3d260b9bbb0e6c203b721d29cf9149f60bfdd93465.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  "db_name": "SQLite",
-  "query": "INSERT INTO relays (name, number, controller_id) VALUES (?, ?, ?) RETURNING *",
-  "describe": {
-    "columns": [
-      {
-        "name": "id",
-        "ordinal": 0,
-        "type_info": "Int64"
-      },
-      {
-        "name": "name",
-        "ordinal": 1,
-        "type_info": "Text"
-      },
-      {
-        "name": "number",
-        "ordinal": 2,
-        "type_info": "Int64"
-      },
-      {
-        "name": "controller_id",
-        "ordinal": 3,
-        "type_info": "Int64"
-      }
-    ],
-    "parameters": {
-      "Right": 3
-    },
-    "nullable": [
-      false,
-      false,
-      false,
-      false
-    ]
-  },
-  "hash": "5865f27b97487b6dfd956a3d260b9bbb0e6c203b721d29cf9149f60bfdd93465"
-}
diff --git a/.sqlx/query-b41855e635ac409559fa63cba4c1285034c573b86e3193da3995606dee412153.json b/.sqlx/query-9224ad423f2c86f3d95f2b0b7d99a27f690020f89958dfc8dd6044a31afdb31d.json
similarity index 66%
rename from .sqlx/query-b41855e635ac409559fa63cba4c1285034c573b86e3193da3995606dee412153.json
rename to .sqlx/query-9224ad423f2c86f3d95f2b0b7d99a27f690020f89958dfc8dd6044a31afdb31d.json
index 94419bd..80fbca7 100644
--- a/.sqlx/query-b41855e635ac409559fa63cba4c1285034c573b86e3193da3995606dee412153.json
+++ b/.sqlx/query-9224ad423f2c86f3d95f2b0b7d99a27f690020f89958dfc8dd6044a31afdb31d.json
@@ -1,6 +1,6 @@
 {
   "db_name": "SQLite",
-  "query": "SELECT * FROM relays WHERE controller_id = ? AND number = ?",
+  "query": "SELECT * FROM v_relays WHERE v_relays.controller_id = ? AND v_relays.number = ?",
   "describe": {
     "columns": [
       {
@@ -22,6 +22,11 @@
         "name": "controller_id",
         "ordinal": 3,
         "type_info": "Int64"
+      },
+      {
+        "name": "controller_uid",
+        "ordinal": 4,
+        "type_info": "Blob"
       }
     ],
     "parameters": {
@@ -31,8 +36,9 @@
       false,
       false,
       false,
+      false,
       false
     ]
   },
-  "hash": "b41855e635ac409559fa63cba4c1285034c573b86e3193da3995606dee412153"
+  "hash": "9224ad423f2c86f3d95f2b0b7d99a27f690020f89958dfc8dd6044a31afdb31d"
 }
diff --git a/.sqlx/query-adbce2c94ac0b54d0826b28f99fe63322d3bb1579e52d0f053307e24bd039ef9.json b/.sqlx/query-adbce2c94ac0b54d0826b28f99fe63322d3bb1579e52d0f053307e24bd039ef9.json
new file mode 100644
index 0000000..a91e2ae
--- /dev/null
+++ b/.sqlx/query-adbce2c94ac0b54d0826b28f99fe63322d3bb1579e52d0f053307e24bd039ef9.json
@@ -0,0 +1,44 @@
+{
+  "db_name": "SQLite",
+  "query": "SELECT v_relays.* FROM v_relays INNER JOIN junction_relay_schedule\n\t\t\tON junction_relay_schedule.relay_id = v_relays.id\n\t\t\tWHERE junction_relay_schedule.schedule_id = ?\n\t\t\tORDER BY junction_relay_schedule.weekday",
+  "describe": {
+    "columns": [
+      {
+        "name": "id",
+        "ordinal": 0,
+        "type_info": "Int64"
+      },
+      {
+        "name": "name",
+        "ordinal": 1,
+        "type_info": "Text"
+      },
+      {
+        "name": "number",
+        "ordinal": 2,
+        "type_info": "Int64"
+      },
+      {
+        "name": "controller_id",
+        "ordinal": 3,
+        "type_info": "Int64"
+      },
+      {
+        "name": "controller_uid",
+        "ordinal": 4,
+        "type_info": "Blob"
+      }
+    ],
+    "parameters": {
+      "Right": 1
+    },
+    "nullable": [
+      false,
+      false,
+      false,
+      false,
+      false
+    ]
+  },
+  "hash": "adbce2c94ac0b54d0826b28f99fe63322d3bb1579e52d0f053307e24bd039ef9"
+}
diff --git a/.sqlx/query-4f5408e64f5e6a8dd923c3b147f993ce9e4cafc90204b06977481130ec06d111.json b/.sqlx/query-d57c388bf6c26fe6cadad35d0f254ca2ef93958f9975c585c6de3c437782995d.json
similarity index 69%
rename from .sqlx/query-4f5408e64f5e6a8dd923c3b147f993ce9e4cafc90204b06977481130ec06d111.json
rename to .sqlx/query-d57c388bf6c26fe6cadad35d0f254ca2ef93958f9975c585c6de3c437782995d.json
index 78e4867..00d044d 100644
--- a/.sqlx/query-4f5408e64f5e6a8dd923c3b147f993ce9e4cafc90204b06977481130ec06d111.json
+++ b/.sqlx/query-d57c388bf6c26fe6cadad35d0f254ca2ef93958f9975c585c6de3c437782995d.json
@@ -1,6 +1,6 @@
 {
   "db_name": "SQLite",
-  "query": "SELECT * FROM relays WHERE id = ?",
+  "query": "SELECT * FROM v_relays WHERE v_relays.id = ?",
   "describe": {
     "columns": [
       {
@@ -22,6 +22,11 @@
         "name": "controller_id",
         "ordinal": 3,
         "type_info": "Int64"
+      },
+      {
+        "name": "controller_uid",
+        "ordinal": 4,
+        "type_info": "Blob"
       }
     ],
     "parameters": {
@@ -31,8 +36,9 @@
       false,
       false,
       false,
+      false,
       false
     ]
   },
-  "hash": "4f5408e64f5e6a8dd923c3b147f993ce9e4cafc90204b06977481130ec06d111"
+  "hash": "d57c388bf6c26fe6cadad35d0f254ca2ef93958f9975c585c6de3c437782995d"
 }
diff --git a/.sqlx/query-f85f0a96bb98d20e47677b0679d552812362c3141738b60bc63d673a7f552506.json b/.sqlx/query-f85f0a96bb98d20e47677b0679d552812362c3141738b60bc63d673a7f552506.json
new file mode 100644
index 0000000..cc26013
--- /dev/null
+++ b/.sqlx/query-f85f0a96bb98d20e47677b0679d552812362c3141738b60bc63d673a7f552506.json
@@ -0,0 +1,12 @@
+{
+  "db_name": "SQLite",
+  "query": "INSERT INTO relays (name, number, controller_id) VALUES (?, ?, ?)",
+  "describe": {
+    "columns": [],
+    "parameters": {
+      "Right": 3
+    },
+    "nullable": []
+  },
+  "hash": "f85f0a96bb98d20e47677b0679d552812362c3141738b60bc63d673a7f552506"
+}
diff --git a/migrations/20240611000000_add_relays_view.down.sql b/migrations/20240611000000_add_relays_view.down.sql
new file mode 100644
index 0000000..093f473
--- /dev/null
+++ b/migrations/20240611000000_add_relays_view.down.sql
@@ -0,0 +1 @@
+DROP VIEW v_relays;
\ No newline at end of file
diff --git a/migrations/20240611000000_add_relays_view.up.sql b/migrations/20240611000000_add_relays_view.up.sql
new file mode 100644
index 0000000..b8fab88
--- /dev/null
+++ b/migrations/20240611000000_add_relays_view.up.sql
@@ -0,0 +1,8 @@
+CREATE VIEW v_relays
+AS
+SELECT
+	relays.*,
+	controllers.uid AS controller_uid
+FROM
+	relays
+INNER JOIN controllers ON controllers.id = relays.controller_id;
diff --git a/src/db/controllers.rs b/src/db/controllers.rs
index bf0a1f0..ade379e 100644
--- a/src/db/controllers.rs
+++ b/src/db/controllers.rs
@@ -158,7 +158,7 @@ impl DbController {
 	) -> Result<Vec<DbRelay>, DatabaseError> {
 		sqlx::query_as!(
 			DbRelay,
-			"SELECT * FROM relays WHERE controller_id = ?",
+			"SELECT * FROM v_relays WHERE v_relays.controller_id = ?",
 			self.id
 		)
 		.fetch_all(conn.deref_mut())
diff --git a/src/db/junction_relay_schedule.rs b/src/db/junction_relay_schedule.rs
index b8e0611..74dc98e 100644
--- a/src/db/junction_relay_schedule.rs
+++ b/src/db/junction_relay_schedule.rs
@@ -50,8 +50,8 @@ impl DbJunctionRelaySchedule {
 	) -> Result<Vec<DbRelay>, DatabaseError> {
 		sqlx::query_as!(
 			DbRelay,
-			r#"SELECT relays.* FROM relays INNER JOIN junction_relay_schedule
-			ON junction_relay_schedule.relay_id = relays.id
+			r#"SELECT v_relays.* FROM v_relays INNER JOIN junction_relay_schedule
+			ON junction_relay_schedule.relay_id = v_relays.id
 			WHERE junction_relay_schedule.schedule_id = ?
 			ORDER BY junction_relay_schedule.weekday"#,
 			schedule.id
diff --git a/src/db/relays.rs b/src/db/relays.rs
index 8fe39e6..ef62382 100644
--- a/src/db/relays.rs
+++ b/src/db/relays.rs
@@ -6,6 +6,7 @@ use sqlx::Sqlite;
 
 use crate::db::{DbController, DbJunctionTag, DbTag};
 use crate::errors::DatabaseError;
+use crate::types::EmgauwaUid;
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct DbRelay {
@@ -13,13 +14,15 @@ pub struct DbRelay {
 	pub id: i64,
 	pub name: String,
 	pub number: i64,
+	#[serde(rename = "controller_id")]
+	pub controller_uid: EmgauwaUid,
 	#[serde(skip)]
 	pub controller_id: i64,
 }
 
 impl DbRelay {
 	pub async fn get_all(conn: &mut PoolConnection<Sqlite>) -> Result<Vec<DbRelay>, DatabaseError> {
-		sqlx::query_as!(DbRelay, "SELECT * FROM relays")
+		sqlx::query_as!(DbRelay, "SELECT * FROM v_relays")
 			.fetch_all(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
@@ -29,7 +32,7 @@ impl DbRelay {
 		conn: &mut PoolConnection<Sqlite>,
 		id: i64,
 	) -> Result<Option<DbRelay>, DatabaseError> {
-		sqlx::query_as!(DbRelay, "SELECT * FROM relays WHERE id = ?", id)
+		sqlx::query_as!(DbRelay, "SELECT * FROM v_relays WHERE v_relays.id = ?", id)
 			.fetch_optional(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
@@ -42,7 +45,7 @@ impl DbRelay {
 	) -> Result<Option<DbRelay>, DatabaseError> {
 		sqlx::query_as!(
 			DbRelay,
-			"SELECT * FROM relays WHERE controller_id = ? AND number = ?",
+			"SELECT * FROM v_relays WHERE v_relays.controller_id = ? AND v_relays.number = ?",
 			controller.id,
 			number
 		)
@@ -70,7 +73,7 @@ impl DbRelay {
 		conn: &mut PoolConnection<Sqlite>,
 		tag: &DbTag,
 	) -> Result<Vec<DbRelay>, DatabaseError> {
-		sqlx::query_as!(DbRelay, "SELECT relay.* FROM relays AS relay INNER JOIN junction_tag ON junction_tag.relay_id = relay.id WHERE junction_tag.tag_id = ?", tag.id)
+		sqlx::query_as!(DbRelay, "SELECT v_relays.* FROM v_relays INNER JOIN junction_tag ON junction_tag.relay_id = v_relays.id WHERE junction_tag.tag_id = ?", tag.id)
 			.fetch_all(conn.deref_mut())
 			.await
 			.map_err(DatabaseError::from)
@@ -82,16 +85,25 @@ impl DbRelay {
 		new_number: i64,
 		new_controller: &DbController,
 	) -> Result<DbRelay, DatabaseError> {
-		sqlx::query_as!(
-			DbRelay,
-			"INSERT INTO relays (name, number, controller_id) VALUES (?, ?, ?) RETURNING *",
+		let result = sqlx::query!(
+			"INSERT INTO relays (name, number, controller_id) VALUES (?, ?, ?)",
 			new_name,
 			new_number,
 			new_controller.id,
 		)
-		.fetch_optional(conn.deref_mut())
-		.await?
-		.ok_or(DatabaseError::InsertGetError)
+			.execute(conn.deref_mut())
+			.await?;
+
+		let last_insert_id = result.last_insert_rowid();
+
+		sqlx::query_as!(
+			DbRelay,
+			"SELECT * FROM v_relays WHERE id = ?",
+			last_insert_id
+		)
+			.fetch_one(conn.deref_mut())
+			.await
+			.map_err(DatabaseError::from)
 	}
 
 	pub async fn delete(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
diff --git a/src/models/controller.rs b/src/models/controller.rs
index 6bb04e3..753749a 100644
--- a/src/models/controller.rs
+++ b/src/models/controller.rs
@@ -9,7 +9,7 @@ use sqlx::Sqlite;
 
 use crate::db::DbController;
 use crate::errors::{DatabaseError, EmgauwaError};
-use crate::models::{convert_db_list_cache, FromDbModel, Relay};
+use crate::models::{convert_db_list, FromDbModel, Relay};
 use crate::types::{EmgauwaNow, RelayState, RelayStates};
 
 #[derive(Serialize, Deserialize, Debug, Clone, MessageResponse)]
@@ -28,7 +28,7 @@ impl FromDbModel for Controller {
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError> {
 		let relays_db = block_on(db_model.get_relays(conn))?;
-		let cache = convert_db_list_cache(conn, relays_db, db_model.clone())?;
+		let cache = convert_db_list(conn, relays_db)?;
 		Self::from_db_model_cache(conn, db_model, cache)
 	}
 
diff --git a/src/models/relay.rs b/src/models/relay.rs
index b67c4d3..5cb97d1 100644
--- a/src/models/relay.rs
+++ b/src/models/relay.rs
@@ -6,18 +6,16 @@ use serde_derive::{Deserialize, Serialize};
 use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
-use crate::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule};
+use crate::db::{DbJunctionRelaySchedule, DbRelay, DbSchedule};
 use crate::errors::DatabaseError;
 use crate::models::FromDbModel;
-use crate::types::{EmgauwaUid, RelayState};
+use crate::types::RelayState;
 use crate::utils;
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
 pub struct Relay {
 	#[serde(flatten)]
 	pub r: DbRelay,
-	pub controller: DbController,
-	pub controller_id: EmgauwaUid,
 	pub schedules: Vec<DbSchedule>,
 	pub active_schedule: Option<DbSchedule>,
 	pub override_schedule: Option<DbSchedule>,
@@ -27,32 +25,27 @@ pub struct Relay {
 	// for internal use only.
 	#[serde(skip)]
 	pub pulsing: Option<Instant>,
-	#[serde(
-		skip,
-		default = "utils::default_weekday",
-	)]
+	#[serde(skip, default = "utils::default_weekday")]
 	pub override_schedule_weekday: Weekday,
 }
 
 impl FromDbModel for Relay {
 	type DbModel = DbRelay;
-	type DbModelCache = DbController;
+	type DbModelCache = ();
 
 	fn from_db_model(
 		conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
 	) -> Result<Self, DatabaseError> {
-		let cache = block_on(db_model.get_controller(conn))?;
-		Self::from_db_model_cache(conn, db_model, cache)
+		Self::from_db_model_cache(conn, db_model, ())
 	}
 
 	fn from_db_model_cache(
 		conn: &mut PoolConnection<Sqlite>,
 		db_model: Self::DbModel,
-		cache: Self::DbModelCache,
+		_cache: Self::DbModelCache,
 	) -> Result<Self, DatabaseError> {
 		let tags = block_on(db_model.get_tags(conn))?;
-		let controller_id = cache.uid.clone();
 
 		let schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &db_model))?;
 
@@ -60,8 +53,6 @@ impl FromDbModel for Relay {
 
 		Ok(Relay {
 			r: db_model,
-			controller: cache,
-			controller_id,
 			schedules,
 			active_schedule: None,
 			override_schedule: None,
@@ -136,5 +127,4 @@ impl Relay {
 			self.apply_state(&stated_relay.into());
 		}
 	}
-
 }

From 41cc9e0622b69868eb13db843b1323337cb7a544 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Tue, 11 Jun 2024 17:46:35 +0200
Subject: [PATCH 15/18] Remove logging file from config (was never implemented)

---
 src/settings.rs | 2 --
 src/utils.rs    | 8 ++++----
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/src/settings.rs b/src/settings.rs
index bc966dd..842c527 100644
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -16,7 +16,6 @@ pub struct Server {
 #[allow(unused)]
 pub struct Logging {
 	pub level: String,
-	pub file: String,
 }
 
 #[derive(Clone, Debug, Deserialize, Default)]
@@ -40,7 +39,6 @@ impl Default for Logging {
 	fn default() -> Self {
 		Logging {
 			level: String::from("info"),
-			file: String::from("stdout"),
 		}
 	}
 }
diff --git a/src/utils.rs b/src/utils.rs
index 67e705b..00b066f 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -7,12 +7,12 @@ use log::LevelFilter;
 use simple_logger::SimpleLogger;
 
 use crate::errors::EmgauwaError;
-use crate::settings::Permissions;
+use crate::settings::{Logging, Permissions};
 use crate::types::RelayStates;
 
-pub fn init_logging(level: &str) -> Result<(), EmgauwaError> {
-	let log_level: LevelFilter = LevelFilter::from_str(level)
-		.map_err(|_| EmgauwaError::Other(format!("Invalid log level: {}", level)))?;
+pub fn init_logging(logging: &Logging) -> Result<(), EmgauwaError> {
+	let log_level: LevelFilter = LevelFilter::from_str(&logging.level)
+		.map_err(|_| EmgauwaError::Other(format!("Invalid log level: {}", logging.level)))?;
 	log::trace!("Log level set to {:?}", log_level);
 
 	SimpleLogger::new()

From 2f5bb538b2be531b9e5632a5eb9a104181bac022 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Mon, 28 Oct 2024 02:17:38 +0100
Subject: [PATCH 16/18] Remove unwrap when getting active schedule

---
 src/models/relay.rs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/models/relay.rs b/src/models/relay.rs
index 5cb97d1..0b3e16c 100644
--- a/src/models/relay.rs
+++ b/src/models/relay.rs
@@ -113,7 +113,9 @@ impl Relay {
 			}
 		}
 
-		self.active_schedule = Some(self.schedules.get(weekday as usize).unwrap().clone())
+		if let Some(schedule) = self.schedules.get(weekday as usize) {
+			self.active_schedule = Some(schedule.clone());
+		}
 	}
 
 	pub fn apply_state(&mut self, state: &RelayState) {

From e923ecb9d88061d2ef6f1342613315f1677551c7 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Wed, 25 Dec 2024 01:54:16 +0100
Subject: [PATCH 17/18] Add tests and fix is_on calculation

---
 src/db/mod.rs         |  3 ++
 src/db/model_utils.rs |  5 ++--
 src/lib.rs            | 65 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/src/db/mod.rs b/src/db/mod.rs
index d406c94..542772a 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -23,6 +23,9 @@ pub use relays::DbRelay;
 pub use schedules::{DbPeriods, DbSchedule};
 pub use tag::DbTag;
 
+#[cfg(test)]
+pub(crate) use model_utils::Period;
+
 use crate::errors::{DatabaseError, EmgauwaError};
 
 static MIGRATOR: Migrator = sqlx::migrate!(); // defaults to "./migrations"
diff --git a/src/db/model_utils.rs b/src/db/model_utils.rs
index db845bc..2421be2 100644
--- a/src/db/model_utils.rs
+++ b/src/db/model_utils.rs
@@ -63,12 +63,13 @@ impl Period {
 		let start_after_now = self.start.gt(now);
 		// add check for end time being 00:00 because end being 00:00 would cause end_after_now to always be false
 		// this will handle end like 24:00 and end_after_now will be true
+		// same for start_before_end
 		let end_after_now = self.end.gt(now) || self.end.eq(&NaiveTime::MIN);
-		let start_before_end = self.start.lt(&self.end);
+		let start_before_end = self.start.lt(&self.end) || self.end.eq(&NaiveTime::MIN);
 
 		match (start_after_now, end_after_now, start_before_end) {
 			(false, false, true) => false, // both before now; start before end means "normal" period before now
-			(false, false, false) => true, // both before now; end before start means "inversed" period around now
+			(false, false, false) => true, // both before now; end before start means "inverse" period around now
 			(true, false, _) => false,     // only start after now
 			(false, true, _) => true,      // only end after now
 			(true, true, true) => false,   // both after now but start first
diff --git a/src/lib.rs b/src/lib.rs
index f939d0c..540a8a1 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,3 +5,68 @@ pub mod models;
 pub mod settings;
 pub mod types;
 pub mod utils;
+
+#[cfg(test)]
+mod periods {
+    use chrono::NaiveTime;
+    use crate::db::Period;
+    use crate::types::EmgauwaNow;
+
+    const MIDNIGHT: NaiveTime = NaiveTime::MIN;
+
+    fn new_time(hour: u32, minute: u32) -> NaiveTime {
+        NaiveTime::from_hms_opt(hour, minute, 0).expect("Failed to create NaiveTime")
+    }
+    fn new_period(start_hour: u32, start_minute: u32, end_hour: u32, end_minute: u32) -> Period {
+        Period {
+            start: new_time(start_hour, start_minute),
+            end: new_time(end_hour, end_minute),
+        }
+    }
+
+    #[test]
+    fn always_on() {
+        let period = Period::new_on();
+        let now: EmgauwaNow = EmgauwaNow::now(&MIDNIGHT);
+        assert_eq!(period.is_always_on(), true);
+        assert_eq!(period.is_on(&MIDNIGHT), true);
+        assert_eq!(period.is_on(&new_time(12, 00)), true);
+        assert_eq!(period.is_on(&now.time), true);
+    }
+
+    #[test]
+    fn simple_period() {
+        let period = new_period(11, 00, 13, 00);
+        assert_eq!(period.is_always_on(), false);
+        assert_eq!(period.is_on(&MIDNIGHT), false);
+        assert_eq!(period.is_on(&new_time(10, 00)), false);
+        assert_eq!(period.is_on(&new_time(11, 00)), true);
+        assert_eq!(period.is_on(&new_time(12, 00)), true);
+        assert_eq!(period.is_on(&new_time(13, 00)), false);
+        assert_eq!(period.is_on(&new_time(14, 00)), false);
+    }
+
+    #[test]
+    fn to_midnight_period() {
+        let period = new_period(22, 00, 00, 00);
+        assert_eq!(period.is_always_on(), false);
+        assert_eq!(period.is_on(&MIDNIGHT), false);
+        assert_eq!(period.is_on(&new_time(21, 00)), false);
+        assert_eq!(period.is_on(&new_time(22, 00)), true);
+        assert_eq!(period.is_on(&new_time(23, 00)), true);
+        assert_eq!(period.is_on(&new_time(00, 00)), false);
+        assert_eq!(period.is_on(&new_time(01, 00)), false);
+    }
+
+    #[test]
+    fn from_midnight_period() {
+        let period = new_period(00, 00, 02, 00);
+        assert_eq!(period.is_always_on(), false);
+        assert_eq!(period.is_on(&MIDNIGHT), true);
+        assert_eq!(period.is_on(&new_time(23, 00)), false);
+        assert_eq!(period.is_on(&new_time(00, 00)), true);
+        assert_eq!(period.is_on(&new_time(01, 00)), true);
+        assert_eq!(period.is_on(&new_time(02, 00)), false);
+        assert_eq!(period.is_on(&new_time(03, 00)), false);
+    }
+}
\ No newline at end of file

From 066e9f7bf8e9379b22ccfc6ab7c3f18013fa561f Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Wed, 25 Dec 2024 19:49:54 +0100
Subject: [PATCH 18/18] Bump version

---
 Cargo.lock | 1157 ++++++++++++++++++++++++++++++++--------------------
 Cargo.toml |    5 +-
 2 files changed, 717 insertions(+), 445 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 1bee2db..f9fc559 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,14 +4,14 @@ version = 3
 
 [[package]]
 name = "actix"
-version = "0.13.3"
+version = "0.13.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb72882332b6d6282f428b77ba0358cb2687e61a6f6df6a6d3871e8a177c2d4f"
+checksum = "de7fa236829ba0841304542f7614c42b80fca007455315c45c785ccfa873a85b"
 dependencies = [
  "actix-macros",
  "actix-rt",
  "actix_derive",
- "bitflags 2.5.0",
+ "bitflags",
  "bytes",
  "crossbeam-channel",
  "futures-core",
@@ -33,7 +33,7 @@ version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
 dependencies = [
- "bitflags 2.5.0",
+ "bitflags",
  "bytes",
  "futures-core",
  "futures-sink",
@@ -46,17 +46,17 @@ dependencies = [
 
 [[package]]
 name = "actix-http"
-version = "3.6.0"
+version = "3.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d223b13fd481fc0d1f83bb12659ae774d9e3601814c68a0bc539731698cca743"
+checksum = "d48f96fc3003717aeb9856ca3d02a8c7de502667ad76eeacd830b48d2e91fac4"
 dependencies = [
  "actix-codec",
  "actix-rt",
  "actix-service",
  "actix-utils",
  "ahash",
- "base64 0.21.7",
- "bitflags 2.5.0",
+ "base64 0.22.1",
+ "bitflags",
  "brotli",
  "bytes",
  "bytestring",
@@ -90,27 +90,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
 dependencies = [
  "quote",
- "syn 2.0.60",
+ "syn 2.0.91",
 ]
 
 [[package]]
 name = "actix-router"
-version = "0.5.2"
+version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d22475596539443685426b6bdadb926ad0ecaefdfc5fb05e5e3441f15463c511"
+checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8"
 dependencies = [
  "bytestring",
+ "cfg-if",
  "http",
  "regex",
+ "regex-lite",
  "serde",
  "tracing",
 ]
 
 [[package]]
 name = "actix-rt"
-version = "2.9.0"
+version = "2.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d"
+checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208"
 dependencies = [
  "futures-core",
  "tokio",
@@ -118,9 +120,9 @@ dependencies = [
 
 [[package]]
 name = "actix-server"
-version = "2.3.0"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3eb13e7eef0423ea6eab0e59f6c72e7cb46d33691ad56a726b3cd07ddec2c2d4"
+checksum = "7ca2549781d8dd6d75c40cf6b6051260a2cc2f3c62343d761a969a0640646894"
 dependencies = [
  "actix-rt",
  "actix-service",
@@ -156,9 +158,9 @@ dependencies = [
 
 [[package]]
 name = "actix-web"
-version = "4.5.1"
+version = "4.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43a6556ddebb638c2358714d853257ed226ece6023ef9364f23f0c70737ea984"
+checksum = "9180d76e5cc7ccbc4d60a506f2c727730b154010262df5b910eb17dbe4b8cb38"
 dependencies = [
  "actix-codec",
  "actix-http",
@@ -178,6 +180,7 @@ dependencies = [
  "encoding_rs",
  "futures-core",
  "futures-util",
+ "impl-more",
  "itoa",
  "language-tags",
  "log",
@@ -185,6 +188,7 @@ dependencies = [
  "once_cell",
  "pin-project-lite",
  "regex",
+ "regex-lite",
  "serde",
  "serde_json",
  "serde_urlencoded",
@@ -195,60 +199,42 @@ dependencies = [
 ]
 
 [[package]]
-name = "actix-web-actors"
+name = "actix-web-codegen"
 version = "4.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "420b001bb709d8510c3e2659dae046e54509ff9528018d09c78381e765a1f9fa"
-dependencies = [
- "actix",
- "actix-codec",
- "actix-http",
- "actix-web",
- "bytes",
- "bytestring",
- "futures-core",
- "pin-project-lite",
- "tokio",
- "tokio-util",
-]
-
-[[package]]
-name = "actix-web-codegen"
-version = "4.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb1f50ebbb30eca122b188319a4398b3f7bb4a8cdf50ecfb73bfc6a3c3ce54f5"
+checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8"
 dependencies = [
  "actix-router",
  "proc-macro2",
  "quote",
- "syn 2.0.60",
+ "syn 2.0.91",
 ]
 
 [[package]]
 name = "actix_derive"
-version = "0.6.1"
+version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c7db3d5a9718568e4cf4a537cfd7070e6e6ff7481510d0237fb529ac850f6d3"
+checksum = "b6ac1e58cded18cb28ddc17143c4dea5345b3ad575e14f32f66e4054a56eb271"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.60",
+ "syn 2.0.91",
 ]
 
 [[package]]
 name = "addr2line"
-version = "0.21.0"
+version = "0.24.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
 dependencies = [
  "gimli",
 ]
 
 [[package]]
-name = "adler"
-version = "1.0.2"
+name = "adler2"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
 
 [[package]]
 name = "ahash"
@@ -289,9 +275,9 @@ dependencies = [
 
 [[package]]
 name = "allocator-api2"
-version = "0.2.18"
+version = "0.2.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
+checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
 
 [[package]]
 name = "android-tzdata"
@@ -309,14 +295,20 @@ dependencies = [
 ]
 
 [[package]]
-name = "async-trait"
-version = "0.1.80"
+name = "arraydeque"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
+checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
+
+[[package]]
+name = "async-trait"
+version = "0.1.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.60",
+ "syn 2.0.91",
 ]
 
 [[package]]
@@ -330,23 +322,23 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.3.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
 
 [[package]]
 name = "backtrace"
-version = "0.3.71"
+version = "0.3.74"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
+checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
 dependencies = [
  "addr2line",
- "cc",
  "cfg-if",
  "libc",
  "miniz_oxide",
  "object",
  "rustc-demangle",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -369,15 +361,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
 
 [[package]]
 name = "bitflags"
-version = "1.3.2"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
-[[package]]
-name = "bitflags"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
 dependencies = [
  "serde",
 ]
@@ -393,9 +379,9 @@ dependencies = [
 
 [[package]]
 name = "brotli"
-version = "3.5.0"
+version = "6.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391"
+checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b"
 dependencies = [
  "alloc-no-stdlib",
  "alloc-stdlib",
@@ -404,9 +390,9 @@ dependencies = [
 
 [[package]]
 name = "brotli-decompressor"
-version = "2.5.1"
+version = "4.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f"
+checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362"
 dependencies = [
  "alloc-no-stdlib",
  "alloc-stdlib",
@@ -426,28 +412,28 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "bytes"
-version = "1.6.0"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
 
 [[package]]
 name = "bytestring"
-version = "1.3.1"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72"
+checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f"
 dependencies = [
  "bytes",
 ]
 
 [[package]]
 name = "cc"
-version = "1.0.96"
+version = "1.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd"
+checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e"
 dependencies = [
  "jobserver",
  "libc",
- "once_cell",
+ "shlex",
 ]
 
 [[package]]
@@ -458,9 +444,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chrono"
-version = "0.4.38"
+version = "0.4.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
+checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
@@ -468,29 +454,28 @@ dependencies = [
  "num-traits",
  "serde",
  "wasm-bindgen",
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
 name = "colored"
-version = "2.1.0"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
+checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
 dependencies = [
  "lazy_static",
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "config"
-version = "0.14.0"
+version = "0.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be"
+checksum = "68578f196d2a33ff61b27fae256c3164f65e36382648e30666dde05b8cc9dfdf"
 dependencies = [
  "async-trait",
  "convert_case 0.6.0",
  "json5",
- "lazy_static",
  "nom",
  "pathdiff",
  "ron",
@@ -498,7 +483,7 @@ dependencies = [
  "serde",
  "serde_json",
  "toml",
- "yaml-rust",
+ "yaml-rust2",
 ]
 
 [[package]]
@@ -555,15 +540,15 @@ dependencies = [
 
 [[package]]
 name = "core-foundation-sys"
-version = "0.8.6"
+version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.12"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
 dependencies = [
  "libc",
 ]
@@ -585,36 +570,36 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
 
 [[package]]
 name = "crc32fast"
-version = "1.4.0"
+version = "1.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
 dependencies = [
  "cfg-if",
 ]
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.5.12"
+version = "0.5.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
+checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
 dependencies = [
  "crossbeam-utils",
 ]
 
 [[package]]
 name = "crossbeam-queue"
-version = "0.3.11"
+version = "0.3.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
+checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
 dependencies = [
  "crossbeam-utils",
 ]
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.19"
+version = "0.8.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
 
 [[package]]
 name = "crunchy"
@@ -634,9 +619,9 @@ dependencies = [
 
 [[package]]
 name = "darling"
-version = "0.20.9"
+version = "0.20.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1"
+checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
 dependencies = [
  "darling_core",
  "darling_macro",
@@ -644,27 +629,27 @@ dependencies = [
 
 [[package]]
 name = "darling_core"
-version = "0.20.9"
+version = "0.20.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120"
+checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
 dependencies = [
  "fnv",
  "ident_case",
  "proc-macro2",
  "quote",
  "strsim",
- "syn 2.0.60",
+ "syn 2.0.91",
 ]
 
 [[package]]
 name = "darling_macro"
-version = "0.20.9"
+version = "0.20.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
+checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
 dependencies = [
  "darling_core",
  "quote",
- "syn 2.0.60",
+ "syn 2.0.91",
 ]
 
 [[package]]
@@ -690,15 +675,15 @@ dependencies = [
 
 [[package]]
 name = "derive_more"
-version = "0.99.17"
+version = "0.99.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
+checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce"
 dependencies = [
  "convert_case 0.4.0",
  "proc-macro2",
  "quote",
  "rustc_version",
- "syn 1.0.109",
+ "syn 2.0.91",
 ]
 
 [[package]]
@@ -713,6 +698,17 @@ dependencies = [
  "subtle",
 ]
 
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.91",
+]
+
 [[package]]
 name = "dlv-list"
 version = "0.5.2"
@@ -730,20 +726,19 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
 
 [[package]]
 name = "either"
-version = "1.11.0"
+version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "emgauwa-common"
-version = "0.5.0"
+version = "0.5.1"
 dependencies = [
  "actix",
  "actix-web",
- "actix-web-actors",
  "chrono",
  "config",
  "futures",
@@ -761,9 +756,9 @@ dependencies = [
 
 [[package]]
 name = "encoding_rs"
-version = "0.8.34"
+version = "0.8.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
 dependencies = [
  "cfg-if",
 ]
@@ -776,12 +771,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
 [[package]]
 name = "errno"
-version = "0.3.8"
+version = "0.3.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
+checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
 dependencies = [
  "libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -803,21 +798,15 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
 
 [[package]]
 name = "fastrand"
-version = "2.1.0"
+version = "2.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
-
-[[package]]
-name = "finl_unicode"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
 
 [[package]]
 name = "flate2"
-version = "1.0.30"
+version = "1.0.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
+checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
 dependencies = [
  "crc32fast",
  "miniz_oxide",
@@ -825,13 +814,13 @@ dependencies = [
 
 [[package]]
 name = "flume"
-version = "0.11.0"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
+checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
 dependencies = [
  "futures-core",
  "futures-sink",
- "spin 0.9.8",
+ "spin",
 ]
 
 [[package]]
@@ -851,9 +840,9 @@ dependencies = [
 
 [[package]]
 name = "futures"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -866,9 +855,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -876,15 +865,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -904,38 +893,38 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.60",
+ "syn 2.0.91",
 ]
 
 [[package]]
 name = "futures-sink"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
 
 [[package]]
 name = "futures-task"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
 
 [[package]]
 name = "futures-util"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -961,9 +950,9 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.14"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
 dependencies = [
  "cfg-if",
  "libc",
@@ -972,9 +961,9 @@ dependencies = [
 
 [[package]]
 name = "gimli"
-version = "0.28.1"
+version = "0.31.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
 
 [[package]]
 name = "h2"
@@ -988,7 +977,7 @@ dependencies = [
  "futures-sink",
  "futures-util",
  "http",
- "indexmap 2.2.6",
+ "indexmap 2.7.0",
  "slab",
  "tokio",
  "tokio-util",
@@ -1001,12 +990,6 @@ version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 
-[[package]]
-name = "hashbrown"
-version = "0.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
-
 [[package]]
 name = "hashbrown"
 version = "0.14.5"
@@ -1017,6 +1000,12 @@ dependencies = [
  "allocator-api2",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+
 [[package]]
 name = "hashlink"
 version = "0.8.4"
@@ -1061,11 +1050,11 @@ dependencies = [
 
 [[package]]
 name = "home"
-version = "0.5.9"
+version = "0.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
+checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
 dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -1081,9 +1070,9 @@ dependencies = [
 
 [[package]]
 name = "httparse"
-version = "1.8.0"
+version = "1.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
 
 [[package]]
 name = "httpdate"
@@ -1093,9 +1082,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
 
 [[package]]
 name = "iana-time-zone"
-version = "0.1.60"
+version = "0.1.61"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
 dependencies = [
  "android_system_properties",
  "core-foundation-sys",
@@ -1114,6 +1103,124 @@ dependencies = [
  "cc",
 ]
 
+[[package]]
+name = "icu_collections"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_locid_transform_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
+
+[[package]]
+name = "icu_normalizer"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "utf16_iter",
+ "utf8_iter",
+ "write16",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+
+[[package]]
+name = "icu_properties"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locid_transform",
+ "icu_properties_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+
+[[package]]
+name = "icu_provider"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_provider_macros",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_provider_macros"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.91",
+]
+
 [[package]]
 name = "ident_case"
 version = "1.0.1"
@@ -1122,14 +1229,31 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
 
 [[package]]
 name = "idna"
-version = "0.5.0"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
 dependencies = [
- "unicode-bidi",
- "unicode-normalization",
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
 ]
 
+[[package]]
+name = "idna_adapter"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "impl-more"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2"
+
 [[package]]
 name = "indexmap"
 version = "1.9.3"
@@ -1143,45 +1267,37 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "2.2.6"
+version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
 dependencies = [
  "equivalent",
- "hashbrown 0.14.5",
+ "hashbrown 0.15.2",
  "serde",
 ]
 
-[[package]]
-name = "itertools"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
-dependencies = [
- "either",
-]
-
 [[package]]
 name = "itoa"
-version = "1.0.11"
+version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
 
 [[package]]
 name = "jobserver"
-version = "0.1.31"
+version = "0.1.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
+checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "js-sys"
-version = "0.3.69"
+version = "0.3.76"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
 dependencies = [
+ "once_cell",
  "wasm-bindgen",
 ]
 
@@ -1204,24 +1320,24 @@ checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
 
 [[package]]
 name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 dependencies = [
- "spin 0.5.2",
+ "spin",
 ]
 
 [[package]]
 name = "libc"
-version = "0.2.154"
+version = "0.2.169"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
 
 [[package]]
 name = "libm"
-version = "0.2.8"
+version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
 
 [[package]]
 name = "libsqlite3-sys"
@@ -1235,16 +1351,16 @@ dependencies = [
 ]
 
 [[package]]
-name = "linked-hash-map"
-version = "0.5.6"
+name = "linux-raw-sys"
+version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
 
 [[package]]
-name = "linux-raw-sys"
-version = "0.4.13"
+name = "litemap"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
 
 [[package]]
 name = "local-channel"
@@ -1275,9 +1391,9 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.21"
+version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
 
 [[package]]
 name = "md-5"
@@ -1291,9 +1407,9 @@ dependencies = [
 
 [[package]]
 name = "memchr"
-version = "2.7.2"
+version = "2.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
 
 [[package]]
 name = "mime"
@@ -1309,23 +1425,23 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.7.2"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
+checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
 dependencies = [
- "adler",
+ "adler2",
 ]
 
 [[package]]
 name = "mio"
-version = "0.8.11"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
 dependencies = [
  "libc",
  "log",
  "wasi",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -1402,34 +1518,34 @@ dependencies = [
 
 [[package]]
 name = "object"
-version = "0.32.2"
+version = "0.36.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "once_cell"
-version = "1.19.0"
+version = "1.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
 
 [[package]]
 name = "ordered-multimap"
-version = "0.6.0"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
+checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
 dependencies = [
  "dlv-list",
- "hashbrown 0.13.2",
+ "hashbrown 0.14.5",
 ]
 
 [[package]]
 name = "parking_lot"
-version = "0.12.2"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
 dependencies = [
  "lock_api",
  "parking_lot_core",
@@ -1443,22 +1559,22 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall 0.5.1",
+ "redox_syscall",
  "smallvec",
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
 name = "paste"
-version = "1.0.14"
+version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
 
 [[package]]
 name = "pathdiff"
-version = "0.2.1"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
+checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
 
 [[package]]
 name = "pem-rfc7468"
@@ -1477,20 +1593,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
 [[package]]
 name = "pest"
-version = "2.7.10"
+version = "2.7.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8"
+checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
 dependencies = [
  "memchr",
- "thiserror",
+ "thiserror 2.0.9",
  "ucd-trie",
 ]
 
 [[package]]
 name = "pest_derive"
-version = "2.7.10"
+version = "2.7.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459"
+checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e"
 dependencies = [
  "pest",
  "pest_generator",
@@ -1498,22 +1614,22 @@ dependencies = [
 
 [[package]]
 name = "pest_generator"
-version = "2.7.10"
+version = "2.7.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687"
+checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b"
 dependencies = [
  "pest",
  "pest_meta",
  "proc-macro2",
  "quote",
- "syn 2.0.60",
+ "syn 2.0.91",
 ]
 
 [[package]]
 name = "pest_meta"
-version = "2.7.10"
+version = "2.7.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd"
+checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea"
 dependencies = [
  "once_cell",
  "pest",
@@ -1522,9 +1638,9 @@ dependencies = [
 
 [[package]]
 name = "pin-project-lite"
-version = "0.2.14"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
 
 [[package]]
 name = "pin-utils"
@@ -1555,9 +1671,9 @@ dependencies = [
 
 [[package]]
 name = "pkg-config"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
 
 [[package]]
 name = "powerfmt"
@@ -1567,24 +1683,27 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.17"
+version = "0.2.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.81"
+version = "1.0.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.36"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
 dependencies = [
  "proc-macro2",
 ]
@@ -1621,27 +1740,18 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.4.1"
+version = "0.5.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
 dependencies = [
- "bitflags 1.3.2",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
-dependencies = [
- "bitflags 2.5.0",
+ "bitflags",
 ]
 
 [[package]]
 name = "regex"
-version = "1.10.4"
+version = "1.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1651,9 +1761,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.6"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1661,10 +1771,16 @@ dependencies = [
 ]
 
 [[package]]
-name = "regex-syntax"
-version = "0.8.3"
+name = "regex-lite"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a"
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
 
 [[package]]
 name = "ron"
@@ -1673,16 +1789,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
 dependencies = [
  "base64 0.21.7",
- "bitflags 2.5.0",
+ "bitflags",
  "serde",
  "serde_derive",
 ]
 
 [[package]]
 name = "rsa"
-version = "0.9.6"
+version = "0.9.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc"
+checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519"
 dependencies = [
  "const-oid",
  "digest",
@@ -1700,9 +1816,9 @@ dependencies = [
 
 [[package]]
 name = "rust-ini"
-version = "0.19.0"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
+checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a"
 dependencies = [
  "cfg-if",
  "ordered-multimap",
@@ -1710,37 +1826,37 @@ dependencies = [
 
 [[package]]
 name = "rustc-demangle"
-version = "0.1.23"
+version = "0.1.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
 
 [[package]]
 name = "rustc_version"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
 dependencies = [
  "semver",
 ]
 
 [[package]]
 name = "rustix"
-version = "0.38.34"
+version = "0.38.42"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
 dependencies = [
- "bitflags 2.5.0",
+ "bitflags",
  "errno",
  "libc",
  "linux-raw-sys",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "ryu"
-version = "1.0.17"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 
 [[package]]
 name = "scopeguard"
@@ -1750,46 +1866,47 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
 [[package]]
 name = "semver"
-version = "1.0.22"
+version = "1.0.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
+checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
 
 [[package]]
 name = "serde"
-version = "1.0.200"
+version = "1.0.216"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
+checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.200"
+version = "1.0.216"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
+checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.60",
+ "syn 2.0.91",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.116"
+version = "1.0.134"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
+checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
 dependencies = [
  "itoa",
+ "memchr",
  "ryu",
  "serde",
 ]
 
 [[package]]
 name = "serde_spanned"
-version = "0.6.5"
+version = "0.6.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
+checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
 dependencies = [
  "serde",
 ]
@@ -1808,15 +1925,15 @@ dependencies = [
 
 [[package]]
 name = "serde_with"
-version = "3.8.1"
+version = "3.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20"
+checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817"
 dependencies = [
  "base64 0.22.1",
  "chrono",
  "hex",
  "indexmap 1.9.3",
- "indexmap 2.2.6",
+ "indexmap 2.7.0",
  "serde",
  "serde_derive",
  "serde_json",
@@ -1826,14 +1943,14 @@ dependencies = [
 
 [[package]]
 name = "serde_with_macros"
-version = "3.8.1"
+version = "3.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2"
+checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d"
 dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.60",
+ "syn 2.0.91",
 ]
 
 [[package]]
@@ -1858,6 +1975,12 @@ dependencies = [
  "digest",
 ]
 
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
 [[package]]
 name = "signal-hook-registry"
 version = "1.4.2"
@@ -1906,20 +2029,14 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
 
 [[package]]
 name = "socket2"
-version = "0.5.7"
+version = "0.5.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
+checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
 dependencies = [
  "libc",
  "windows-sys 0.52.0",
 ]
 
-[[package]]
-name = "spin"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
-
 [[package]]
 name = "spin"
 version = "0.9.8"
@@ -1941,11 +2058,10 @@ dependencies = [
 
 [[package]]
 name = "sqlformat"
-version = "0.2.3"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c"
+checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790"
 dependencies = [
- "itertools",
  "nom",
  "unicode_categories",
 ]
@@ -1984,7 +2100,7 @@ dependencies = [
  "futures-util",
  "hashlink",
  "hex",
- "indexmap 2.2.6",
+ "indexmap 2.7.0",
  "log",
  "memchr",
  "once_cell",
@@ -1995,7 +2111,7 @@ dependencies = [
  "sha2",
  "smallvec",
  "sqlformat",
- "thiserror",
+ "thiserror 1.0.69",
  "tokio",
  "tokio-stream",
  "tracing",
@@ -2048,7 +2164,7 @@ checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
 dependencies = [
  "atoi",
  "base64 0.21.7",
- "bitflags 2.5.0",
+ "bitflags",
  "byteorder",
  "bytes",
  "crc",
@@ -2077,7 +2193,7 @@ dependencies = [
  "smallvec",
  "sqlx-core",
  "stringprep",
- "thiserror",
+ "thiserror 1.0.69",
  "tracing",
  "whoami",
 ]
@@ -2090,7 +2206,7 @@ checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
 dependencies = [
  "atoi",
  "base64 0.21.7",
- "bitflags 2.5.0",
+ "bitflags",
  "byteorder",
  "crc",
  "dotenvy",
@@ -2115,7 +2231,7 @@ dependencies = [
  "smallvec",
  "sqlx-core",
  "stringprep",
- "thiserror",
+ "thiserror 1.0.69",
  "tracing",
  "whoami",
 ]
@@ -2144,14 +2260,20 @@ dependencies = [
 ]
 
 [[package]]
-name = "stringprep"
-version = "0.1.4"
+name = "stable_deref_trait"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "stringprep"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1"
 dependencies = [
- "finl_unicode",
  "unicode-bidi",
  "unicode-normalization",
+ "unicode-properties",
 ]
 
 [[package]]
@@ -2162,9 +2284,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
 [[package]]
 name = "subtle"
-version = "2.5.0"
+version = "2.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 
 [[package]]
 name = "syn"
@@ -2179,9 +2301,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.60"
+version = "2.0.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
+checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2189,42 +2311,74 @@ dependencies = [
 ]
 
 [[package]]
-name = "tempfile"
-version = "3.10.1"
+name = "synstructure"
+version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.91",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
 dependencies = [
  "cfg-if",
  "fastrand",
+ "once_cell",
  "rustix",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "thiserror"
-version = "1.0.59"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
 dependencies = [
- "thiserror-impl",
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
+dependencies = [
+ "thiserror-impl 2.0.9",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.59"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.60",
+ "syn 2.0.91",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.91",
 ]
 
 [[package]]
 name = "time"
-version = "0.3.36"
+version = "0.3.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
+checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
 dependencies = [
  "deranged",
  "itoa",
@@ -2245,9 +2399,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
 [[package]]
 name = "time-macros"
-version = "0.2.18"
+version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
+checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
 dependencies = [
  "num-conv",
  "time-core",
@@ -2263,10 +2417,20 @@ dependencies = [
 ]
 
 [[package]]
-name = "tinyvec"
-version = "1.6.0"
+name = "tinystr"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
 dependencies = [
  "tinyvec_macros",
 ]
@@ -2279,9 +2443,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "tokio"
-version = "1.37.0"
+version = "1.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
+checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
 dependencies = [
  "backtrace",
  "bytes",
@@ -2291,14 +2455,14 @@ dependencies = [
  "pin-project-lite",
  "signal-hook-registry",
  "socket2",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "tokio-stream"
-version = "0.1.15"
+version = "0.1.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
+checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
 dependencies = [
  "futures-core",
  "pin-project-lite",
@@ -2307,9 +2471,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-util"
-version = "0.7.11"
+version = "0.7.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
+checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
 dependencies = [
  "bytes",
  "futures-core",
@@ -2320,9 +2484,9 @@ dependencies = [
 
 [[package]]
 name = "toml"
-version = "0.8.12"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
+checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
 dependencies = [
  "serde",
  "serde_spanned",
@@ -2332,20 +2496,20 @@ dependencies = [
 
 [[package]]
 name = "toml_datetime"
-version = "0.6.5"
+version = "0.6.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "toml_edit"
-version = "0.22.12"
+version = "0.22.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef"
+checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
 dependencies = [
- "indexmap 2.2.6",
+ "indexmap 2.7.0",
  "serde",
  "serde_spanned",
  "toml_datetime",
@@ -2354,9 +2518,9 @@ dependencies = [
 
 [[package]]
 name = "tracing"
-version = "0.1.40"
+version = "0.1.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
 dependencies = [
  "log",
  "pin-project-lite",
@@ -2366,20 +2530,20 @@ dependencies = [
 
 [[package]]
 name = "tracing-attributes"
-version = "0.1.27"
+version = "0.1.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.60",
+ "syn 2.0.91",
 ]
 
 [[package]]
 name = "tracing-core"
-version = "0.1.32"
+version = "0.1.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
 dependencies = [
  "once_cell",
 ]
@@ -2392,36 +2556,42 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
 
 [[package]]
 name = "ucd-trie"
-version = "0.1.6"
+version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
+checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
 
 [[package]]
 name = "unicode-bidi"
-version = "0.3.15"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.12"
+version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
 
 [[package]]
 name = "unicode-normalization"
-version = "0.1.23"
+version = "0.1.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
 dependencies = [
  "tinyvec",
 ]
 
 [[package]]
-name = "unicode-segmentation"
-version = "1.11.0"
+name = "unicode-properties"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
 
 [[package]]
 name = "unicode_categories"
@@ -2431,9 +2601,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
 
 [[package]]
 name = "url"
-version = "2.5.0"
+version = "2.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
 dependencies = [
  "form_urlencoded",
  "idna",
@@ -2447,10 +2617,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
 
 [[package]]
-name = "uuid"
-version = "1.8.0"
+name = "utf16_iter"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
+checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "uuid"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
 dependencies = [
  "getrandom",
 ]
@@ -2463,9 +2645,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
 
 [[package]]
 name = "version_check"
-version = "0.9.4"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
 
 [[package]]
 name = "wasi"
@@ -2481,34 +2663,34 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.92"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
 dependencies = [
  "cfg-if",
+ "once_cell",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
 dependencies = [
  "bumpalo",
  "log",
- "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.60",
+ "syn 2.0.91",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -2516,30 +2698,30 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.60",
+ "syn 2.0.91",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
 
 [[package]]
 name = "whoami"
-version = "1.5.1"
+version = "1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9"
+checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d"
 dependencies = [
- "redox_syscall 0.4.1",
+ "redox_syscall",
  "wasite",
 ]
 
@@ -2549,7 +2731,7 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
 dependencies = [
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -2567,7 +2749,16 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
 dependencies = [
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -2587,18 +2778,18 @@ dependencies = [
 
 [[package]]
 name = "windows-targets"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 dependencies = [
- "windows_aarch64_gnullvm 0.52.5",
- "windows_aarch64_msvc 0.52.5",
- "windows_i686_gnu 0.52.5",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
  "windows_i686_gnullvm",
- "windows_i686_msvc 0.52.5",
- "windows_x86_64_gnu 0.52.5",
- "windows_x86_64_gnullvm 0.52.5",
- "windows_x86_64_msvc 0.52.5",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
 ]
 
 [[package]]
@@ -2609,9 +2800,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
 [[package]]
 name = "windows_aarch64_msvc"
@@ -2621,9 +2812,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
 [[package]]
 name = "windows_i686_gnu"
@@ -2633,15 +2824,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
 
 [[package]]
 name = "windows_i686_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
 [[package]]
 name = "windows_i686_msvc"
@@ -2651,9 +2842,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
 [[package]]
 name = "windows_x86_64_gnu"
@@ -2663,9 +2854,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
@@ -2675,9 +2866,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
 [[package]]
 name = "windows_x86_64_msvc"
@@ -2687,77 +2878,159 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
 [[package]]
 name = "winnow"
-version = "0.6.7"
+version = "0.6.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578"
+checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
-name = "yaml-rust"
-version = "0.4.5"
+name = "write16"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
+checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
+
+[[package]]
+name = "writeable"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+
+[[package]]
+name = "yaml-rust2"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8"
 dependencies = [
- "linked-hash-map",
+ "arraydeque",
+ "encoding_rs",
+ "hashlink",
+]
+
+[[package]]
+name = "yoke"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.91",
+ "synstructure",
 ]
 
 [[package]]
 name = "zerocopy"
-version = "0.7.33"
+version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
 dependencies = [
+ "byteorder",
  "zerocopy-derive",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.7.33"
+version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.60",
+ "syn 2.0.91",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.91",
+ "synstructure",
 ]
 
 [[package]]
 name = "zeroize"
-version = "1.7.0"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+
+[[package]]
+name = "zerovec"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.91",
+]
 
 [[package]]
 name = "zstd"
-version = "0.13.1"
+version = "0.13.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a"
+checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9"
 dependencies = [
  "zstd-safe",
 ]
 
 [[package]]
 name = "zstd-safe"
-version = "7.1.0"
+version = "7.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a"
+checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059"
 dependencies = [
  "zstd-sys",
 ]
 
 [[package]]
 name = "zstd-sys"
-version = "2.0.10+zstd.1.5.6"
+version = "2.0.13+zstd.1.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa"
+checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
 dependencies = [
  "cc",
  "pkg-config",
diff --git a/Cargo.toml b/Cargo.toml
index 464e9b2..e8f2433 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "emgauwa-common"
-version = "0.5.0"
+version = "0.5.1"
 edition = "2021"
 authors = ["Tobias Reisinger <tobias@msrg.cc>"]
 
@@ -8,7 +8,6 @@ authors = ["Tobias Reisinger <tobias@msrg.cc>"]
 [dependencies]
 actix = "0.13"
 actix-web = "4.4"
-actix-web-actors = "4.2"
 
 serde = "1.0"
 serde_json = "1.0"
@@ -26,4 +25,4 @@ sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros"] }
 libsqlite3-sys = { version = "*", features = ["bundled"] }
 uuid = { version = "1.8", features = ["v4"] }
 futures = "0.3"
-libc = "0.2"
\ No newline at end of file
+libc = "0.2"