Browse Source

Initial commit

drone-volume-cache
Alex Mikhalev 5 years ago
commit
07785918c1
  1. 4
      .gitignore
  2. 15
      Cargo.toml
  3. 17
      src/db.rs
  4. 27
      src/main.rs
  5. 122
      src/migrations.rs
  6. 1
      src/model/mod.rs
  7. 21
      src/model/section.rs
  8. 10
      src/section_interface.rs

4
.gitignore vendored

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
/target
/.vscode
/*.db
Cargo.lock

15
Cargo.toml

@ -0,0 +1,15 @@ @@ -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

@ -0,0 +1,17 @@ @@ -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

@ -0,0 +1,27 @@ @@ -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

@ -0,0 +1,122 @@ @@ -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

@ -0,0 +1 @@ @@ -0,0 +1 @@
mod section;

21
src/model/section.rs

@ -0,0 +1,21 @@ @@ -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

@ -0,0 +1,10 @@ @@ -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…
Cancel
Save