diff --git a/Cargo.lock b/Cargo.lock index 576a236..8f3c409 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -146,6 +146,7 @@ dependencies = [ "clap-verbosity-flag", "home", "log", + "resource", "rusqlite", "serde", "serde_json", @@ -335,6 +336,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "resource" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a7f6703c396037a02da99195e49138c37f3cc5146cb95f2f7d26debc0c5622" + [[package]] name = "rusqlite" version = "0.28.0" diff --git a/Cargo.toml b/Cargo.toml index a77801a..904fa5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,5 @@ simple_logger = "4.0.0" log = "0.4.17" home = "0.5.4" sha2 = "0.10.6" -serde_json = "1.0" \ No newline at end of file +serde_json = "1.0" +resource = "0.5.0" \ No newline at end of file diff --git a/src/migrations/create_tables_001.sql b/migrations/create_tables_001.sql similarity index 63% rename from src/migrations/create_tables_001.sql rename to migrations/create_tables_001.sql index e46b711..e40c7c2 100644 --- a/src/migrations/create_tables_001.sql +++ b/migrations/create_tables_001.sql @@ -2,23 +2,25 @@ CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, - public_key TEXT NOT NULL + public_key TEXT NOT NULL, + foo INTEGER ); CREATE TABLE IF NOT EXISTS security_types ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL UNIQUE + name TEXT PRIMARY KEY ); +INSERT INTO security_types(name) +VALUES ('currency'), ('bond'); + CREATE TABLE IF NOT EXISTS securities ( id INTEGER PRIMARY KEY AUTOINCREMENT, - security_type INTEGER NOT NULL, - name TEXT NOT NULL UNIQUE, - symbol TEXT NOT NULL UNIQUE, - - FOREIGN KEY (security_type) REFERENCES security_types (id) + security_type TEXT NOT NULL, + name TEXT NOT NULL UNIQUE, + symbol TEXT NOT NULL UNIQUE, + FOREIGN KEY (security_type) REFERENCES security_types (name) ); CREATE TABLE IF NOT EXISTS account @@ -33,4 +35,4 @@ CREATE TABLE IF NOT EXISTS account CREATE TABLE IF NOT EXISTS account_values ( id INTEGER PRIMARY KEY AUTOINCREMENT -); \ No newline at end of file +); diff --git a/src/main.rs b/src/main.rs index 8ceecbd..800086a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use log::{debug, info}; use rusqlite::Connection; use simple_logger::SimpleLogger; -use crate::migration::migrate; +use crate::migration::apply_migrations; mod migration; mod users; @@ -68,8 +68,8 @@ fn new_user_command_line_parser() -> Command { return Command::new("new") .long_flag("new") .about("Create new user.") - .arg(Arg::new("name").long("name").help("name of new user.").action(ArgAction::Set)) - .arg(Arg::new("public_key").long("public-key").help("public key of users.").action(ArgAction::Set)); + .arg(Arg::new("name").long("name").required(true).help("name of new user.").action(ArgAction::Set)) + .arg(Arg::new("public_key").required(true).long("public-key").help("public key of users.").action(ArgAction::Set)); } fn ls_user_command_line_parser() -> Command { @@ -308,14 +308,15 @@ fn set_logger(m: &ArgMatches) { SimpleLogger::new().with_level(get_verbosity(&m)).init().unwrap(); } + fn main() { let command = command_line_parser(); let m = command.get_matches(); set_logger(&m); let mut conn = get_connection(&m); - let migrations: Vec = Vec::new(); - migrate(&mut conn, & migrations).unwrap(); + + apply_migrations(&mut conn).unwrap(); let mut logic = ProCommandLineLogic::new(); process_command_line(&conn, &m, &mut logic); } \ No newline at end of file diff --git a/src/migration.rs b/src/migration.rs index bade694..b16ae09 100644 --- a/src/migration.rs +++ b/src/migration.rs @@ -1,6 +1,7 @@ use std::time::Instant; use rusqlite::{Connection, Transaction}; use sha2::{Digest, Sha256}; +use resource::{resource_str}; #[derive(Debug)] pub enum IntegrityError { @@ -52,13 +53,12 @@ pub fn migrate(conn: &mut Connection, migrations: &Vec) -> Result<(), Er fn apply_migration(tx: &Transaction, id: usize, migration: &String) -> Result<(), Error> { let hash = compute_migration_sha256(migration); - let q = "INSERT INTO shitty_migrations(id, sha256) VALUES (?1, ?2)"; tx.execute(q, (&id, &hash)).map_err(|e| Error::Sql(e))?; - // let migration_id = tx.last_insert_rowid(); + let now = Instant::now(); - tx.execute(migration, ()).map_err(|e| Error::Sql(e))?; + tx.execute_batch(migration).map_err(|e| Error::Sql(e))?; let elapsed_time = now.elapsed(); let q = "UPDATE shitty_migrations SET duration = ?1 WHERE id = ?2"; @@ -89,6 +89,18 @@ fn check_migration(tx: &Transaction, id: usize, migration: &String) -> Result<() return Ok(()); } +fn get_migrations() -> Vec { + let mut ret = Vec::new(); + ret.push(resource_str!("migrations/create_tables_001.sql").as_ref().to_string()); + return ret; +} + + +pub fn apply_migrations(conn: &mut Connection) -> Result<(), Error> { + let migrations = get_migrations(); + return migrate(conn, &migrations); +} + #[cfg(test)] mod test { use super::*; diff --git a/src/securities.rs b/src/securities.rs index 139597f..f113d2b 100644 --- a/src/securities.rs +++ b/src/securities.rs @@ -1,2 +1,88 @@ +use rusqlite::{Connection, ToSql}; +use serde::{Serialize}; +use crate::migration; +#[derive(Debug, Serialize)] +pub enum SecurityType { + Currency, + Bond, +} + +impl ToSql for SecurityType { + #[inline] + fn to_sql(&self) -> rusqlite::Result> { + Ok(rusqlite::types::ToSqlOutput::from(match self { + SecurityType::Currency => "currency", + SecurityType::Bond => "bond", + })) + } +} + +#[derive(Debug, Serialize)] +pub struct Security { + id: i32, + security_type: SecurityType, + public_key: String, + symbol: String, +} + +pub struct NewSecurity { + security_type: SecurityType, + public_key: String, + symbol: String, + name: String, +} + +pub fn create_security(conn: &mut Connection, sec: &NewSecurity) -> Result<(), rusqlite::Error> { + let tx = conn.transaction()?; + let q = "INSERT INTO + securities(name, symbol, security_type) + VALUES(?1, ?2, ?3)"; + let params = (&sec.name, &sec.symbol, &sec.security_type); + tx.execute(q, params)?; + return Ok(()); +} + + +#[cfg(test)] +mod test { + use super::*; + use rusqlite::{Connection, params}; + + + fn setup() -> Connection { + let mut conn = Connection::open_in_memory().unwrap(); + migration::apply_migrations(&mut conn).unwrap(); + return conn; + } + + #[test] + fn test_security_types() -> Result<(), rusqlite::Error> { + let conn = setup(); + let q = "CREATE TABLE test_security_types( + name TEXT, + FOREIGN KEY (name) REFERENCES security_types (name) + )"; + conn.execute(q, ())?; + let security_types = vec![SecurityType::Currency, SecurityType::Bond]; + for sec in security_types.iter() { + let q = "INSERT INTO test_security_types(name) VALUES (?1)"; + conn.execute(q, params![sec])?; + } + return Ok(()); + } + + #[test] + fn test_create_security() -> Result<(), rusqlite::Error> { + let mut conn = setup(); + let sec = NewSecurity { + security_type: SecurityType::Currency, + public_key: "123".to_string(), + symbol: "P".to_string(), + name: "pisatre".to_string(), + }; + create_security(&mut conn, &sec)?; + return Ok(()); + } +} \ No newline at end of file