fuck off, I call that a day.
This commit is contained in:
1 parent
04d4d1b7a9
commit
cb5805a829
12 files changed
+229
-86
No files matched your search
Generated
+51
@@ -164,6 +164,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
"simple_logger",
|
"simple_logger",
|
||||||
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -178,6 +179,15 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
|
||||||
|
dependencies = [
|
||||||
|
"instant",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.6"
|
version = "0.14.6"
|
||||||
@@ -247,6 +257,15 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@@ -360,6 +379,15 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
@@ -377,6 +405,15 @@ version = "0.6.28"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "remove_dir_all"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "resource"
|
name = "resource"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -481,6 +518,20 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"fastrand",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"remove_dir_all",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
|
|||||||
+2
-1
@@ -18,4 +18,5 @@ serde_json = "1.0"
|
|||||||
resource = "0.5.0"
|
resource = "0.5.0"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
regex = "1.7.0"
|
regex = "1.7.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
tempfile = "3.3.0"
|
||||||
@@ -65,15 +65,18 @@ CREATE TABLE IF NOT EXISTS cashed_cheques
|
|||||||
(
|
(
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
drawer_user_id INTEGER NOT NULL,
|
drawer_user_id INTEGER NOT NULL,
|
||||||
drawer_account_id INTEGER,
|
drawer_account_id INTEGER NOT NULL,
|
||||||
cheque_number INTEGER NOT NULL,
|
cheque_number INTEGER NOT NULL,
|
||||||
amount INTEGER NOT NULL,
|
amount INTEGER NOT NULL,
|
||||||
underlying_security INTEGER NOT NULL,
|
underlying_security INTEGER NOT NULL,
|
||||||
|
payee_user_id INTEGER NOT NULL,
|
||||||
|
payee_account_id INTEGER NOT NULL,
|
||||||
creation_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
creation_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
|
||||||
FOREIGN KEY (underlying_security) REFERENCES securities (id),
|
FOREIGN KEY (underlying_security) REFERENCES securities (id),
|
||||||
FOREIGN KEY (drawer_user_id) REFERENCES users (id),
|
FOREIGN KEY (drawer_user_id) REFERENCES users (id),
|
||||||
FOREIGN KEY (drawer_account_id) REFERENCES accounts (id),
|
FOREIGN KEY (drawer_account_id) REFERENCES accounts (id),
|
||||||
|
FOREIGN KEY (payee_user_id) REFERENCES users (id),
|
||||||
|
FOREIGN KEY (payee_account_id) REFERENCES accounts (id),
|
||||||
CONSTRAINT double_spending UNIQUE (drawer_user_id, cheque_number) -- no double spending
|
CONSTRAINT double_spending UNIQUE (drawer_user_id, cheque_number) -- no double spending
|
||||||
);
|
);
|
||||||
+1
-4
@@ -1,5 +1,4 @@
|
|||||||
use rusqlite::{Connection, OptionalExtension, params, Result, Row, Transaction};
|
use rusqlite::{Connection, OptionalExtension, params, Result, Row, Transaction};
|
||||||
use sha2::digest::typenum::op;
|
|
||||||
|
|
||||||
use crate::db::_external_id::{AccountId, Id, UserId};
|
use crate::db::_external_id::{AccountId, Id, UserId};
|
||||||
use crate::db::_securities::Security;
|
use crate::db::_securities::Security;
|
||||||
@@ -156,9 +155,7 @@ pub fn add_to_account(tx: &Connection,
|
|||||||
mod test_accounts {
|
mod test_accounts {
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
|
|
||||||
use crate::db::_securities;
|
|
||||||
use crate::db::_test_utils::setup_users_and_securities;
|
use crate::db::_test_utils::setup_users_and_securities;
|
||||||
use crate::db::_users;
|
|
||||||
use crate::migration;
|
use crate::migration;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -243,7 +240,7 @@ mod test_accounts {
|
|||||||
fn test_is_owner() -> Result<()> {
|
fn test_is_owner() -> Result<()> {
|
||||||
let (mut conn, users, secs) = setup();
|
let (mut conn, users, secs) = setup();
|
||||||
let tx = &conn.transaction()?;
|
let tx = &conn.transaction()?;
|
||||||
let accounts = create_all_accounts(tx, &users, &secs)?;
|
let _accounts = create_all_accounts(tx, &users, &secs)?;
|
||||||
let user_1 = users.get(0).unwrap();
|
let user_1 = users.get(0).unwrap();
|
||||||
let user_2 = users.get(1).unwrap();
|
let user_2 = users.get(1).unwrap();
|
||||||
let sec = secs.get(0).unwrap();
|
let sec = secs.get(0).unwrap();
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use rusqlite::{Result, Transaction};
|
|
||||||
|
|
||||||
use crate::db::_external_id::{AccountId, SecurityId, UserId};
|
use crate::db::_external_id::{AccountId, SecurityId, UserId};
|
||||||
|
|
||||||
pub type Drawer = (UserId, AccountId, SecurityId);
|
pub type Drawer = (UserId, AccountId, SecurityId);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use std::io::Read;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use regex::{Captures, Regex};
|
use regex::{Captures, Regex};
|
||||||
@@ -168,8 +167,6 @@ impl ExternalId {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test_external_ids {
|
pub mod test_external_ids {
|
||||||
use std::any::Any;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+11
-14
@@ -1,7 +1,7 @@
|
|||||||
use rusqlite::{Connection, OptionalExtension, params, Result, Row, ToSql, Transaction};
|
use rusqlite::{Connection, params, Result, Row, ToSql, Transaction};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::db::_external_id::{Id, SecurityId, UserId};
|
use crate::db::_external_id::{Id, SecurityId};
|
||||||
use crate::db::users::User;
|
use crate::db::users::User;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, PartialEq)]
|
#[derive(Debug, Serialize, PartialEq)]
|
||||||
@@ -62,8 +62,7 @@ pub struct NewSecurity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_security(tx: &Transaction,
|
pub fn create_security(tx: &Transaction,
|
||||||
sec: &NewSecurity,
|
sec: &NewSecurity) -> Result<Security> {
|
||||||
owner: &UserId) -> Result<Security> {
|
|
||||||
let q = "INSERT INTO
|
let q = "INSERT INTO
|
||||||
securities(name, symbol, security_type)
|
securities(name, symbol, security_type)
|
||||||
VALUES(?1, ?2, ?3)";
|
VALUES(?1, ?2, ?3)";
|
||||||
@@ -118,7 +117,7 @@ mod test_securities {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_security_types() -> Result<(), rusqlite::Error> {
|
fn test_security_types() -> Result<(), rusqlite::Error> {
|
||||||
let (mut conn, users) = setup();
|
let (conn, _users) = setup();
|
||||||
let q = "CREATE TABLE test_security_types(
|
let q = "CREATE TABLE test_security_types(
|
||||||
name TEXT,
|
name TEXT,
|
||||||
FOREIGN KEY (name) REFERENCES security_types (name)
|
FOREIGN KEY (name) REFERENCES security_types (name)
|
||||||
@@ -134,25 +133,23 @@ mod test_securities {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_security() -> Result<(), rusqlite::Error> {
|
fn test_create_security() -> Result<(), rusqlite::Error> {
|
||||||
let (mut conn, users) = setup();
|
let (mut conn, _users) = setup();
|
||||||
let owner = users.get(0).unwrap();
|
let new_sec = mock_currency_1();
|
||||||
let not_owner = users.get(1).unwrap();
|
|
||||||
let sec = mock_currency_1();
|
|
||||||
let tx = conn.transaction()?;
|
let tx = conn.transaction()?;
|
||||||
let sec = create_security(&tx, &sec, &owner.id())?;
|
let sec = create_security(&tx, &new_sec)?;
|
||||||
|
assert_eq!(sec.symbol, new_sec.symbol);
|
||||||
|
|
||||||
tx.commit()
|
tx.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list_security() -> Result<(), rusqlite::Error> {
|
fn test_list_security() -> Result<(), rusqlite::Error> {
|
||||||
let (mut conn, users) = setup();
|
let (mut conn, _users) = setup();
|
||||||
let user = users.get(0).unwrap();
|
|
||||||
assert_eq!(0, list_securities(&conn)?.len());
|
assert_eq!(0, list_securities(&conn)?.len());
|
||||||
|
|
||||||
let sec = mock_currency_1();
|
let sec = mock_currency_1();
|
||||||
let tx = conn.transaction()?;
|
let tx = conn.transaction()?;
|
||||||
create_security(&tx, &sec, &user.id())?;
|
create_security(&tx, &sec)?;
|
||||||
tx.commit()?;
|
tx.commit()?;
|
||||||
let ret = list_securities(&conn)?;
|
let ret = list_securities(&conn)?;
|
||||||
let expt = Security {
|
let expt = Security {
|
||||||
@@ -171,7 +168,7 @@ mod test_securities {
|
|||||||
symbol: "B".to_string(),
|
symbol: "B".to_string(),
|
||||||
};
|
};
|
||||||
let tx = conn.transaction()?;
|
let tx = conn.transaction()?;
|
||||||
create_security(&tx, &sec, &user.id())?;
|
create_security(&tx, &sec)?;
|
||||||
tx.commit()?;
|
tx.commit()?;
|
||||||
let ret = list_securities(&conn)?;
|
let ret = list_securities(&conn)?;
|
||||||
assert_eq!(2, ret.len());
|
assert_eq!(2, ret.len());
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ pub fn setup_users(tx: &Transaction) -> Result<Vec<User>> {
|
|||||||
return Ok(vec![user_1, user_2]);
|
return Ok(vec![user_1, user_2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_securities(tx: &Transaction, owner: &UserId) -> Result<Vec<Security>> {
|
pub fn setup_securities(tx: &Transaction, _owner: &UserId) -> Result<Vec<Security>> {
|
||||||
let sec_1 = _securities::create_security(tx, &mock_currency_1(), owner)?;
|
let sec_1 = _securities::create_security(tx, &mock_currency_1())?;
|
||||||
let sec_2 = _securities::create_security(tx, &mock_currency_2(), owner)?;
|
let sec_2 = _securities::create_security(tx, &mock_currency_2())?;
|
||||||
return Ok(vec![sec_1, sec_2]);
|
return Ok(vec![sec_1, sec_2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
use std::borrow::Borrow;
|
||||||
|
use std::fs;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use rusqlite::Connection;
|
||||||
|
|
||||||
|
use crate::util::{CustomResult, Error};
|
||||||
|
|
||||||
|
// struct
|
||||||
|
|
||||||
|
|
||||||
|
fn process_event_file(conn: &mut Connection, file: File) -> CustomResult<()> {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_event_files<I>(conn: &mut Connection, paths: I) -> CustomResult<()>
|
||||||
|
where I: Iterator,
|
||||||
|
I::Item: Borrow<PathBuf> {
|
||||||
|
for path in paths {
|
||||||
|
let file = File::open(path.borrow()).map_err(Error::IOError)?;
|
||||||
|
process_event_file(conn, file).unwrap();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
use tempfile::tempfile;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn test_basic_logic(file: File) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_basic() -> CustomResult<()> {
|
||||||
|
let file = tempfile().unwrap();
|
||||||
|
let mut conn = Connection::open_in_memory().unwrap();
|
||||||
|
let ret = process_event_file(&mut conn, file);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
+85
-13
@@ -1,19 +1,23 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::{Arg, ArgAction, ArgMatches, Command, value_parser};
|
use clap::{Arg, ArgAction, ArgMatches, Command, value_parser};
|
||||||
use log::{debug, info};
|
use clap::parser::ValuesRef;
|
||||||
|
use log::{debug, error, info};
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
|
|
||||||
use crate::migration::apply_migrations;
|
|
||||||
use db::securities::list_securities;
|
use db::securities::list_securities;
|
||||||
|
|
||||||
use crate::db::users::{create_user, list_users};
|
use crate::db::users::{create_user, list_users};
|
||||||
|
use crate::event_processor::process_event_files;
|
||||||
|
use crate::migration::apply_migrations;
|
||||||
|
|
||||||
mod migration;
|
mod migration;
|
||||||
mod db;
|
mod db;
|
||||||
mod services;
|
mod services;
|
||||||
mod util;
|
mod util;
|
||||||
|
mod event_processor;
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
struct Output<T> {
|
struct Output<T> {
|
||||||
@@ -33,6 +37,8 @@ trait CommandLineUserLogic {
|
|||||||
fn list_user(&mut self, conn: &Connection);
|
fn list_user(&mut self, conn: &Connection);
|
||||||
|
|
||||||
fn list_securities(&mut self, conn: &Connection);
|
fn list_securities(&mut self, conn: &Connection);
|
||||||
|
|
||||||
|
fn process_events(&mut self, conn: &mut Connection, files: ValuesRef<PathBuf>);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verbosity_arg() -> Arg {
|
fn verbosity_arg() -> Arg {
|
||||||
@@ -58,10 +64,10 @@ fn db_path_arg() -> Arg {
|
|||||||
.long("db-path")
|
.long("db-path")
|
||||||
.value_name("FILE")
|
.value_name("FILE")
|
||||||
.value_parser(value_parser!(PathBuf))
|
.value_parser(value_parser!(PathBuf))
|
||||||
.default_value( "ledger.sqlite");
|
.default_value("ledger.sqlite");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_db_path(m: & ArgMatches) -> & PathBuf{
|
fn get_db_path(m: &ArgMatches) -> &PathBuf {
|
||||||
return m.get_one::<PathBuf>("db-path").unwrap();
|
return m.get_one::<PathBuf>("db-path").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,10 +85,28 @@ fn ls_user_command_line_parser() -> Command {
|
|||||||
.about("List all users.");
|
.about("List all users.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn process_events_command_line_parser() -> Command {
|
||||||
|
Command::new("process")
|
||||||
|
.about("Process events one after the other. In the order received")
|
||||||
|
.arg_required_else_help(true)
|
||||||
|
.arg(clap::arg!(<PATH> ... "Stuff to add")
|
||||||
|
.value_parser(value_parser!(PathBuf)))
|
||||||
|
}
|
||||||
|
|
||||||
fn user_command_line_parser() -> Command {
|
fn user_command_line_parser() -> Command {
|
||||||
return Command::new("users")
|
return Command::new("users")
|
||||||
|
.about("Handy command about users.")
|
||||||
.subcommand(new_user_command_line_parser())
|
.subcommand(new_user_command_line_parser())
|
||||||
.subcommand(ls_user_command_line_parser());
|
.subcommand(ls_user_command_line_parser())
|
||||||
|
.arg_required_else_help(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_command_line_parser() -> Command {
|
||||||
|
return Command::new("events")
|
||||||
|
.about("Process and query events files")
|
||||||
|
.subcommand(process_events_command_line_parser())
|
||||||
|
.arg_required_else_help(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ls_securities_command_line_parser() -> Command {
|
fn ls_securities_command_line_parser() -> Command {
|
||||||
@@ -93,15 +117,18 @@ fn ls_securities_command_line_parser() -> Command {
|
|||||||
|
|
||||||
fn securities_command_line_parser() -> Command {
|
fn securities_command_line_parser() -> Command {
|
||||||
return Command::new("securities")
|
return Command::new("securities")
|
||||||
.subcommand(ls_securities_command_line_parser());
|
.subcommand(ls_securities_command_line_parser())
|
||||||
|
.arg_required_else_help(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn command_line_parser() -> Command {
|
fn command_line_parser() -> Command {
|
||||||
return Command::new("prog")
|
return Command::new("prog")
|
||||||
.arg(verbosity_arg())
|
.arg(verbosity_arg())
|
||||||
.arg(db_path_arg())
|
.arg(db_path_arg())
|
||||||
|
.subcommand(event_command_line_parser())
|
||||||
.subcommand(user_command_line_parser())
|
.subcommand(user_command_line_parser())
|
||||||
.subcommand(securities_command_line_parser());
|
.subcommand(securities_command_line_parser())
|
||||||
|
.arg_required_else_help(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_new_user_command_line(conn: &mut Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
|
fn process_new_user_command_line(conn: &mut Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
|
||||||
@@ -134,6 +161,17 @@ fn process_list_securities_command_line(conn: &Connection, m: &ArgMatches, logic
|
|||||||
logic.list_securities(conn);
|
logic.list_securities(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_process_events_command_line(conn: &mut Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
|
||||||
|
debug!("Processing process event command line {:?}.", m);
|
||||||
|
let mut files = m.get_many::<PathBuf>("PATH").unwrap();
|
||||||
|
if files.any(|x| !x.exists()) {
|
||||||
|
error!("Some passed files do not exists. Existing right now.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logic.process_events(conn, files);
|
||||||
|
}
|
||||||
|
|
||||||
fn process_securities_command_line(conn: &Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
|
fn process_securities_command_line(conn: &Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
|
||||||
debug!("Processing securities command line {:?}.", m);
|
debug!("Processing securities command line {:?}.", m);
|
||||||
match m.subcommand() {
|
match m.subcommand() {
|
||||||
@@ -144,6 +182,16 @@ fn process_securities_command_line(conn: &Connection, m: &ArgMatches, logic: &mu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_events_command_line(conn: &mut Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
|
||||||
|
debug!("Processing events command line {:?}.", m);
|
||||||
|
match m.subcommand() {
|
||||||
|
Some(("process", process_events_match)) => {
|
||||||
|
process_process_events_command_line(conn, process_events_match, logic);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn process_command_line(conn: &mut 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);
|
debug!("Processing command line {:?}.", m);
|
||||||
match m.subcommand() {
|
match m.subcommand() {
|
||||||
@@ -154,10 +202,13 @@ fn process_command_line(conn: &mut Connection, m: &ArgMatches, logic: &mut dyn C
|
|||||||
Some(("securities", securities_match)) => {
|
Some(("securities", securities_match)) => {
|
||||||
process_securities_command_line(conn, securities_match, logic);
|
process_securities_command_line(conn, securities_match, logic);
|
||||||
}
|
}
|
||||||
|
Some(("events", events_match)) => {
|
||||||
|
process_events_command_line(conn, events_match, logic);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!("Received unparsable command. {:?}", m.subcommand());
|
println!("Received un parsable command. {:?}", m.subcommand());
|
||||||
unreachable!();
|
unreachable!();
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,6 +220,7 @@ mod cli_test {
|
|||||||
list_user_count: usize,
|
list_user_count: usize,
|
||||||
create_user_count: usize,
|
create_user_count: usize,
|
||||||
list_securities_count: usize,
|
list_securities_count: usize,
|
||||||
|
process_events_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestCommandLineLogic {
|
impl TestCommandLineLogic {
|
||||||
@@ -177,6 +229,7 @@ mod cli_test {
|
|||||||
list_user_count: 0,
|
list_user_count: 0,
|
||||||
create_user_count: 0,
|
create_user_count: 0,
|
||||||
list_securities_count: 0,
|
list_securities_count: 0,
|
||||||
|
process_events_count: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,6 +246,10 @@ mod cli_test {
|
|||||||
fn list_securities(&mut self, _conn: &Connection) {
|
fn list_securities(&mut self, _conn: &Connection) {
|
||||||
self.list_securities_count += 1;
|
self.list_securities_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_events(&mut self, conn: &mut Connection, _files: ValuesRef<PathBuf>) {
|
||||||
|
self.process_events_count += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -257,6 +314,16 @@ mod cli_test {
|
|||||||
process_command_line(&mut conn, &m, &mut logic);
|
process_command_line(&mut conn, &m, &mut logic);
|
||||||
assert_eq!(logic.list_securities_count, 1);
|
assert_eq!(logic.list_securities_count, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn process_events() {
|
||||||
|
let cmd = command_line_parser();
|
||||||
|
let m = cmd.get_matches_from(vec!["test", "events", "process", "/dev/null"]);
|
||||||
|
let mut logic = TestCommandLineLogic::new();
|
||||||
|
let mut conn = Connection::open_in_memory().unwrap();
|
||||||
|
process_command_line(&mut conn, &m, &mut logic);
|
||||||
|
assert_eq!(logic.process_events_count, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProCommandLineLogic {}
|
struct ProCommandLineLogic {}
|
||||||
@@ -270,7 +337,7 @@ impl ProCommandLineLogic {
|
|||||||
impl CommandLineUserLogic for ProCommandLineLogic {
|
impl CommandLineUserLogic for ProCommandLineLogic {
|
||||||
fn create_user(&mut self, conn: &mut Connection, name: &String, public_key: &String) {
|
fn create_user(&mut self, conn: &mut Connection, name: &String, public_key: &String) {
|
||||||
let tx = conn.transaction().unwrap();
|
let tx = conn.transaction().unwrap();
|
||||||
create_user(& tx, name, public_key).unwrap();
|
create_user(&tx, name, public_key).unwrap();
|
||||||
tx.commit().unwrap();
|
tx.commit().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,20 +354,25 @@ impl CommandLineUserLogic for ProCommandLineLogic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_securities(&mut self, conn: & Connection) {
|
fn list_securities(&mut self, conn: &Connection) {
|
||||||
let securities = list_securities(conn);
|
let securities = list_securities(conn);
|
||||||
match securities {
|
match securities {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
let out = Output::success(s);
|
let out = Output::success(s);
|
||||||
println!("{}", serde_json::to_string(&out).unwrap());
|
println!("{}", serde_json::to_string(&out).unwrap());
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{:?}", e);
|
println!("{:?}", e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
fn process_events(&mut self, conn: &mut Connection, files: ValuesRef<PathBuf>) {
|
||||||
|
process_event_files(conn, files);
|
||||||
|
|
||||||
|
println!("fuck me");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_connection(m: &ArgMatches) -> Connection {
|
fn get_connection(m: &ArgMatches) -> Connection {
|
||||||
let path = get_db_path(&m);
|
let path = get_db_path(&m);
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
use rusqlite::{Connection, ffi, params, Params, Savepoint, Transaction};
|
use rusqlite::{Connection, ffi, params, Savepoint, Transaction};
|
||||||
|
|
||||||
use crate::db::{accounts, securities};
|
use crate::db::accounts;
|
||||||
use crate::db::accounts::{Account, get_account_by_id};
|
use crate::db::accounts::get_account_by_id;
|
||||||
use crate::db::cheques::Cheque;
|
use crate::db::cheques::Cheque;
|
||||||
use crate::db::external_id::AccountId;
|
use crate::db::external_id::AccountId;
|
||||||
use crate::db::securities::get_security_by_id;
|
|
||||||
use crate::util::*;
|
use crate::util::*;
|
||||||
|
|
||||||
fn _move_securities(tx: &Savepoint,
|
fn _move_securities(tx: &Savepoint,
|
||||||
cheque: &Cheque,
|
cheque: &Cheque,
|
||||||
account_id: &AccountId) -> CustomResult<()> {
|
account_id: &AccountId) -> CustomResult<()> {
|
||||||
let drawer_receipt = accounts::add_to_account(tx, &cheque.drawer_account_id, -cheque.amount)?;
|
let drawer_receipt = accounts::add_to_account(tx, &cheque.drawer_account_id, -cheque.amount)?;
|
||||||
let payee_receipt = accounts::add_to_account(tx, account_id, cheque.amount)?;
|
let _payee_receipt = accounts::add_to_account(tx, account_id, cheque.amount)?;
|
||||||
if drawer_receipt.amount < 0 {
|
if drawer_receipt.amount < 0 {
|
||||||
let m = format!("Drawer of {:?} does not have sufficient amount in account.", cheque);
|
let m = format!("Drawer of {:?} does not have sufficient amount in account.", cheque);
|
||||||
return Err(Error::InvalidAmount(m));
|
return Err(Error::InvalidAmount(m));
|
||||||
@@ -21,20 +20,27 @@ fn _move_securities(tx: &Savepoint,
|
|||||||
|
|
||||||
fn _add_cheque_to_cashed_cheques_log(tx: &Connection,
|
fn _add_cheque_to_cashed_cheques_log(tx: &Connection,
|
||||||
cheque: &Cheque,
|
cheque: &Cheque,
|
||||||
account_id: &AccountId) -> Result<(), Error> {
|
payee_account_id: &AccountId) -> Result<(), Error> {
|
||||||
let q = "
|
let q = "
|
||||||
INSERT INTO cashed_cheques(
|
INSERT INTO cashed_cheques(
|
||||||
drawer_user_id,
|
drawer_user_id,
|
||||||
|
drawer_account_id,
|
||||||
|
payee_user_id,
|
||||||
|
payee_account_id,
|
||||||
cheque_number,
|
cheque_number,
|
||||||
amount,
|
amount,
|
||||||
underlying_security
|
underlying_security
|
||||||
)
|
)
|
||||||
VALUES(?1, ?2, ?3, ?4)
|
VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7)
|
||||||
";
|
";
|
||||||
let ret = tx.execute(q, params![cheque.drawer_user_id,
|
let params = params![cheque.drawer_user_id,
|
||||||
|
cheque.drawer_account_id,
|
||||||
|
cheque.payee_user_id,
|
||||||
|
payee_account_id,
|
||||||
cheque.cheque_number,
|
cheque.cheque_number,
|
||||||
cheque.amount,
|
cheque.amount,
|
||||||
cheque.security_id]).map_err(Error::SqlError);
|
cheque.security_id];
|
||||||
|
let ret = tx.execute(q, params).map_err(Error::SqlError);
|
||||||
match ret {
|
match ret {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(Error::SqlError(rusqlite::Error::SqliteFailure(ffi::Error { code: _, extended_code: 2067 }, _))) =>
|
Err(Error::SqlError(rusqlite::Error::SqliteFailure(ffi::Error { code: _, extended_code: 2067 }, _))) =>
|
||||||
@@ -81,13 +87,12 @@ fn cash_cheque(tx: &mut Transaction,
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_caching_cheques {
|
mod test_caching_cheques {
|
||||||
use rusqlite::{Connection, params};
|
use rusqlite::Connection;
|
||||||
|
|
||||||
use crate::db::{accounts, test_utils};
|
|
||||||
use crate::db::accounts::{create_checking_account, create_security_emitter_account};
|
use crate::db::accounts::{create_checking_account, create_security_emitter_account};
|
||||||
use crate::db::cheques::{Drawer, Payee};
|
use crate::db::cheques::{Drawer, Payee};
|
||||||
use crate::db::external_id::{Id, SecurityId, UserId};
|
use crate::db::external_id::{Id, UserId};
|
||||||
use crate::db::users::User;
|
use crate::db::test_utils;
|
||||||
use crate::migration;
|
use crate::migration;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -138,7 +143,7 @@ mod test_caching_cheques {
|
|||||||
let drawer: Drawer = _create_drawer(payee, payee_account);
|
let drawer: Drawer = _create_drawer(payee, payee_account);
|
||||||
let new_payee: Payee = (security_emitter.0, security_emitter.2);
|
let new_payee: Payee = (security_emitter.0, security_emitter.2);
|
||||||
|
|
||||||
return (conn, drawer, new_payee, reg_account.id())
|
return (conn, drawer, new_payee, reg_account.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -267,7 +272,7 @@ mod test_caching_cheques {
|
|||||||
test_invalid_drawer(&mut conn, drawer, payee, payee_account)
|
test_invalid_drawer(&mut conn, drawer, payee, payee_account)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_invalid_payee(conn: &mut Connection, security_emitter: Drawer, payee: Payee, payee_account: AccountId) -> CustomResult<()> {
|
fn test_invalid_payee(conn: &mut Connection, security_emitter: Drawer, payee: Payee) -> CustomResult<()> {
|
||||||
let (mut conn, security_emitter, payee, payee_account) = setup_security_emitter();
|
let (mut conn, security_emitter, payee, payee_account) = setup_security_emitter();
|
||||||
let mut tx = conn.transaction().unwrap();
|
let mut tx = conn.transaction().unwrap();
|
||||||
let wrong_payee_account = &security_emitter.1;
|
let wrong_payee_account = &security_emitter.1;
|
||||||
@@ -286,12 +291,12 @@ mod test_caching_cheques {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_payee_security_emitter_cheque() -> CustomResult<()> {
|
fn test_invalid_payee_security_emitter_cheque() -> CustomResult<()> {
|
||||||
let (mut conn, security_emitter, payee, payee_account) = setup_security_emitter();
|
let (mut conn, security_emitter, payee, payee_account) = setup_security_emitter();
|
||||||
test_invalid_payee(&mut conn, security_emitter, payee, payee_account)
|
test_invalid_payee(&mut conn, security_emitter, payee)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_payee_regular_cheque() -> CustomResult<()> {
|
fn test_invalid_payee_regular_cheque() -> CustomResult<()> {
|
||||||
let (mut conn, drawer, payee, payee_account) = setup_basic();
|
let (mut conn, drawer, payee, payee_account) = setup_basic();
|
||||||
test_invalid_payee(&mut conn, drawer, payee, payee_account)
|
test_invalid_payee(&mut conn, drawer, payee)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-27
@@ -1,39 +1,14 @@
|
|||||||
use rusqlite::{Params, Row, Transaction};
|
use std::io;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
SqlError(rusqlite::Error),
|
SqlError(rusqlite::Error),
|
||||||
DoubleBooking(String),
|
|
||||||
InvalidDrawer(String),
|
InvalidDrawer(String),
|
||||||
InvalidAmount(String),
|
InvalidAmount(String),
|
||||||
InvalidPayee(String),
|
InvalidPayee(String),
|
||||||
DoubleSpendingError(String),
|
DoubleSpendingError(String),
|
||||||
|
IOError(io::Error),
|
||||||
Testing(String),
|
Testing(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type CustomResult<T, E = Error> = Result<T, E>;
|
pub type CustomResult<T, E = Error> = Result<T, E>;
|
||||||
|
|
||||||
pub struct TransactionHelper<'a> {
|
|
||||||
tx: &'a Transaction<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TransactionHelper<'_> {
|
|
||||||
pub fn new<'a>(tx: &'a Transaction) -> TransactionHelper<'a> {
|
|
||||||
TransactionHelper { tx }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute<P: Params>(&self, sql: &str, params: P) -> CustomResult<usize> {
|
|
||||||
self.tx.execute(sql, params).map_err(Error::SqlError)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn query_row<T, P, F>(&self, sql: &str, params: P, f: F) -> CustomResult<T>
|
|
||||||
where
|
|
||||||
P: Params,
|
|
||||||
F: FnOnce(&Row<'_>) -> rusqlite::Result<T>, {
|
|
||||||
self.tx.query_row(sql, params, f).map_err(Error::SqlError)
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// pub fn commit(&self) -> CustomResult<()> {
|
|
||||||
// self.tx.commit().map_err(Error::SqlError)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user