Split project (keep common)
This commit is contained in:
parent
9bc75b9627
commit
96a64cf90d
68 changed files with 32 additions and 3502 deletions
184
src/db/controllers.rs
Normal file
184
src/db/controllers.rs
Normal file
|
@ -0,0 +1,184 @@
|
|||
use std::ops::DerefMut;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::Sqlite;
|
||||
|
||||
use crate::db::{DbRelay, DbTag};
|
||||
use crate::errors::DatabaseError;
|
||||
use crate::types::EmgauwaUid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DbController {
|
||||
#[serde(skip)]
|
||||
pub id: i64,
|
||||
#[serde(rename = "id")]
|
||||
pub uid: EmgauwaUid,
|
||||
pub name: String,
|
||||
pub relay_count: i64,
|
||||
pub active: bool,
|
||||
}
|
||||
|
||||
impl DbController {
|
||||
pub async fn get_all(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
) -> Result<Vec<DbController>, DatabaseError> {
|
||||
sqlx::query_as!(DbController, "SELECT * FROM controllers")
|
||||
.fetch_all(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
id: i64,
|
||||
) -> Result<Option<DbController>, DatabaseError> {
|
||||
sqlx::query_as!(DbController, "SELECT * FROM controllers WHERE id = ?", id)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get_by_uid(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
filter_uid: &EmgauwaUid,
|
||||
) -> Result<Option<DbController>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbController,
|
||||
"SELECT * FROM controllers WHERE uid = ?",
|
||||
filter_uid
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get_by_uid_or_create(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
uid: &EmgauwaUid,
|
||||
new_name: &str,
|
||||
new_relay_count: i64,
|
||||
) -> Result<DbController, DatabaseError> {
|
||||
match DbController::get_by_uid(conn, uid).await? {
|
||||
Some(tag) => Ok(tag),
|
||||
None => DbController::create(conn, uid, new_name, new_relay_count).await,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_by_tag(
|
||||
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(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn delete_by_uid(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
filter_uid: EmgauwaUid,
|
||||
) -> Result<(), DatabaseError> {
|
||||
if sqlx::query_scalar!("SELECT 1 FROM controllers WHERE uid = ?", filter_uid)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await?
|
||||
.is_none()
|
||||
{
|
||||
return Err(DatabaseError::NotFound);
|
||||
}
|
||||
|
||||
sqlx::query!("DELETE FROM controllers WHERE uid = ?", filter_uid)
|
||||
.execute(conn.deref_mut())
|
||||
.await
|
||||
.map(|res| match res.rows_affected() {
|
||||
0 => Err(DatabaseError::DeleteError),
|
||||
_ => Ok(()),
|
||||
})?
|
||||
}
|
||||
|
||||
pub async fn create(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_uid: &EmgauwaUid,
|
||||
new_name: &str,
|
||||
new_relay_count: i64,
|
||||
) -> Result<DbController, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbController,
|
||||
"INSERT INTO controllers (uid, name, relay_count) VALUES (?, ?, ?) RETURNING *",
|
||||
new_uid,
|
||||
new_name,
|
||||
new_relay_count,
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await?
|
||||
.ok_or(DatabaseError::InsertGetError)
|
||||
}
|
||||
|
||||
pub async fn update(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_name: &str,
|
||||
new_relay_count: i64,
|
||||
) -> Result<DbController, DatabaseError> {
|
||||
sqlx::query!(
|
||||
"UPDATE controllers SET name = ?, relay_count = ? WHERE id = ?",
|
||||
new_name,
|
||||
new_relay_count,
|
||||
self.id,
|
||||
)
|
||||
.execute(conn.deref_mut())
|
||||
.await?;
|
||||
|
||||
Self::get(conn, self.id)
|
||||
.await?
|
||||
.ok_or(DatabaseError::UpdateGetError)
|
||||
}
|
||||
|
||||
pub async fn update_active(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_active: bool,
|
||||
) -> Result<DbController, DatabaseError> {
|
||||
sqlx::query!(
|
||||
"UPDATE controllers SET active = ? WHERE id = ?",
|
||||
new_active,
|
||||
self.id,
|
||||
)
|
||||
.execute(conn.deref_mut())
|
||||
.await?;
|
||||
|
||||
Self::get(conn, self.id)
|
||||
.await?
|
||||
.ok_or(DatabaseError::UpdateGetError)
|
||||
}
|
||||
|
||||
pub async fn get_relays(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
) -> Result<Vec<DbRelay>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbRelay,
|
||||
"SELECT * FROM relays WHERE controller_id = ?",
|
||||
self.id
|
||||
)
|
||||
.fetch_all(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn all_inactive(conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
|
||||
sqlx::query!("UPDATE controllers SET active = 0")
|
||||
.execute(conn.deref_mut())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn reload(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
) -> Result<DbController, DatabaseError> {
|
||||
Self::get(conn, self.id)
|
||||
.await?
|
||||
.ok_or(DatabaseError::NotFound)
|
||||
}
|
||||
}
|
146
src/db/junction_relay_schedule.rs
Normal file
146
src/db/junction_relay_schedule.rs
Normal file
|
@ -0,0 +1,146 @@
|
|||
use std::ops::DerefMut;
|
||||
|
||||
use sqlx::pool::PoolConnection;
|
||||
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 relay_id: i64,
|
||||
pub schedule_id: i64,
|
||||
}
|
||||
|
||||
impl DbJunctionRelaySchedule {
|
||||
pub async fn get(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
id: i64,
|
||||
) -> Result<Option<DbJunctionRelaySchedule>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbJunctionRelaySchedule,
|
||||
"SELECT * FROM junction_relay_schedule WHERE id = ?",
|
||||
id
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get_junction_by_relay_and_weekday(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
relay: &DbRelay,
|
||||
weekday: Weekday,
|
||||
) -> Result<Option<DbJunctionRelaySchedule>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbJunctionRelaySchedule,
|
||||
"SELECT * FROM junction_relay_schedule WHERE relay_id = ? AND weekday = ?",
|
||||
relay.id,
|
||||
weekday
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get_relays(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
schedule: &DbSchedule,
|
||||
) -> 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
|
||||
WHERE junction_relay_schedule.schedule_id = ?
|
||||
ORDER BY junction_relay_schedule.weekday"#,
|
||||
schedule.id
|
||||
)
|
||||
.fetch_all(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get_schedule(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
relay: &DbRelay,
|
||||
weekday: Weekday,
|
||||
) -> Result<Option<DbSchedule>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbSchedule,
|
||||
r#"SELECT schedules.* FROM schedules INNER JOIN junction_relay_schedule
|
||||
ON junction_relay_schedule.schedule_id = schedules.id
|
||||
WHERE junction_relay_schedule.relay_id = ? AND junction_relay_schedule.weekday = ?"#,
|
||||
relay.id,
|
||||
weekday
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get_schedules(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
relay: &DbRelay,
|
||||
) -> Result<Vec<DbSchedule>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbSchedule,
|
||||
r#"SELECT schedules.* FROM schedules INNER JOIN junction_relay_schedule
|
||||
ON junction_relay_schedule.schedule_id = schedules.id
|
||||
WHERE junction_relay_schedule.relay_id = ?
|
||||
ORDER BY junction_relay_schedule.weekday"#,
|
||||
relay.id
|
||||
)
|
||||
.fetch_all(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn set_schedule(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
relay: &DbRelay,
|
||||
schedule: &DbSchedule,
|
||||
weekday: Weekday,
|
||||
) -> Result<DbJunctionRelaySchedule, DatabaseError> {
|
||||
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 *",
|
||||
weekday,
|
||||
relay.id,
|
||||
schedule.id
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await?
|
||||
.ok_or(DatabaseError::InsertGetError),
|
||||
|
||||
Some(junction) => {
|
||||
sqlx::query!(
|
||||
"UPDATE junction_relay_schedule SET weekday = ?, relay_id = ?, schedule_id= ? WHERE id = ?",
|
||||
weekday,
|
||||
relay.id,
|
||||
schedule.id,
|
||||
junction.id
|
||||
)
|
||||
.execute(conn.deref_mut())
|
||||
.await?;
|
||||
|
||||
Self::get(conn, junction.id)
|
||||
.await?
|
||||
.ok_or(DatabaseError::UpdateGetError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set_schedules(
|
||||
conn: &mut PoolConnection<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?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
48
src/db/junction_tag.rs
Normal file
48
src/db/junction_tag.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
use std::ops::DerefMut;
|
||||
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::Sqlite;
|
||||
|
||||
use crate::db::{DbRelay, DbSchedule, DbTag};
|
||||
use crate::errors::DatabaseError;
|
||||
|
||||
pub struct DbJunctionTag {
|
||||
pub id: i64,
|
||||
pub tag_id: i64,
|
||||
pub relay_id: Option<i64>,
|
||||
pub schedule_id: Option<i64>,
|
||||
}
|
||||
|
||||
impl DbJunctionTag {
|
||||
pub async fn link_relay(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
tag: &DbTag,
|
||||
relay: &DbRelay,
|
||||
) -> Result<DbJunctionTag, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbJunctionTag,
|
||||
"INSERT INTO junction_tag (tag_id, relay_id) VALUES (?, ?) RETURNING *",
|
||||
tag.id,
|
||||
relay.id
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await?
|
||||
.ok_or(DatabaseError::InsertGetError)
|
||||
}
|
||||
|
||||
pub async fn link_schedule(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
tag: &DbTag,
|
||||
schedule: &DbSchedule,
|
||||
) -> Result<DbJunctionTag, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbJunctionTag,
|
||||
"INSERT INTO junction_tag (tag_id, schedule_id) VALUES (?, ?) RETURNING *",
|
||||
tag.id,
|
||||
schedule.id
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await?
|
||||
.ok_or(DatabaseError::InsertGetError)
|
||||
}
|
||||
}
|
166
src/db/macro.rs
Normal file
166
src/db/macro.rs
Normal file
|
@ -0,0 +1,166 @@
|
|||
use std::ops::DerefMut;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::Sqlite;
|
||||
|
||||
use crate::db::{DbController, DbMacroAction, DbRelay, DbSchedule};
|
||||
use crate::errors::DatabaseError;
|
||||
use crate::types::{EmgauwaUid, RequestMacroAction};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DbMacro {
|
||||
#[serde(skip)]
|
||||
pub id: i64,
|
||||
#[serde(rename = "id")]
|
||||
pub uid: EmgauwaUid,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
|
||||
impl DbMacro {
|
||||
pub async fn get_all(conn: &mut PoolConnection<Sqlite>) -> Result<Vec<DbMacro>, DatabaseError> {
|
||||
sqlx::query_as!(DbMacro, "SELECT * FROM macros")
|
||||
.fetch_all(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
id: i64,
|
||||
) -> Result<Option<DbMacro>, DatabaseError> {
|
||||
sqlx::query_as!(DbMacro, "SELECT * FROM macros WHERE id = ?", id)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get_by_uid(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
filter_uid: &EmgauwaUid,
|
||||
) -> Result<Option<DbMacro>, DatabaseError> {
|
||||
sqlx::query_as!(DbMacro, "SELECT * FROM macros WHERE uid = ?", filter_uid)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn create(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_uid: EmgauwaUid,
|
||||
new_name: &str,
|
||||
) -> Result<DbMacro, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbMacro,
|
||||
"INSERT INTO macros (uid, name) VALUES (?, ?) RETURNING *",
|
||||
new_uid,
|
||||
new_name
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await?
|
||||
.ok_or(DatabaseError::InsertGetError)
|
||||
}
|
||||
|
||||
pub async fn delete(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
|
||||
sqlx::query!("DELETE FROM macros WHERE id = ?", self.id)
|
||||
.execute(conn.deref_mut())
|
||||
.await
|
||||
.map(|res| match res.rows_affected() {
|
||||
0 => Err(DatabaseError::DeleteError),
|
||||
_ => Ok(()),
|
||||
})?
|
||||
}
|
||||
|
||||
pub async fn delete_by_uid(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
filter_uid: EmgauwaUid,
|
||||
) -> Result<(), DatabaseError> {
|
||||
if sqlx::query_scalar!("SELECT 1 FROM macros WHERE uid = ?", filter_uid)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await?
|
||||
.is_none()
|
||||
{
|
||||
return Err(DatabaseError::NotFound);
|
||||
}
|
||||
|
||||
sqlx::query!("DELETE FROM macros WHERE uid = ?", filter_uid)
|
||||
.execute(conn.deref_mut())
|
||||
.await
|
||||
.map(|res| match res.rows_affected() {
|
||||
0 => Err(DatabaseError::DeleteError),
|
||||
_ => Ok(()),
|
||||
})?
|
||||
}
|
||||
|
||||
pub async fn update(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_name: &str,
|
||||
) -> Result<DbMacro, DatabaseError> {
|
||||
sqlx::query!("UPDATE relays SET name = ? WHERE id = ?", new_name, self.id,)
|
||||
.execute(conn.deref_mut())
|
||||
.await?;
|
||||
|
||||
DbMacro::get(conn, self.id)
|
||||
.await?
|
||||
.ok_or(DatabaseError::UpdateGetError)
|
||||
}
|
||||
|
||||
pub async fn set_actions(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_actions: &[RequestMacroAction],
|
||||
) -> Result<(), DatabaseError> {
|
||||
sqlx::query!("DELETE FROM macro_actions WHERE macro_id = ?", self.id)
|
||||
.execute(conn.deref_mut())
|
||||
.await?;
|
||||
|
||||
for new_action in new_actions {
|
||||
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(conn, &controller, new_action.relay.number)
|
||||
.await?
|
||||
.ok_or(DatabaseError::NotFound)?;
|
||||
|
||||
let schedule = DbSchedule::get_by_uid(conn, &new_action.schedule.id)
|
||||
.await?
|
||||
.ok_or(DatabaseError::NotFound)?;
|
||||
|
||||
DbMacroAction::create(conn, self, &relay, &schedule, new_action.weekday).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_actions(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
) -> Result<Vec<DbMacroAction>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbMacroAction,
|
||||
"SELECT * FROM macro_actions WHERE macro_id = ?",
|
||||
self.id
|
||||
)
|
||||
.fetch_all(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get_actions_weekday(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
weekday: i64,
|
||||
) -> Result<Vec<DbMacroAction>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbMacroAction,
|
||||
"SELECT * FROM macro_actions WHERE macro_id = ? AND weekday = ?",
|
||||
self.id,
|
||||
weekday
|
||||
)
|
||||
.fetch_all(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
}
|
99
src/db/macro_action.rs
Normal file
99
src/db/macro_action.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
use std::ops::DerefMut;
|
||||
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::Sqlite;
|
||||
|
||||
use crate::db::{DbMacro, DbRelay, DbSchedule};
|
||||
use crate::errors::DatabaseError;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DbMacroAction {
|
||||
pub id: i64,
|
||||
pub macro_id: i64,
|
||||
pub relay_id: i64,
|
||||
pub schedule_id: i64,
|
||||
pub weekday: i64, // should be u8, but sqlite will store it as i64
|
||||
}
|
||||
|
||||
|
||||
impl DbMacroAction {
|
||||
pub async fn get_all(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
) -> Result<Vec<DbMacroAction>, DatabaseError> {
|
||||
sqlx::query_as!(DbMacroAction, "SELECT * FROM macro_actions")
|
||||
.fetch_all(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
id: i64,
|
||||
) -> Result<Option<DbMacroAction>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbMacroAction,
|
||||
"SELECT * FROM macro_actions WHERE id = ?",
|
||||
id
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn create(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_macro: &DbMacro,
|
||||
new_relay: &DbRelay,
|
||||
new_schedule: &DbSchedule,
|
||||
new_weekday: i64,
|
||||
) -> Result<DbMacroAction, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbMacroAction,
|
||||
"INSERT INTO macro_actions (macro_id, relay_id, schedule_id, weekday) VALUES (?, ?, ?, ?) RETURNING *",
|
||||
new_macro.id,
|
||||
new_relay.id,
|
||||
new_schedule.id,
|
||||
new_weekday
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await?
|
||||
.ok_or(DatabaseError::InsertGetError)
|
||||
}
|
||||
|
||||
pub async fn delete(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
|
||||
sqlx::query!("DELETE FROM macro_actions WHERE id = ?", self.id)
|
||||
.execute(conn.deref_mut())
|
||||
.await
|
||||
.map(|res| match res.rows_affected() {
|
||||
0 => Err(DatabaseError::DeleteError),
|
||||
_ => Ok(()),
|
||||
})?
|
||||
}
|
||||
|
||||
pub async fn get_relay(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
) -> Result<DbRelay, DatabaseError> {
|
||||
DbRelay::get(conn, self.relay_id)
|
||||
.await?
|
||||
.ok_or(DatabaseError::NotFound)
|
||||
}
|
||||
|
||||
pub async fn get_schedule(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
) -> Result<DbSchedule, DatabaseError> {
|
||||
DbSchedule::get(conn, self.schedule_id)
|
||||
.await?
|
||||
.ok_or(DatabaseError::NotFound)
|
||||
}
|
||||
|
||||
pub async fn get_macro(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
) -> Result<DbMacro, DatabaseError> {
|
||||
DbMacro::get(conn, self.macro_id)
|
||||
.await?
|
||||
.ok_or(DatabaseError::NotFound)
|
||||
}
|
||||
}
|
55
src/db/mod.rs
Normal file
55
src/db/mod.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use sqlx::migrate::Migrator;
|
||||
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
|
||||
use sqlx::{ConnectOptions, Pool, Sqlite};
|
||||
|
||||
mod controllers;
|
||||
mod junction_relay_schedule;
|
||||
mod junction_tag;
|
||||
mod r#macro;
|
||||
mod macro_action;
|
||||
mod model_utils;
|
||||
mod relays;
|
||||
mod schedules;
|
||||
mod tag;
|
||||
|
||||
pub use controllers::DbController;
|
||||
pub use junction_relay_schedule::DbJunctionRelaySchedule;
|
||||
pub use junction_tag::DbJunctionTag;
|
||||
pub use macro_action::DbMacroAction;
|
||||
pub use r#macro::DbMacro;
|
||||
pub use relays::DbRelay;
|
||||
pub use schedules::{DbPeriods, DbSchedule};
|
||||
pub use tag::DbTag;
|
||||
|
||||
use crate::errors::{DatabaseError, EmgauwaError};
|
||||
|
||||
static MIGRATOR: Migrator = sqlx::migrate!("../migrations"); // defaults to "./migrations"
|
||||
|
||||
pub async fn run_migrations(pool: &Pool<Sqlite>) -> Result<(), EmgauwaError> {
|
||||
log::info!("Running migrations");
|
||||
MIGRATOR.run(pool).await.map_err(DatabaseError::from)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn init(db: &str) -> 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)
|
||||
.connect_with(options)
|
||||
.await?;
|
||||
|
||||
run_migrations(&pool).await?;
|
||||
|
||||
let mut pool_conn = pool.acquire().await?;
|
||||
|
||||
DbSchedule::get_on(&mut pool_conn).await?;
|
||||
DbSchedule::get_off(&mut pool_conn).await?;
|
||||
|
||||
Ok(pool)
|
||||
}
|
137
src/db/model_utils.rs
Normal file
137
src/db/model_utils.rs
Normal file
|
@ -0,0 +1,137 @@
|
|||
use chrono::{NaiveTime, Timelike};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::database::HasArguments;
|
||||
use sqlx::encode::IsNull;
|
||||
use sqlx::error::BoxDynError;
|
||||
use sqlx::sqlite::{SqliteTypeInfo, SqliteValueRef};
|
||||
use sqlx::{Decode, Encode, Sqlite, Type};
|
||||
|
||||
use crate::db::DbPeriods;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
pub struct Period {
|
||||
#[serde(with = "period_format")]
|
||||
pub start: NaiveTime,
|
||||
#[serde(with = "period_format")]
|
||||
pub end: NaiveTime,
|
||||
}
|
||||
|
||||
mod period_format {
|
||||
use chrono::NaiveTime;
|
||||
use serde::{self, Deserialize, Deserializer, Serializer};
|
||||
|
||||
const FORMAT: &str = "%H:%M";
|
||||
|
||||
pub fn serialize<S>(time: &NaiveTime, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let s = format!("{}", time.format(FORMAT));
|
||||
serializer.serialize_str(&s)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveTime, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
NaiveTime::parse_from_str(&s, FORMAT).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl Period {
|
||||
pub fn new(start: NaiveTime, end: NaiveTime) -> Self {
|
||||
Period { start, end }
|
||||
}
|
||||
|
||||
pub fn new_on() -> Self {
|
||||
Period {
|
||||
start: NaiveTime::MIN,
|
||||
end: NaiveTime::MIN,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_on(&self, now: &NaiveTime) -> bool {
|
||||
self.start.eq(&self.end) || (self.start.le(now) && self.end.gt(now))
|
||||
}
|
||||
|
||||
pub fn get_next_time(&self, now: &NaiveTime) -> Option<NaiveTime> {
|
||||
if self.start.eq(&self.end) {
|
||||
// this period is always on
|
||||
return None;
|
||||
}
|
||||
|
||||
let start_after_now = self.start.gt(now);
|
||||
let end_after_now = self.end.gt(now);
|
||||
let start_before_end = self.start.lt(&self.end);
|
||||
|
||||
match (start_after_now, end_after_now, start_before_end) {
|
||||
(false, false, _) => None, // both before now
|
||||
(true, false, _) => Some(self.start), // only start after now
|
||||
(false, true, _) => Some(self.end), // only end after now
|
||||
(true, true, true) => Some(self.start), // both after now but start first
|
||||
(true, true, false) => Some(self.end), // both after now but end first
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Sqlite> for DbPeriods {
|
||||
fn type_info() -> SqliteTypeInfo {
|
||||
<&[u8] as Type<Sqlite>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &SqliteTypeInfo) -> bool {
|
||||
<&[u8] as Type<Sqlite>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q> Encode<'q, Sqlite> for DbPeriods {
|
||||
//noinspection DuplicatedCode
|
||||
fn encode_by_ref(&self, buf: &mut <Sqlite as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
|
||||
<&Vec<u8> as Encode<Sqlite>>::encode(&Vec::from(self), buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Sqlite> for DbPeriods {
|
||||
fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
let blob = <&[u8] as Decode<Sqlite>>::decode(value)?;
|
||||
Ok(DbPeriods::from(Vec::from(blob)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&DbPeriods> for Vec<u8> {
|
||||
fn from(periods: &DbPeriods) -> Vec<u8> {
|
||||
periods
|
||||
.0
|
||||
.iter()
|
||||
.flat_map(|period| {
|
||||
let vec = vec![
|
||||
period.start.hour() as u8,
|
||||
period.start.minute() as u8,
|
||||
period.end.hour() as u8,
|
||||
period.end.minute() as u8,
|
||||
];
|
||||
vec
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for DbPeriods {
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
let mut vec = Vec::new();
|
||||
for i in (3..value.len()).step_by(4) {
|
||||
let start_val_h: u32 = value[i - 3] as u32;
|
||||
let start_val_m: u32 = value[i - 2] as u32;
|
||||
let end_val_h: u32 = value[i - 1] as u32;
|
||||
let end_val_m: u32 = value[i] as u32;
|
||||
vec.push(Period {
|
||||
start: NaiveTime::from_hms_opt(start_val_h, start_val_m, 0)
|
||||
.expect("Failed to parse period start time from database"),
|
||||
end: NaiveTime::from_hms_opt(end_val_h, end_val_m, 0)
|
||||
.expect("Failed to parse period end time from database"),
|
||||
});
|
||||
}
|
||||
DbPeriods(vec)
|
||||
}
|
||||
}
|
177
src/db/relays.rs
Normal file
177
src/db/relays.rs
Normal file
|
@ -0,0 +1,177 @@
|
|||
use std::ops::DerefMut;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::Sqlite;
|
||||
|
||||
use crate::db::{DbController, DbJunctionRelaySchedule, DbJunctionTag, DbSchedule, DbTag};
|
||||
use crate::errors::DatabaseError;
|
||||
use crate::types::Weekday;
|
||||
use crate::utils;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DbRelay {
|
||||
#[serde(skip)]
|
||||
pub id: i64,
|
||||
pub name: String,
|
||||
pub number: i64,
|
||||
#[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")
|
||||
.fetch_all(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
id: i64,
|
||||
) -> Result<Option<DbRelay>, DatabaseError> {
|
||||
sqlx::query_as!(DbRelay, "SELECT * FROM relays WHERE id = ?", id)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get_by_controller_and_num(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
controller: &DbController,
|
||||
number: i64,
|
||||
) -> Result<Option<DbRelay>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbRelay,
|
||||
"SELECT * FROM relays WHERE controller_id = ? AND number = ?",
|
||||
controller.id,
|
||||
number
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get_by_controller_and_num_or_create(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
controller: &DbController,
|
||||
number: i64,
|
||||
new_name: &str,
|
||||
) -> Result<(DbRelay, bool), DatabaseError> {
|
||||
match DbRelay::get_by_controller_and_num(conn, controller, number).await? {
|
||||
Some(relay) => Ok((relay, false)),
|
||||
None => {
|
||||
let relay = DbRelay::create(conn, new_name, number, controller).await?;
|
||||
Ok((relay, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_by_tag(
|
||||
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(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn create(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_name: &str,
|
||||
new_number: i64,
|
||||
new_controller: &DbController,
|
||||
) -> Result<DbRelay, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbRelay,
|
||||
"INSERT INTO relays (name, number, controller_id) VALUES (?, ?, ?) RETURNING *",
|
||||
new_name,
|
||||
new_number,
|
||||
new_controller.id,
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await?
|
||||
.ok_or(DatabaseError::InsertGetError)
|
||||
}
|
||||
|
||||
pub async fn delete(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
|
||||
sqlx::query!("DELETE FROM relays WHERE id = ?", self.id)
|
||||
.execute(conn.deref_mut())
|
||||
.await
|
||||
.map(|res| match res.rows_affected() {
|
||||
0 => Err(DatabaseError::DeleteError),
|
||||
_ => Ok(()),
|
||||
})?
|
||||
}
|
||||
|
||||
pub async fn update(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_name: &str,
|
||||
) -> Result<DbRelay, DatabaseError> {
|
||||
sqlx::query!("UPDATE relays SET name = ? WHERE id = ?", new_name, self.id,)
|
||||
.execute(conn.deref_mut())
|
||||
.await?;
|
||||
|
||||
DbRelay::get(conn, self.id)
|
||||
.await?
|
||||
.ok_or(DatabaseError::UpdateGetError)
|
||||
}
|
||||
|
||||
pub async fn get_controller(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
) -> Result<DbController, DatabaseError> {
|
||||
DbController::get(conn, self.controller_id)
|
||||
.await?
|
||||
.ok_or(DatabaseError::NotFound)
|
||||
}
|
||||
|
||||
pub async fn get_tags(
|
||||
&self,
|
||||
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(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn set_tags(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_tags: &[String],
|
||||
) -> Result<(), DatabaseError> {
|
||||
sqlx::query!("DELETE FROM junction_tag WHERE relay_id = ?", self.id)
|
||||
.execute(conn.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?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn reload(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
) -> Result<DbRelay, DatabaseError> {
|
||||
Self::get(conn, self.id)
|
||||
.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)
|
||||
}
|
||||
}
|
209
src/db/schedules.rs
Normal file
209
src/db/schedules.rs
Normal file
|
@ -0,0 +1,209 @@
|
|||
use std::borrow::Borrow;
|
||||
use std::ops::DerefMut;
|
||||
|
||||
use chrono::NaiveTime;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::Sqlite;
|
||||
|
||||
use crate::db::model_utils::Period;
|
||||
use crate::db::{DbJunctionTag, DbTag};
|
||||
use crate::errors::DatabaseError;
|
||||
use crate::types::ScheduleUid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DbSchedule {
|
||||
#[serde(skip)]
|
||||
pub id: i64,
|
||||
#[serde(rename = "id")]
|
||||
pub uid: ScheduleUid,
|
||||
pub name: String,
|
||||
pub periods: DbPeriods,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
pub struct DbPeriods(pub Vec<Period>);
|
||||
|
||||
impl DbSchedule {
|
||||
pub async fn get_all(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
) -> Result<Vec<DbSchedule>, DatabaseError> {
|
||||
sqlx::query_as!(DbSchedule, "SELECT * FROM schedules")
|
||||
.fetch_all(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
id: i64,
|
||||
) -> Result<Option<DbSchedule>, DatabaseError> {
|
||||
sqlx::query_as!(DbSchedule, "SELECT * FROM schedules WHERE id = ?", id)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get_by_uid(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
filter_uid: &ScheduleUid,
|
||||
) -> Result<Option<DbSchedule>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbSchedule,
|
||||
"SELECT * FROM schedules WHERE uid = ?",
|
||||
filter_uid
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get_by_tag(
|
||||
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(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn delete_by_uid(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
filter_uid: ScheduleUid,
|
||||
) -> Result<(), DatabaseError> {
|
||||
let filter_uid = match filter_uid {
|
||||
ScheduleUid::Off => Err(DatabaseError::Protected),
|
||||
ScheduleUid::On => Err(DatabaseError::Protected),
|
||||
ScheduleUid::Any(_) => Ok(filter_uid),
|
||||
}?;
|
||||
|
||||
if sqlx::query_scalar!("SELECT 1 FROM schedules WHERE uid = ?", filter_uid)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await?
|
||||
.is_none()
|
||||
{
|
||||
return Err(DatabaseError::NotFound);
|
||||
}
|
||||
|
||||
sqlx::query!("DELETE FROM schedules WHERE uid = ?", filter_uid)
|
||||
.execute(conn.deref_mut())
|
||||
.await
|
||||
.map(|res| match res.rows_affected() {
|
||||
0 => Err(DatabaseError::DeleteError),
|
||||
_ => Ok(()),
|
||||
})?
|
||||
}
|
||||
|
||||
pub async fn create(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_uid: ScheduleUid,
|
||||
new_name: &str,
|
||||
new_periods: &DbPeriods,
|
||||
) -> Result<DbSchedule, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbSchedule,
|
||||
"INSERT INTO schedules (uid, name, periods) VALUES (?, ?, ?) RETURNING *",
|
||||
new_uid,
|
||||
new_name,
|
||||
new_periods,
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await?
|
||||
.ok_or(DatabaseError::InsertGetError)
|
||||
}
|
||||
|
||||
pub async fn get_by_uid_or_create(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
uid: ScheduleUid,
|
||||
name: &str,
|
||||
periods: &DbPeriods,
|
||||
) -> Result<(DbSchedule, bool), DatabaseError> {
|
||||
match DbSchedule::get_by_uid(conn, &uid).await? {
|
||||
Some(schedule) => Ok((schedule, false)),
|
||||
None => {
|
||||
let schedule = DbSchedule::create(conn, 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? {
|
||||
return Ok(schedule);
|
||||
}
|
||||
let periods = DbPeriods(vec![Period::new_on()]);
|
||||
Self::create(conn, 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? {
|
||||
return Ok(schedule);
|
||||
}
|
||||
let periods = DbPeriods(vec![]);
|
||||
Self::create(conn, ScheduleUid::Off, "Off", &periods).await
|
||||
}
|
||||
|
||||
pub async fn update(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_name: &str,
|
||||
new_periods: &DbPeriods,
|
||||
) -> Result<DbSchedule, DatabaseError> {
|
||||
// overwrite periods on protected schedules
|
||||
let new_periods = match self.uid {
|
||||
ScheduleUid::Off | ScheduleUid::On => self.periods.borrow(),
|
||||
ScheduleUid::Any(_) => new_periods,
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE schedules SET name = ?, periods = ? WHERE id = ?",
|
||||
new_name,
|
||||
new_periods,
|
||||
self.id,
|
||||
)
|
||||
.execute(conn.deref_mut())
|
||||
.await?;
|
||||
|
||||
DbSchedule::get(conn, self.id)
|
||||
.await?
|
||||
.ok_or(DatabaseError::UpdateGetError)
|
||||
}
|
||||
|
||||
pub async fn get_tags(
|
||||
&self,
|
||||
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(conn.deref_mut())
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn set_tags(
|
||||
&self,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_tags: &[String],
|
||||
) -> Result<(), DatabaseError> {
|
||||
sqlx::query!("DELETE FROM junction_tag WHERE schedule_id = ?", self.id)
|
||||
.execute(conn.deref_mut())
|
||||
.await?;
|
||||
|
||||
for new_tag in new_tags {
|
||||
let tag: DbTag = DbTag::get_by_tag_or_create(conn, new_tag).await?;
|
||||
DbJunctionTag::link_schedule(conn, &tag, self).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_on(&self, now: &NaiveTime) -> bool {
|
||||
self.periods.0.iter().any(|period| period.is_on(now))
|
||||
}
|
||||
|
||||
pub fn get_next_time(&self, now: &NaiveTime) -> Option<NaiveTime> {
|
||||
self.periods
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|period| period.get_next_time(now))
|
||||
.min()
|
||||
}
|
||||
}
|
91
src/db/tag.rs
Normal file
91
src/db/tag.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
use std::ops::DerefMut;
|
||||
|
||||
use serde_derive::Serialize;
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::Sqlite;
|
||||
|
||||
use crate::errors::DatabaseError;
|
||||
|
||||
#[derive(Debug, Serialize, Clone)]
|
||||
pub struct DbTag {
|
||||
pub id: i64,
|
||||
pub tag: String,
|
||||
}
|
||||
|
||||
impl DbTag {
|
||||
pub async fn create(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_tag: &str,
|
||||
) -> Result<DbTag, DatabaseError> {
|
||||
if new_tag.is_empty() {
|
||||
return Err(DatabaseError::EmptyDataInsert);
|
||||
}
|
||||
|
||||
sqlx::query_as!(
|
||||
DbTag,
|
||||
"INSERT INTO tags (tag) VALUES (?) RETURNING *",
|
||||
new_tag
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await?
|
||||
.ok_or(DatabaseError::InsertGetError)
|
||||
}
|
||||
|
||||
pub async fn get_all(conn: &mut PoolConnection<Sqlite>) -> Result<Vec<DbTag>, DatabaseError> {
|
||||
sqlx::query_as!(DbTag, "SELECT * FROM tags")
|
||||
.fetch_all(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
id: i64,
|
||||
) -> Result<Option<DbTag>, DatabaseError> {
|
||||
sqlx::query_as!(DbTag, "SELECT * FROM tags WHERE id = ?", id)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn get_by_tag_or_create(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
target_tag: &str,
|
||||
) -> Result<DbTag, DatabaseError> {
|
||||
match DbTag::get_by_tag(conn, target_tag).await? {
|
||||
Some(tag) => Ok(tag),
|
||||
None => DbTag::create(conn, target_tag).await,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_by_tag(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
target_tag: &str,
|
||||
) -> Result<Option<DbTag>, DatabaseError> {
|
||||
sqlx::query_as!(DbTag, "SELECT * FROM tags WHERE tag = ?", target_tag)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
}
|
||||
|
||||
pub async fn delete_by_tag(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
filter_tag: &str,
|
||||
) -> Result<(), DatabaseError> {
|
||||
if sqlx::query_scalar!("SELECT 1 FROM tags WHERE tag = ?", filter_tag)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await?
|
||||
.is_none()
|
||||
{
|
||||
return Err(DatabaseError::NotFound);
|
||||
}
|
||||
|
||||
sqlx::query!("DELETE FROM tags WHERE tag = ?", filter_tag)
|
||||
.execute(conn.deref_mut())
|
||||
.await
|
||||
.map(|res| match res.rows_affected() {
|
||||
0 => Err(DatabaseError::DeleteError),
|
||||
_ => Ok(()),
|
||||
})?
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue