|
|
|
@ -1,52 +1,19 @@
@@ -1,52 +1,19 @@
|
|
|
|
|
use super::sql_json::SqlJson; |
|
|
|
|
use super::DbConn; |
|
|
|
|
use sprinklers_core::{ |
|
|
|
|
model::{Program, ProgramId, ProgramItem, ProgramSequence, SectionId}, |
|
|
|
|
model::{ |
|
|
|
|
Program, ProgramId, ProgramItem, ProgramSequence, ProgramUpdateData, Programs, SectionId, |
|
|
|
|
}, |
|
|
|
|
schedule::Schedule, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
use rusqlite::{ |
|
|
|
|
types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, Value, ValueRef}, |
|
|
|
|
Error as SqlError, Result as SqlResult, Row as SqlRow, ToSql, |
|
|
|
|
}; |
|
|
|
|
use serde::{Deserialize, Serialize}; |
|
|
|
|
|
|
|
|
|
pub struct SqlJson<T>(T); |
|
|
|
|
|
|
|
|
|
impl<T> SqlJson<T> { |
|
|
|
|
pub fn into_inner(self) -> T { |
|
|
|
|
self.0 |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl<T> FromSql for SqlJson<T> |
|
|
|
|
where |
|
|
|
|
for<'de> T: Deserialize<'de>, |
|
|
|
|
{ |
|
|
|
|
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { |
|
|
|
|
if let ValueRef::Text(text) = value { |
|
|
|
|
let deser_value: T = |
|
|
|
|
serde_json::from_slice(text).map_err(|err| FromSqlError::Other(Box::new(err)))?; |
|
|
|
|
Ok(SqlJson(deser_value)) |
|
|
|
|
} else { |
|
|
|
|
Err(FromSqlError::InvalidType) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl<T> ToSql for SqlJson<T> |
|
|
|
|
where |
|
|
|
|
T: Serialize, |
|
|
|
|
{ |
|
|
|
|
fn to_sql(&self) -> SqlResult<ToSqlOutput<'_>> { |
|
|
|
|
serde_json::to_string(&self.0) |
|
|
|
|
.map(|serialized| ToSqlOutput::Owned(Value::Text(serialized))) |
|
|
|
|
.map_err(|err| SqlError::ToSqlConversionFailure(Box::new(err))) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
use eyre::Result; |
|
|
|
|
use rusqlite::{params, Row, ToSql, Transaction, NO_PARAMS}; |
|
|
|
|
|
|
|
|
|
type SqlProgramSequence = SqlJson<ProgramSequence>; |
|
|
|
|
type SqlSchedule = SqlJson<Schedule>; |
|
|
|
|
|
|
|
|
|
pub fn from_sql<'a>(row: &SqlRow<'a>) -> SqlResult<Program> { |
|
|
|
|
fn from_sql<'a>(row: &Row<'a>) -> rusqlite::Result<Program> { |
|
|
|
|
Ok(Program { |
|
|
|
|
id: row.get(0)?, |
|
|
|
|
name: row.get(1)?, |
|
|
|
@ -56,21 +23,21 @@ pub fn from_sql<'a>(row: &SqlRow<'a>) -> SqlResult<Program> {
@@ -56,21 +23,21 @@ pub fn from_sql<'a>(row: &SqlRow<'a>) -> SqlResult<Program> {
|
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub struct SqlProgram<'a> { |
|
|
|
|
struct SqlProgramUpdate<'a> { |
|
|
|
|
id: ProgramId, |
|
|
|
|
name: &'a String, |
|
|
|
|
enabled: bool, |
|
|
|
|
schedule: SqlJson<&'a Schedule>, |
|
|
|
|
name: Option<&'a String>, |
|
|
|
|
enabled: Option<bool>, |
|
|
|
|
schedule: Option<SqlJson<&'a Schedule>>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl<'a> IntoIterator for &'a SqlProgram<'a> { |
|
|
|
|
impl<'a> IntoIterator for &'a SqlProgramUpdate<'a> { |
|
|
|
|
type Item = &'a dyn ToSql; |
|
|
|
|
type IntoIter = std::vec::IntoIter<Self::Item>; |
|
|
|
|
|
|
|
|
|
fn into_iter(self) -> Self::IntoIter { |
|
|
|
|
vec![ |
|
|
|
|
&self.id as &dyn ToSql, |
|
|
|
|
self.name, |
|
|
|
|
&self.name, |
|
|
|
|
&self.enabled, |
|
|
|
|
&self.schedule, |
|
|
|
|
] |
|
|
|
@ -78,16 +45,16 @@ impl<'a> IntoIterator for &'a SqlProgram<'a> {
@@ -78,16 +45,16 @@ impl<'a> IntoIterator for &'a SqlProgram<'a> {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn as_sql(program: &Program) -> SqlProgram { |
|
|
|
|
SqlProgram { |
|
|
|
|
id: program.id, |
|
|
|
|
name: &program.name, |
|
|
|
|
fn update_as_sql(id: ProgramId, program: &ProgramUpdateData) -> SqlProgramUpdate { |
|
|
|
|
SqlProgramUpdate { |
|
|
|
|
id, |
|
|
|
|
name: program.name.as_ref(), |
|
|
|
|
enabled: program.enabled, |
|
|
|
|
schedule: SqlJson(&program.schedule), |
|
|
|
|
schedule: program.schedule.as_ref().map(SqlJson), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub struct SqlProgramItem { |
|
|
|
|
struct SqlProgramItem { |
|
|
|
|
program_id: ProgramId, |
|
|
|
|
seq_num: isize, |
|
|
|
|
section_id: SectionId, |
|
|
|
@ -109,7 +76,7 @@ impl<'a> IntoIterator for &'a SqlProgramItem {
@@ -109,7 +76,7 @@ impl<'a> IntoIterator for &'a SqlProgramItem {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn item_as_sql( |
|
|
|
|
fn item_as_sql( |
|
|
|
|
program_item: &ProgramItem, |
|
|
|
|
program_id: ProgramId, |
|
|
|
|
seq_num: usize, |
|
|
|
@ -122,11 +89,71 @@ pub fn item_as_sql(
@@ -122,11 +89,71 @@ pub fn item_as_sql(
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn sequence_as_sql<'a>(program: &'a Program) -> impl Iterator<Item = SqlProgramItem> + 'a { |
|
|
|
|
let program_id = program.id; |
|
|
|
|
program |
|
|
|
|
.sequence |
|
|
|
|
fn sequence_as_sql<'a>( |
|
|
|
|
program_id: ProgramId, |
|
|
|
|
sequence: &'a [ProgramItem], |
|
|
|
|
) -> impl Iterator<Item = SqlProgramItem> + 'a { |
|
|
|
|
sequence |
|
|
|
|
.iter() |
|
|
|
|
.enumerate() |
|
|
|
|
.map(move |(seq_num, item)| item_as_sql(item, program_id, seq_num)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn query_programs(conn: &DbConn) -> Result<Programs> { |
|
|
|
|
let query_sql = "\ |
|
|
|
|
SELECT p.id, p.name, p.enabled, p.schedule, ps.sequence |
|
|
|
|
FROM programs AS p |
|
|
|
|
INNER JOIN program_sequences AS ps ON ps.program_id = p.id;"; |
|
|
|
|
let mut statement = conn.prepare_cached(query_sql)?; |
|
|
|
|
let rows = statement.query_map(NO_PARAMS, from_sql)?; |
|
|
|
|
let mut programs = Programs::new(); |
|
|
|
|
for row in rows { |
|
|
|
|
let program = row?; |
|
|
|
|
programs.insert(program.id, program.into()); |
|
|
|
|
} |
|
|
|
|
Ok(programs) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn query_program_by_id(conn: &DbConn, id: ProgramId) -> Result<Program> { |
|
|
|
|
let query_sql = "\ |
|
|
|
|
SELECT p.id, p.name, p.enabled, p.schedule, ps.sequence |
|
|
|
|
FROM programs AS p |
|
|
|
|
INNER JOIN program_sequences AS ps ON ps.program_id = p.id |
|
|
|
|
WHERE p.id = ?1;"; |
|
|
|
|
let mut statement = conn.prepare_cached(query_sql)?; |
|
|
|
|
Ok(statement.query_row(params![id], from_sql)?) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn update_program( |
|
|
|
|
trans: &mut Transaction, |
|
|
|
|
id: ProgramId, |
|
|
|
|
prog: &ProgramUpdateData, |
|
|
|
|
) -> Result<()> { |
|
|
|
|
let save = trans.savepoint()?; |
|
|
|
|
let conn = &*save; |
|
|
|
|
let update_sql = "\ |
|
|
|
|
UPDATE programs |
|
|
|
|
SET name = ifnull(?2, name), |
|
|
|
|
enabled = ifnull(?3, enabled), |
|
|
|
|
schedule = ifnull(?4, schedule) |
|
|
|
|
WHERE id = ?1;"; |
|
|
|
|
conn.prepare_cached(update_sql)? |
|
|
|
|
.execute(&update_as_sql(id, prog))?; |
|
|
|
|
if let Some(sequence) = &prog.sequence { |
|
|
|
|
let clear_seq_sql = "\ |
|
|
|
|
DELETE |
|
|
|
|
FROM program_sequence_items |
|
|
|
|
WHERE program_id = ?1;"; |
|
|
|
|
conn.prepare_cached(clear_seq_sql)?.execute(params![id])?; |
|
|
|
|
let insert_seq_sql = "\ |
|
|
|
|
INSERT INTO program_sequence_items (program_id, seq_num, section_id, duration) |
|
|
|
|
VALUES (?1, ?2, ?3, ?4);"; |
|
|
|
|
let mut insert_seq = conn.prepare_cached(insert_seq_sql)?; |
|
|
|
|
for params in sequence_as_sql(id, sequence) { |
|
|
|
|
insert_seq.execute(¶ms)?; |
|
|
|
|
} |
|
|
|
|
drop(insert_seq); |
|
|
|
|
} |
|
|
|
|
save.commit()?; |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|