Implement updating program
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			This commit is contained in:
		
							parent
							
								
									cc1a5bb09d
								
							
						
					
					
						commit
						89d7b01a76
					
				| @ -8,10 +8,10 @@ pub use migrations::create_migrations; | ||||
| 
 | ||||
| pub use rusqlite::Connection as DbConn; | ||||
| 
 | ||||
| use sprinklers_core::model::{Programs, Sections}; | ||||
| use sprinklers_core::model::{Program, Programs, Sections}; | ||||
| 
 | ||||
| use eyre::Result; | ||||
| use rusqlite::NO_PARAMS; | ||||
| use rusqlite::{params, NO_PARAMS}; | ||||
| 
 | ||||
| pub fn setup_db() -> Result<DbConn> { | ||||
|     // let conn = DbConn::open_in_memory()?;
 | ||||
| @ -40,7 +40,7 @@ pub fn query_sections(conn: &DbConn) -> Result<Sections> { | ||||
| pub fn query_programs(conn: &DbConn) -> Result<Programs> { | ||||
|     let mut statement = conn.prepare_cached( | ||||
|         " | ||||
|         SELECT p.id, p.name, ps.sequence, p.enabled, p.schedule | ||||
|         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;",
 | ||||
| @ -53,3 +53,34 @@ pub fn query_programs(conn: &DbConn) -> Result<Programs> { | ||||
|     } | ||||
|     Ok(programs) | ||||
| } | ||||
| 
 | ||||
| pub fn update_program(conn: &mut DbConn, prog: &Program) -> Result<()> { | ||||
|     let trans = conn.transaction()?; | ||||
|     trans | ||||
|         .prepare_cached( | ||||
|             " | ||||
|         UPDATE programs | ||||
|             SET (name, enabled, schedule) = (?2, ?3, ?4) | ||||
|             WHERE id = ?1;",
 | ||||
|         )? | ||||
|         .execute(&program::as_sql(prog))?; | ||||
|     trans | ||||
|         .prepare_cached( | ||||
|             " | ||||
|         DELETE FROM program_sequence_items AS psi | ||||
|             WHERE psi.program_id = ?1;",
 | ||||
|         )? | ||||
|         .execute(params![prog.id])?; | ||||
|     let mut insert_prog_seq = trans.prepare_cached( | ||||
|         " | ||||
|         INSERT INTO program_sequence_items | ||||
|             (program_id, seq_num, section_id, duration) | ||||
|             VALUES (?1, ?2, ?3, ?4);",
 | ||||
|     )?; | ||||
|     for params in program::sequence_as_sql(prog) { | ||||
|         insert_prog_seq.execute(¶ms)?; | ||||
|     } | ||||
|     drop(insert_prog_seq); | ||||
|     trans.commit()?; | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| @ -1,18 +1,18 @@ | ||||
| use sprinklers_core::{ | ||||
|     model::{Program, ProgramSequence}, | ||||
|     model::{Program, ProgramId, ProgramItem, ProgramSequence, SectionId}, | ||||
|     schedule::Schedule, | ||||
| }; | ||||
| 
 | ||||
| use rusqlite::{ | ||||
|     types::{FromSql, FromSqlError, FromSqlResult, ValueRef}, | ||||
|     Result as SqlResult, Row as SqlRow, | ||||
|     types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, Value, ValueRef}, | ||||
|     Error as SqlError, Result as SqlResult, Row as SqlRow, ToSql, | ||||
| }; | ||||
| use serde::Deserialize; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| struct SqlJson<T>(T); | ||||
| pub struct SqlJson<T>(T); | ||||
| 
 | ||||
| impl<T> SqlJson<T> { | ||||
|     fn into_inner(self) -> T { | ||||
|     pub fn into_inner(self) -> T { | ||||
|         self.0 | ||||
|     } | ||||
| } | ||||
| @ -32,6 +32,17 @@ where | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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))) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| type SqlProgramSequence = SqlJson<ProgramSequence>; | ||||
| type SqlSchedule = SqlJson<Schedule>; | ||||
| 
 | ||||
| @ -39,8 +50,83 @@ pub fn from_sql<'a>(row: &SqlRow<'a>) -> SqlResult<Program> { | ||||
|     Ok(Program { | ||||
|         id: row.get(0)?, | ||||
|         name: row.get(1)?, | ||||
|         sequence: row.get::<_, SqlProgramSequence>(2)?.into_inner(), | ||||
|         enabled: row.get(3)?, | ||||
|         schedule: row.get::<_, SqlSchedule>(4)?.into_inner(), | ||||
|         enabled: row.get(2)?, | ||||
|         schedule: row.get::<_, SqlSchedule>(3)?.into_inner(), | ||||
|         sequence: row.get::<_, SqlProgramSequence>(4)?.into_inner(), | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| pub struct SqlProgram<'a> { | ||||
|     id: ProgramId, | ||||
|     name: &'a String, | ||||
|     enabled: bool, | ||||
|     schedule: SqlJson<&'a Schedule>, | ||||
| } | ||||
| 
 | ||||
| impl<'a> IntoIterator for &'a SqlProgram<'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.enabled, | ||||
|             &self.schedule, | ||||
|         ] | ||||
|         .into_iter() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn as_sql(program: &Program) -> SqlProgram { | ||||
|     SqlProgram { | ||||
|         id: program.id, | ||||
|         name: &program.name, | ||||
|         enabled: program.enabled, | ||||
|         schedule: SqlJson(&program.schedule), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct SqlProgramItem { | ||||
|     program_id: ProgramId, | ||||
|     seq_num: isize, | ||||
|     section_id: SectionId, | ||||
|     duration: f64, | ||||
| } | ||||
| 
 | ||||
| impl<'a> IntoIterator for &'a SqlProgramItem { | ||||
|     type Item = &'a dyn ToSql; | ||||
|     type IntoIter = std::vec::IntoIter<Self::Item>; | ||||
| 
 | ||||
|     fn into_iter(self) -> Self::IntoIter { | ||||
|         vec![ | ||||
|             &self.program_id as &dyn ToSql, | ||||
|             &self.seq_num, | ||||
|             &self.section_id, | ||||
|             &self.duration, | ||||
|         ] | ||||
|         .into_iter() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn item_as_sql( | ||||
|     program_item: &ProgramItem, | ||||
|     program_id: ProgramId, | ||||
|     seq_num: usize, | ||||
| ) -> SqlProgramItem { | ||||
|     SqlProgramItem { | ||||
|         program_id, | ||||
|         seq_num: (seq_num + 1) as isize, | ||||
|         section_id: program_item.section_id, | ||||
|         duration: program_item.duration.as_secs_f64(), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn sequence_as_sql<'a>(program: &'a Program) -> impl Iterator<Item = SqlProgramItem> + 'a { | ||||
|     let program_id = program.id; | ||||
|     program | ||||
|         .sequence | ||||
|         .iter() | ||||
|         .enumerate() | ||||
|         .map(move |(seq_num, item)| item_as_sql(item, program_id, seq_num)) | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user