Going somewhere.
This commit is contained in:
1 parent
20cc644bfb
commit
7a212d6d28
5 files changed
+200
-78
No files matched your search
@@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS users
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
public_key TEXT NOT NULL,
|
||||
foo INTEGER
|
||||
creation_time DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS security_types
|
||||
@@ -24,13 +24,17 @@ CREATE TABLE IF NOT EXISTS securities
|
||||
FOREIGN KEY (security_type) REFERENCES security_types (name)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS account
|
||||
CREATE TABLE IF NOT EXISTS accounts
|
||||
(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
security INTEGER NOT NULL,
|
||||
owner INTEGER NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
timestamp DATETIME NOT NULL,
|
||||
creation_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (security) REFERENCES securities (id)
|
||||
FOREIGN KEY (security) REFERENCES securities (id),
|
||||
FOREIGN KEY (owner) REFERENCES users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS account_values
|
||||
|
||||
+99
-6
@@ -1,16 +1,109 @@
|
||||
use rusqlite::Connection;
|
||||
use rusqlite::{Connection, params, Result, Row, Transaction};
|
||||
|
||||
use crate::securities::Security;
|
||||
use crate::users::User;
|
||||
|
||||
struct Account<'a> {
|
||||
security: &'a Security,
|
||||
user: &'a User,
|
||||
pub struct Account {
|
||||
id: i64,
|
||||
amount: i64,
|
||||
time_stamp: i64
|
||||
timestamp: i64,
|
||||
}
|
||||
|
||||
fn _account_from_row(row: &Row) -> Result<Account> {
|
||||
return Ok(Account{
|
||||
id: row.get(0)?,
|
||||
amount: row.get(1)?,
|
||||
timestamp: row.get(2)?,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn list_accounts(conn: & Connection){
|
||||
pub fn list_accounts<'a>(conn: &Connection) -> Result<Vec<Account>> {
|
||||
let q = "SELECT id, amount, UNIXEPOCH(timestamp)
|
||||
FROM accounts
|
||||
ORDER BY id ASC";
|
||||
let mut stmt = conn.prepare(q)?;
|
||||
let ret: Vec<Account> = stmt
|
||||
.query_map([], &_account_from_row)?
|
||||
.filter(Result::is_ok)
|
||||
.map(Result::unwrap)
|
||||
.collect();
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
pub fn get_account_by_id(tx: &Transaction, id: u32) -> Result<Account>{
|
||||
let q = "SELECT id, amount, UNIXEPOCH(timestamp) FROM accounts WHERE id = ?1";
|
||||
return tx.query_row(q, params![id], _account_from_row);
|
||||
}
|
||||
|
||||
pub fn create_account(tx: &Transaction, sec: &Security, user: &User) -> Result<Account>{
|
||||
let q = "INSERT INTO accounts(security, owner, amount, timestamp)
|
||||
VALUES (?1, ?2, 0, CURRENT_TIMESTAMP)";
|
||||
tx.execute(q, (sec.id, user.id))?;
|
||||
return get_account_by_id(tx, tx.last_insert_rowid() as u32);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rusqlite::{Connection};
|
||||
use crate::users;
|
||||
use crate::securities;
|
||||
use crate::migration;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn setup_users(tx: &Transaction) -> Result<Vec<User>> {
|
||||
let user_1 = users::create_user(tx, &"bernard".to_string(), &"123".to_string())?;
|
||||
let user_2 = users::create_user(tx, &"sam".to_string(), &"abc".to_string())?;
|
||||
return Ok(vec![user_1, user_2]);
|
||||
}
|
||||
|
||||
fn setup_securities(tx: &Transaction) -> Result<Vec<Security>> {
|
||||
let sec_1 = securities::create_security(tx, &securities::test_util::mock_currency_1())?;
|
||||
let sec_2 = securities::create_security(tx, &securities::test_util::mock_currency_2())?;
|
||||
return Ok(vec![sec_1, sec_2]);
|
||||
}
|
||||
|
||||
fn setup() -> (Connection, Vec<User>, Vec<Security>) {
|
||||
let mut conn = Connection::open_in_memory().unwrap();
|
||||
migration::apply_migrations(&mut conn).unwrap();
|
||||
|
||||
let tx = conn.transaction().unwrap();
|
||||
let users = setup_users(&tx).unwrap();
|
||||
let securities = setup_securities(&tx).unwrap();
|
||||
|
||||
tx.commit().unwrap();
|
||||
return (conn, users, securities);
|
||||
}
|
||||
|
||||
fn create_all_accounts(tx: & Transaction, users: & Vec<User>, secs: & Vec<Security>) -> Result<()>{
|
||||
for sec in secs.iter() {
|
||||
for user in users.iter() {
|
||||
create_account(&tx, sec, user)?;
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_happy_path() -> Result<()>{
|
||||
let (mut conn, users, secs) = setup();
|
||||
let tx = conn.transaction()?;
|
||||
create_all_accounts(& tx, &users, &secs)?;
|
||||
tx.commit()?;
|
||||
let accs = list_accounts(& conn)?;
|
||||
assert_eq!(4, accs.len());
|
||||
assert_eq!(1, accs.get(0).unwrap().id);
|
||||
assert_eq!(4, accs.get(3).unwrap().id);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_happy_path_no_commit() -> Result<()>{
|
||||
let (mut conn, users, secs) = setup();
|
||||
create_all_accounts(& conn.transaction()?, &users, &secs)?;
|
||||
let accs = list_accounts(& conn)?;
|
||||
assert_eq!(0, accs.len());
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
+17
-21
@@ -27,7 +27,7 @@ impl<T> Output<T> {
|
||||
}
|
||||
|
||||
trait CommandLineUserLogic {
|
||||
fn create_user(&mut self, conn: &Connection, name: &String, public_key: &String);
|
||||
fn create_user(&mut self, conn: &mut Connection, name: &String, public_key: &String);
|
||||
|
||||
fn list_user(&mut self, conn: &Connection);
|
||||
|
||||
@@ -103,7 +103,7 @@ fn command_line_parser() -> Command {
|
||||
.subcommand(securities_command_line_parser());
|
||||
}
|
||||
|
||||
fn process_new_user_command_line(conn: &Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
|
||||
fn process_new_user_command_line(conn: &mut Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
|
||||
debug!("Processing new user command line {:?}.", m);
|
||||
let name = m.get_one::<String>("name").unwrap();
|
||||
let pk = m.get_one::<String>("public_key").unwrap();
|
||||
@@ -115,7 +115,7 @@ fn process_list_user_command_line(conn: &Connection, m: &ArgMatches, logic: &mut
|
||||
logic.list_user(conn);
|
||||
}
|
||||
|
||||
fn process_user_command_line(conn: &Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
|
||||
fn process_user_command_line(conn: &mut Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
|
||||
debug!("Processing user command line {:?}.", m);
|
||||
match m.subcommand() {
|
||||
Some(("new", new_user_match)) => {
|
||||
@@ -143,7 +143,7 @@ fn process_securities_command_line(conn: &Connection, m: &ArgMatches, logic: &mu
|
||||
}
|
||||
}
|
||||
|
||||
fn process_command_line(conn: &Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
|
||||
fn process_command_line(conn: &mut Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
|
||||
debug!("Processing command line {:?}.", m);
|
||||
match m.subcommand() {
|
||||
Some(("users", user_match)) => {
|
||||
@@ -181,7 +181,7 @@ mod test {
|
||||
}
|
||||
|
||||
impl CommandLineUserLogic for TestCommandLineLogic {
|
||||
fn create_user(&mut self, _conn: &Connection, _name: &String, _public_key: &String) {
|
||||
fn create_user(&mut self, _conn: &mut Connection, _name: &String, _public_key: &String) {
|
||||
self.create_user_count += 1;
|
||||
}
|
||||
|
||||
@@ -232,8 +232,8 @@ mod test {
|
||||
let cmd = command_line_parser();
|
||||
let m = cmd.get_matches_from(vec!["test", "users", "new", "--name", "paul", "--public-key", "123abc"]);
|
||||
let mut logic = TestCommandLineLogic::new();
|
||||
let conn = Connection::open_in_memory().unwrap();
|
||||
process_command_line(&conn, &m, &mut logic);
|
||||
let mut conn = Connection::open_in_memory().unwrap();
|
||||
process_command_line(&mut conn, &m, &mut logic);
|
||||
assert_eq!(logic.create_user_count, 1);
|
||||
}
|
||||
|
||||
@@ -242,8 +242,8 @@ mod test {
|
||||
let cmd = command_line_parser();
|
||||
let m = cmd.get_matches_from(vec!["test", "users", "ls"]);
|
||||
let mut logic = TestCommandLineLogic::new();
|
||||
let conn = Connection::open_in_memory().unwrap();
|
||||
process_command_line(&conn, &m, &mut logic);
|
||||
let mut conn = Connection::open_in_memory().unwrap();
|
||||
process_command_line(&mut conn, &m, &mut logic);
|
||||
assert_eq!(logic.list_user_count, 1);
|
||||
}
|
||||
|
||||
@@ -252,8 +252,8 @@ mod test {
|
||||
let cmd = command_line_parser();
|
||||
let m = cmd.get_matches_from(vec!["test", "securities", "ls"]);
|
||||
let mut logic = TestCommandLineLogic::new();
|
||||
let conn = Connection::open_in_memory().unwrap();
|
||||
process_command_line(&conn, &m, &mut logic);
|
||||
let mut conn = Connection::open_in_memory().unwrap();
|
||||
process_command_line(&mut conn, &m, &mut logic);
|
||||
assert_eq!(logic.list_securities_count, 1);
|
||||
}
|
||||
}
|
||||
@@ -267,21 +267,17 @@ impl ProCommandLineLogic {
|
||||
}
|
||||
|
||||
impl CommandLineUserLogic for ProCommandLineLogic {
|
||||
fn create_user(&mut self, conn: &Connection, name: &String, public_key: &String) {
|
||||
users::create_user(conn, name, public_key);
|
||||
fn create_user(&mut self, conn: &mut Connection, name: &String, public_key: &String) {
|
||||
let tx = conn.transaction().unwrap();
|
||||
users::create_user(& tx, name, public_key);
|
||||
tx.commit().unwrap();
|
||||
}
|
||||
|
||||
fn list_user(&mut self, conn: &Connection) {
|
||||
let users = users::list_users(conn);
|
||||
match users {
|
||||
Ok(u) => {
|
||||
let r: Vec<& users::User> = u.iter()
|
||||
.filter(|& x| x.is_ok())
|
||||
.map(| x| x.as_ref().unwrap())
|
||||
.collect();
|
||||
|
||||
let out = Output::success(r);
|
||||
|
||||
let out = Output::success(u);
|
||||
println!("{}", serde_json::to_string(&out).unwrap());
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -325,5 +321,5 @@ fn main() {
|
||||
|
||||
apply_migrations(&mut conn).unwrap();
|
||||
let mut logic = ProCommandLineLogic::new();
|
||||
process_command_line(&conn, &m, &mut logic);
|
||||
process_command_line(&mut conn, &m, &mut logic);
|
||||
}
|
||||
+54
-38
@@ -1,7 +1,6 @@
|
||||
use rusqlite::{Connection, Result, ToSql};
|
||||
use rusqlite::{Connection, params, Result, Row, ToSql, Transaction};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::migration;
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
pub enum SecurityType {
|
||||
@@ -33,50 +32,80 @@ impl ToSql for SecurityType {
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
pub struct Security {
|
||||
id: i32,
|
||||
pub id: i32,
|
||||
security_type: SecurityType,
|
||||
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<()> {
|
||||
let tx = conn.transaction()?;
|
||||
pub fn create_security(tx: &Transaction, sec: &NewSecurity) -> Result<Security> {
|
||||
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 tx.commit();
|
||||
return get_security_by_id(tx, tx.last_insert_rowid() as i32);
|
||||
}
|
||||
|
||||
pub fn list_securities(conn: & Connection) -> Result<Vec<Security>> {
|
||||
let q = "SELECT id, security_type, symbol
|
||||
FROM securities
|
||||
ORDER BY creation_time DESC";
|
||||
let mut stmt = conn.prepare(q)?;
|
||||
|
||||
let ret: Vec<Security> = stmt.query_map([], |row| {
|
||||
fn _security_from_row(row: &Row) -> Result<Security> {
|
||||
Ok(Security {
|
||||
id: row.get(0)?,
|
||||
security_type: SecurityType::from_string(row.get(1)?),
|
||||
symbol: row.get(2)?,
|
||||
})
|
||||
}).unwrap().filter(|x| x.is_ok()).map(|x| x.unwrap()).collect();
|
||||
}
|
||||
|
||||
pub fn get_security_by_id(tx: &Transaction, id: i32) -> Result<Security> {
|
||||
let q = "SELECT id, security_type, symbol
|
||||
FROM securities
|
||||
WHERE id = ?1";
|
||||
tx.query_row(q, params![id], _security_from_row)
|
||||
}
|
||||
|
||||
pub fn list_securities(conn: &Connection) -> Result<Vec<Security>> {
|
||||
let q = "SELECT id, security_type, symbol
|
||||
FROM securities
|
||||
ORDER BY id ASC";
|
||||
let mut stmt = conn.prepare(q)?;
|
||||
|
||||
let ret: Vec<Security> = stmt
|
||||
.query_map([], &_security_from_row)?
|
||||
.filter(Result::is_ok)
|
||||
.map(Result::unwrap)
|
||||
.collect();
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
pub mod test_util {
|
||||
use super::*;
|
||||
pub fn mock_currency_1() -> NewSecurity {
|
||||
return NewSecurity {
|
||||
security_type: SecurityType::Currency,
|
||||
symbol: "P".to_string(),
|
||||
name: "piastre".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn mock_currency_2() -> NewSecurity {
|
||||
return NewSecurity {
|
||||
security_type: SecurityType::Bond,
|
||||
symbol: "B".to_string(),
|
||||
name: "bong".to_string(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rusqlite::{Connection, params};
|
||||
|
||||
use super::*;
|
||||
use super::test_util::*;
|
||||
use crate::migration;
|
||||
|
||||
fn setup() -> Connection {
|
||||
let mut conn = Connection::open_in_memory().unwrap();
|
||||
@@ -84,24 +113,6 @@ mod test {
|
||||
return conn;
|
||||
}
|
||||
|
||||
fn mock_currency_1() -> NewSecurity {
|
||||
return NewSecurity {
|
||||
security_type: SecurityType::Currency,
|
||||
public_key: "123".to_string(),
|
||||
symbol: "P".to_string(),
|
||||
name: "pisatre".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
fn mock_currency_2() -> NewSecurity {
|
||||
return NewSecurity {
|
||||
security_type: SecurityType::Bond,
|
||||
public_key: "123".to_string(),
|
||||
symbol: "B".to_string(),
|
||||
name: "bong".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_security_types() -> Result<(), rusqlite::Error> {
|
||||
let conn = setup();
|
||||
@@ -122,8 +133,9 @@ mod test {
|
||||
fn test_create_security() -> Result<(), rusqlite::Error> {
|
||||
let mut conn = setup();
|
||||
let sec = mock_currency_1();
|
||||
create_security(&mut conn, &sec)?;
|
||||
return Ok(());
|
||||
let tx = conn.transaction()?;
|
||||
create_security(&tx, &sec)?;
|
||||
return tx.commit();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -132,7 +144,9 @@ mod test {
|
||||
assert_eq!(0, list_securities(&conn)?.len());
|
||||
|
||||
let sec = mock_currency_1();
|
||||
create_security(&mut conn, &sec)?;
|
||||
let tx = conn.transaction()?;
|
||||
create_security(&tx, &sec)?;
|
||||
tx.commit()?;
|
||||
let ret = list_securities(&conn)?;
|
||||
let expt = Security {
|
||||
id: 1,
|
||||
@@ -149,7 +163,9 @@ mod test {
|
||||
security_type: SecurityType::Bond,
|
||||
symbol: "B".to_string(),
|
||||
};
|
||||
create_security(&mut conn, &sec)?;
|
||||
let tx = conn.transaction()?;
|
||||
create_security(&tx, &sec)?;
|
||||
tx.commit()?;
|
||||
let ret = list_securities(&conn)?;
|
||||
assert_eq!(2, ret.len());
|
||||
assert_eq!(&expt, ret.get(1).unwrap());
|
||||
|
||||
+23
-10
@@ -1,28 +1,41 @@
|
||||
use rusqlite::{Connection, Result};
|
||||
use rusqlite::{Connection, params, Result, Transaction, Row};
|
||||
use serde::{Serialize};
|
||||
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct User {
|
||||
id: i32,
|
||||
pub id: i32,
|
||||
name: String,
|
||||
public_key: String,
|
||||
}
|
||||
|
||||
pub fn create_user(conn: & Connection, name: &String, public_key: &String){
|
||||
conn.execute("INSERT INTO users (name, public_key) VALUES (?1, ?2)",
|
||||
(name, public_key)).unwrap();
|
||||
pub fn create_user(tx: & Transaction, name: &String, public_key: &String) -> Result<User>{
|
||||
tx.execute("INSERT INTO users (name, public_key) VALUES (?1, ?2)",
|
||||
(name, public_key))?;
|
||||
return get_user_by_id(tx, tx.last_insert_rowid() as i32);
|
||||
}
|
||||
|
||||
pub fn list_users(conn: & Connection) -> Result<Vec<Result<User>>>{
|
||||
|
||||
let mut stmt = conn.prepare("SELECT id, name, public_key FROM users")?;
|
||||
let ret: Vec<Result<User>> = stmt.query_map([], |row| {
|
||||
fn _user_from_row(row: &Row) -> Result<User>{
|
||||
Ok(User {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
public_key: row.get(2)?,
|
||||
})
|
||||
}).unwrap().collect();
|
||||
}
|
||||
|
||||
fn get_user_by_id(tx: & Transaction, id: i32) -> Result<User>{
|
||||
let q = "SELECT id, name, public_key
|
||||
FROM users
|
||||
WHERE id = ?1";
|
||||
tx.query_row(q, params![id], _user_from_row)
|
||||
}
|
||||
|
||||
pub fn list_users(conn: & Connection) -> Result<Vec<User>>{
|
||||
|
||||
let mut stmt = conn.prepare("SELECT id, name, public_key FROM users")?;
|
||||
let ret: Vec<User> = stmt.query_map([], _user_from_row)?
|
||||
.filter(Result::is_ok)
|
||||
.map(Result::unwrap)
|
||||
.collect();
|
||||
return Ok(ret);
|
||||
}
|
||||
Reference in new issue
Block a user