Initial commit
This commit is contained in:
		
						commit
						07785918c1
					
				
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| /target | ||||
| /.vscode | ||||
| /*.db | ||||
| Cargo.lock | ||||
							
								
								
									
										15
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| [package] | ||||
| name = "sprinklers_rs" | ||||
| version = "0.1.0" | ||||
| authors = ["Alex Mikhalev <alexmikhalevalex@gmail.com>"] | ||||
| edition = "2018" | ||||
| 
 | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
| 
 | ||||
| [dependencies] | ||||
| rusqlite = "0.23.1" | ||||
| log = "0.4.11" | ||||
| env_logger = "0.7.1" | ||||
| color-eyre = "0.5.1" | ||||
| eyre = "0.6.0" | ||||
| thiserror = "1.0.20" | ||||
							
								
								
									
										17
									
								
								src/db.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/db.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| use crate::migrations::{Migrations, SimpleMigration}; | ||||
| 
 | ||||
| pub fn create_migrations() -> Migrations { | ||||
|     let mut migs = Migrations::new(); | ||||
|     migs.add( | ||||
|         1, | ||||
|         SimpleMigration::new_box( | ||||
|             "CREATE TABLE sections (
 | ||||
|                         id INTEGER PRIMARY KEY, | ||||
|                         name TEXT NOT NULL, | ||||
|                         interface_id INTEGER NOT NULL | ||||
|                     );",
 | ||||
|             "DROP TABLE sections;", | ||||
|         ), | ||||
|     ); | ||||
|     migs | ||||
| } | ||||
							
								
								
									
										27
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| use rusqlite::NO_PARAMS; | ||||
| use rusqlite::Connection as DbConnection; | ||||
| use color_eyre::eyre::Result; | ||||
| 
 | ||||
| mod section_interface; | ||||
| mod model; | ||||
| mod db; | ||||
| mod migrations; | ||||
| 
 | ||||
| fn setup_db() -> Result<()> { | ||||
|     // let conn = DbConnection::open_in_memory()?;
 | ||||
|     let mut conn = DbConnection::open("test.db")?; | ||||
| 
 | ||||
|     let migs = db::create_migrations(); | ||||
|     migs.apply(&mut conn)?; | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn main() -> Result<()> { | ||||
|     env_logger::init(); | ||||
|     color_eyre::install()?; | ||||
|     println!("Hello, world!"); | ||||
|     setup_db().unwrap(); | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
							
								
								
									
										122
									
								
								src/migrations.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								src/migrations.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | ||||
| use rusqlite::NO_PARAMS; | ||||
| use rusqlite::{params, Connection}; | ||||
| use std::collections::BTreeMap; | ||||
| use std::ops::Bound::{Excluded, Unbounded}; | ||||
| use thiserror::Error; | ||||
| use log::debug; | ||||
| 
 | ||||
| #[derive(Debug, Error)] | ||||
| pub enum MigrationError { | ||||
|     #[error("sql error: {0}")] | ||||
|     SqlError(#[from] rusqlite::Error), | ||||
|     #[error("database version {0} too new to migrate")] | ||||
|     VersionTooNew(MigrationVersion), | ||||
| } | ||||
| 
 | ||||
| pub type MigrationResult<T> = Result<T, MigrationError>; | ||||
| 
 | ||||
| pub trait Migration { | ||||
|     fn up(&self, conn: &Connection) -> MigrationResult<()>; | ||||
|     fn down(&self, conn: &Connection) -> MigrationResult<()>; | ||||
| } | ||||
| 
 | ||||
| pub struct SimpleMigration { | ||||
|     pub up_sql: String, | ||||
|     pub down_sql: String, | ||||
| } | ||||
| 
 | ||||
| impl SimpleMigration { | ||||
|     pub fn new<T1: ToString, T2: ToString>(up_sql: T1, down_sql: T2) -> Self { | ||||
|         Self { | ||||
|             up_sql: up_sql.to_string(), | ||||
|             down_sql: down_sql.to_string(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn new_box<T1: ToString, T2: ToString>(up_sql: T1, down_sql: T2) -> Box<dyn Migration> { | ||||
|         Box::new(Self::new(up_sql, down_sql)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Migration for SimpleMigration { | ||||
|     fn up(&self, conn: &Connection) -> MigrationResult<()> { | ||||
|         conn.execute(&self.up_sql, NO_PARAMS)?; | ||||
|         Ok(()) | ||||
|     } | ||||
|     fn down(&self, conn: &Connection) -> MigrationResult<()> { | ||||
|         conn.execute(&self.down_sql, NO_PARAMS)?; | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub type MigrationVersion = u32; | ||||
| pub const NO_MIGRATIONS: MigrationVersion = 0; | ||||
| 
 | ||||
| pub fn get_db_version(conn: &Connection) -> MigrationResult<MigrationVersion> { | ||||
|     let table_count: u32 = conn.query_row( | ||||
|         "SELECT COUNT(*) FROM sqlite_master
 | ||||
|                          WHERE type='table' AND name='db_version'",
 | ||||
|         NO_PARAMS, | ||||
|         |row| row.get(0), | ||||
|     )?; | ||||
|     if table_count == 0 { | ||||
|         return Ok(NO_MIGRATIONS); | ||||
|     } | ||||
| 
 | ||||
|     let version: u32 = conn.query_row( | ||||
|         "SELECT version FROM db_version WHERE id = 1", | ||||
|         NO_PARAMS, | ||||
|         |row| row.get(0), | ||||
|     )?; | ||||
|     Ok(version) | ||||
| } | ||||
| 
 | ||||
| pub fn set_db_version(conn: &Connection, version: MigrationVersion) -> MigrationResult<()> { | ||||
|     conn.execute( | ||||
|         " | ||||
|         CREATE TABLE IF NOT EXISTS db_version ( | ||||
|             id INTEGER PRIMARY KEY, | ||||
|             version INTEGER | ||||
|         );", NO_PARAMS)?;
 | ||||
|     conn.execute( | ||||
|         " | ||||
|         INSERT OR REPLACE INTO db_version (id, version) | ||||
|             VALUES (1, ?1);",
 | ||||
|         params![version])?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| pub struct Migrations { | ||||
|     migrations: BTreeMap<MigrationVersion, Box<dyn Migration>>, | ||||
| } | ||||
| 
 | ||||
| impl Migrations { | ||||
|     pub fn new() -> Self { | ||||
|         Self { | ||||
|             migrations: BTreeMap::new(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn add(&mut self, version: MigrationVersion, migration: Box<dyn Migration>) { | ||||
|         self.migrations.insert(version, migration); | ||||
|     } | ||||
| 
 | ||||
|     pub fn apply(&self, conn: &mut Connection) -> MigrationResult<()> { | ||||
|         let db_version = get_db_version(conn)?; | ||||
|         if db_version != 0 && !self.migrations.contains_key(&db_version) { | ||||
|             return Err(MigrationError::VersionTooNew(db_version)); | ||||
|         } | ||||
|         let mig_range = self.migrations.range( | ||||
|             (Excluded(db_version), Unbounded)); | ||||
|         let mut trans = conn.transaction()?; | ||||
|         let mut last_ver: MigrationVersion = 0; | ||||
|         for (ver, mig) in mig_range { | ||||
|             debug!("applying migration version {}", ver); | ||||
|             mig.up(&mut trans)?; | ||||
|             last_ver = *ver; | ||||
|         } | ||||
|         set_db_version(&trans, last_ver)?; | ||||
|         trans.commit()?; | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1
									
								
								src/model/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/model/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| mod section; | ||||
							
								
								
									
										21
									
								
								src/model/section.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/model/section.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| use crate::section_interface::SectionId; | ||||
| use rusqlite::Row; | ||||
| use std::convert::TryFrom; | ||||
| 
 | ||||
| pub struct Section { | ||||
|     pub id: u32, | ||||
|     pub name: String, | ||||
|     pub interface_id: SectionId, | ||||
| } | ||||
| 
 | ||||
| impl<'a> TryFrom<&Row<'a>> for Section { | ||||
|     type Error = rusqlite::Error; | ||||
|     fn try_from(row: &Row<'a>) -> Result<Section, Self::Error> { | ||||
|         Ok(Section { | ||||
|             id: row.get(0)?, | ||||
|             name: row.get(1)?, | ||||
|             interface_id: row.get(2)?, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										10
									
								
								src/section_interface.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/section_interface.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| pub type SectionId = u32; | ||||
| 
 | ||||
| pub trait SectionInterface { | ||||
|     fn num_sections() -> SectionId; | ||||
|     fn set_section(id: SectionId, running: bool); | ||||
|     fn get_section(id: SectionId) -> bool; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user