Linted code.

This commit is contained in:
didier amyot committed 2023-01-30 08:10:00 -05:00
1 parent 894f969d0f
commit 174d7e855a
16 files changed
+296 -198

No files matched your search

+1
View File
@@ -45,6 +45,7 @@
RUST_SRC_PATH = rustPlatform.rustLibSrc;
shellHook = ''
export PROJECT_ROOT=${PROJECT_ROOT};
export PATH=$PROJECT_ROOT/tools:$PATH;
'';
};
});
+48 -19
View File
@@ -1,5 +1,5 @@
use rusqlite::{Connection, OptionalExtension, params, Result, Row, Transaction};
use fraction::{Fraction};
use fraction::Fraction;
use rusqlite::{params, Connection, OptionalExtension, Result, Row, Transaction};
use crate::db::_external_id::{AccountId, Id, UserId};
use crate::db::_securities::Security;
@@ -100,7 +100,6 @@ impl AccountHistory {
}
}
pub fn list_accounts<'a>(conn: &Connection) -> CustomResult<Vec<Account>> {
let q = "SELECT acc.id AS acc_id,
acc.amount AS acc_amount,
@@ -135,17 +134,25 @@ pub fn get_account_by_id(tx: &Connection, id: &AccountId) -> CustomResult<Accoun
FROM accounts acc
JOIN securities sec ON acc.security = sec.id
WHERE acc.id = ?1";
return tx.query_row(q, params![id], Account::from_row).map_err(Error::SqlError);
return tx
.query_row(q, params![id], Account::from_row)
.map_err(Error::SqlError);
}
fn create_account(tx: &Connection, sec: &SecurityId, user: &UserId, account_type: AccountType) -> CustomResult<Account> {
fn create_account(
tx: &Connection,
sec: &SecurityId,
user: &UserId,
account_type: AccountType,
) -> CustomResult<Account> {
let q = "INSERT INTO accounts(security, version, amount, timestamp, account_type)
VALUES (?1, 0, 0, CURRENT_TIMESTAMP, ?2)";
let account_type = match account_type {
AccountType::Checking => "checking",
AccountType::SecurityEmitter => "security_emitter",
};
tx.execute(q, (sec, account_type)).map_err(Error::SqlError)?;
tx.execute(q, (sec, account_type))
.map_err(Error::SqlError)?;
let id = AccountId::from_id(tx.last_insert_rowid() as i64);
let q = "INSERT INTO account_user_permissions(user_id, account_id)
@@ -165,7 +172,8 @@ fn get_account_history(tx: &Connection, id: &AccountId) -> CustomResult<AccountH
WHERE account_id = ?1
ORDER BY account_version DESC";
let mut stmt = tx.prepare(q).map_err(Error::SqlError)?;
let snapshots: Vec<AccountSnapshot> = stmt.query_map(params![id], AccountSnapshot::from_row)
let snapshots: Vec<AccountSnapshot> = stmt
.query_map(params![id], AccountSnapshot::from_row)
.map_err(Error::SqlError)?
.filter(Result::is_ok)
.map(Result::unwrap)
@@ -174,11 +182,19 @@ fn get_account_history(tx: &Connection, id: &AccountId) -> CustomResult<AccountH
return AccountHistory::new(get_account_by_id(tx, id)?, snapshots);
}
pub fn create_checking_account(tx: &Connection, sec: &SecurityId, user: &UserId) -> CustomResult<Account> {
pub fn create_checking_account(
tx: &Connection,
sec: &SecurityId,
user: &UserId,
) -> CustomResult<Account> {
create_account(tx, sec, user, AccountType::Checking)
}
pub fn create_security_emitter_account(tx: &Connection, sec: &SecurityId, user: &UserId) -> CustomResult<Account> {
pub fn create_security_emitter_account(
tx: &Connection,
sec: &SecurityId,
user: &UserId,
) -> CustomResult<Account> {
create_account(tx, sec, user, AccountType::SecurityEmitter)
}
@@ -186,7 +202,10 @@ pub fn is_owner(tx: &Connection, account_id: &AccountId, user_id: &UserId) -> Cu
let q = "SELECT id FROM account_user_permissions WHERE account_id = ?1 AND user_id = ?2";
let params = params![account_id, user_id];
let ite = |row: &Row| row.get::<usize, i64>(0);
let option = tx.query_row(q, params, ite).optional().map_err(Error::SqlError)?;
let option = tx
.query_row(q, params, ite)
.optional()
.map_err(Error::SqlError)?;
Ok(option.is_some())
}
@@ -218,13 +237,16 @@ fn update_account_history(tx: &Connection, account_id: &AccountId) -> CustomResu
FROM accounts
WHERE id = ?1
";
tx.execute(q, params![account_id]).map_err(Error::SqlError)?;
tx.execute(q, params![account_id])
.map_err(Error::SqlError)?;
Ok(())
}
pub fn add_to_account(tx: &Connection,
account_id: &AccountId,
amount: i64) -> CustomResult<AccountChangeReceipt> {
pub fn add_to_account(
tx: &Connection,
account_id: &AccountId,
amount: i64,
) -> CustomResult<AccountChangeReceipt> {
let q = "
UPDATE accounts SET amount = CASE account_type
WHEN 'security_emitter'
@@ -236,7 +258,9 @@ pub fn add_to_account(tx: &Connection,
RETURNING id, version, amount, UNIXEPOCH(CURRENT_TIMESTAMP) as timestamp
";
let params = params![amount, account_id];
let ret = tx.query_row(q, params, AccountChangeReceipt::from_row).map_err(Error::SqlError);
let ret = tx
.query_row(q, params, AccountChangeReceipt::from_row)
.map_err(Error::SqlError);
update_account_history(tx, account_id)?;
return ret;
}
@@ -261,7 +285,11 @@ mod test_accounts {
return (conn, users, securities);
}
fn create_all_accounts(tx: &Transaction, users: &Vec<User>, secs: &Vec<Security>) -> CustomResult<Vec<Account>> {
fn create_all_accounts(
tx: &Transaction,
users: &Vec<User>,
secs: &Vec<Security>,
) -> CustomResult<Vec<Account>> {
let mut ret: Vec<Account> = Vec::new();
for sec in secs.iter() {
for user in users.iter() {
@@ -361,8 +389,10 @@ mod test_accounts {
let acc = get_account_by_id(&tx2, &acc.id()).unwrap();
assert_eq!(acc.timestamp, receipt.timestamp);
assert_eq!(acc.amount, 33);
assert_eq!(get_account_history(&tx2, &acc.id())?.average_amount(),
Fraction::from(0 + 33) / Fraction::from(2));
assert_eq!(
get_account_history(&tx2, &acc.id())?.average_amount(),
Fraction::from(0 + 33) / Fraction::from(2)
);
Ok(())
}
@@ -390,4 +420,3 @@ mod test_accounts {
Ok(())
}
}
+1 -4
View File
@@ -14,10 +14,7 @@ pub struct Cheque {
}
impl Cheque {
pub fn new(drawer: Drawer,
payee: Payee,
amount: i64,
cheque_number: i64) -> Cheque {
pub fn new(drawer: Drawer, payee: Payee, amount: i64, cheque_number: i64) -> Cheque {
Cheque {
drawer_user_id: drawer.0,
drawer_account_id: drawer.1,
+53 -28
View File
@@ -8,7 +8,6 @@ struct SafeId {
signature: [u8; 8],
}
pub trait Id {
type Output;
fn from_regex_captures(caps: &Captures) -> Option<Self::Output>;
@@ -19,7 +18,12 @@ pub trait Id {
impl fmt::Display for SafeId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}-{}", hex::encode(self.encrypted_id), hex::encode(self.signature))
write!(
f,
"{}-{}",
hex::encode(self.encrypted_id),
hex::encode(self.signature)
)
}
}
@@ -55,11 +59,8 @@ impl Id for SafeId {
}
}
macro_rules! impl_ids {
($T:ident, $prefix:literal) => {
impl fmt::Display for $T {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}-{}", $prefix, self.0)
@@ -78,16 +79,17 @@ macro_rules! impl_ids {
fn from_regex_captures(caps: &Captures) -> Option<$T> {
let type_ = caps.name("type")?;
if(type_.as_str() != $prefix){
if (type_.as_str() != $prefix) {
return None;
}
SafeId::from_regex_captures(caps).map(|x| $T(x))
}
fn from_string(s: &String) -> Option<$T> {
let external_id_pattern = Regex::new(r"^(?P<type>\w)-(?P<id>\w+)-(?P<sign>\w+)$").unwrap();
let external_id_pattern =
Regex::new(r"^(?P<type>\w)-(?P<id>\w+)-(?P<sign>\w+)$").unwrap();
let caps = external_id_pattern.captures(&s)?;
Self::from_regex_captures(& caps)
Self::from_regex_captures(&caps)
}
fn from_id(id: i64) -> $T {
@@ -98,7 +100,7 @@ macro_rules! impl_ids {
self.0.get_id()
}
}
}
};
}
#[derive(PartialEq, Debug, Clone)]
@@ -121,17 +123,15 @@ pub enum ExternalId {
SecurityId(SecurityId),
}
fn deserialize_str(s: &str) -> Option<[u8; 8]> {
let mut encrypted_id_bytes = [0u8; 8];
let r = hex::decode_to_slice(s, &mut encrypted_id_bytes as &mut [u8]);
return match r {
Ok(_) => Some(encrypted_id_bytes),
Err(_) => None
Err(_) => None,
};
}
impl ToString for ExternalId {
fn to_string(&self) -> String {
match self {
@@ -142,17 +142,20 @@ impl ToString for ExternalId {
}
}
impl ExternalId {
pub fn from_string(s: &String) -> Option<ExternalId> {
let pattern = Regex::new(r"^(?P<type>\w)-(?P<id>\w+)-(?P<sign>\w+)$").unwrap();
let caps = pattern.captures(&s)?;
match caps.name("type")?.as_str() {
"a" => Some(ExternalId::AccountId(AccountId::from_regex_captures(&caps)?)),
"a" => Some(ExternalId::AccountId(AccountId::from_regex_captures(
&caps,
)?)),
"u" => Some(ExternalId::UserId(UserId::from_regex_captures(&caps)?)),
"s" => Some(ExternalId::SecurityId(SecurityId::from_regex_captures(&caps)?)),
_ => None
"s" => Some(ExternalId::SecurityId(SecurityId::from_regex_captures(
&caps,
)?)),
_ => None,
}
}
@@ -172,16 +175,31 @@ pub mod test_external_ids {
#[test]
fn test_id_macro() {
let id = SecurityId::from_id(33);
assert_eq!(id.to_string(), "s-0000000000000021-0000000000000021".to_string());
assert!(SecurityId::from_string(&"s-0000000000000021-0000000000000021".to_string()).is_some());
assert!(SecurityId::from_string(&"u-0000000000000021-0000000000000021".to_string()).is_none());
assert!(SecurityId::from_string(&"0000000000000021-0000000000000021".to_string()).is_none());
assert_eq!(
id.to_string(),
"s-0000000000000021-0000000000000021".to_string()
);
assert!(
SecurityId::from_string(&"s-0000000000000021-0000000000000021".to_string()).is_some()
);
assert!(
SecurityId::from_string(&"u-0000000000000021-0000000000000021".to_string()).is_none()
);
assert!(
SecurityId::from_string(&"0000000000000021-0000000000000021".to_string()).is_none()
);
}
#[test]
fn safe_id_happy_path_1() {
let sid = SafeId { encrypted_id: [1, 2, 3, 4, 5, 6, 7, 8], signature: [8, 7, 6, 5, 4, 3, 2, 1] };
assert_eq!("0102030405060708-0807060504030201".to_string(), sid.to_string());
let sid = SafeId {
encrypted_id: [1, 2, 3, 4, 5, 6, 7, 8],
signature: [8, 7, 6, 5, 4, 3, 2, 1],
};
assert_eq!(
"0102030405060708-0807060504030201".to_string(),
sid.to_string()
);
let deserialized_eid = SafeId::from_string(&sid.to_string()).unwrap();
assert_eq!(sid, deserialized_eid);
}
@@ -189,7 +207,10 @@ pub mod test_external_ids {
#[test]
fn safe_id_happy_path_2() {
let sid = SafeId::from_id(56);
assert_eq!("0000000000000038-0000000000000038".to_string(), sid.to_string());
assert_eq!(
"0000000000000038-0000000000000038".to_string(),
sid.to_string()
);
let deserialized_eid = SafeId::from_string(&sid.to_string()).unwrap();
assert_eq!(sid, deserialized_eid);
assert_eq!(56, deserialized_eid.get_id());
@@ -200,31 +221,35 @@ pub mod test_external_ids {
let bad_strings = vec!["a", "a-b", "01020304-0708090É"];
for bad_string in bad_strings.iter() {
let ret = SafeId::from_string(&bad_string.to_string());
assert!(ret.is_none(), "This should not be a valid safe id: {}", bad_string);
assert!(
ret.is_none(),
"This should not be a valid safe id: {}",
bad_string
);
}
}
#[test]
fn external_id_simple_test() {
let base = "0000000000000038-0000000000000038";
let eid = ExternalId::from_string(& format!("u-{:}", base)).unwrap();
let eid = ExternalId::from_string(&format!("u-{:}", base)).unwrap();
assert!(match eid {
ExternalId::UserId(_) => true,
_ => false
_ => false,
});
assert_eq!(eid.to_string(), format!("u-{:}", base));
let eid = ExternalId::from_string(&format!("a-{:}", base)).unwrap();
assert!(match eid {
ExternalId::AccountId(_) => true,
_ => false
_ => false,
});
assert_eq!(eid.to_string(), format!("a-{:}", base));
let eid = ExternalId::from_string(&format!("s-{:}", base)).unwrap();
assert!(match eid {
ExternalId::SecurityId(_) => true,
_ => false
_ => false,
});
assert_eq!(eid.to_string(), format!("s-{:}", base));
}
-1
View File
@@ -1,2 +1 @@
+3 -7
View File
@@ -1,4 +1,4 @@
use rusqlite::{Connection, params, Result, Row, ToSql, Transaction};
use rusqlite::{params, Connection, Result, Row, ToSql, Transaction};
use serde::Serialize;
use crate::db::_external_id::{Id, SecurityId};
@@ -61,8 +61,7 @@ pub struct NewSecurity {
pub name: String,
}
pub fn create_security(tx: &Transaction,
sec: &NewSecurity) -> Result<Security> {
pub fn create_security(tx: &Transaction, sec: &NewSecurity) -> Result<Security> {
let q = "INSERT INTO
securities(name, symbol, security_type)
VALUES(?1, ?2, ?3)";
@@ -73,7 +72,6 @@ pub fn create_security(tx: &Transaction,
return get_security_by_id(tx, new_sec_id);
}
pub fn get_security_by_id(tx: &Transaction, id: i32) -> Result<Security> {
let q = "SELECT id AS sec_id, security_type AS sec_security_type, symbol AS sec_symbol
FROM securities
@@ -95,10 +93,9 @@ pub fn list_securities(conn: &Connection) -> Result<Vec<Security>> {
return Ok(ret);
}
#[cfg(test)]
mod test_securities {
use rusqlite::{Connection, params};
use rusqlite::{params, Connection};
use crate::db::_test_utils::*;
use crate::migration;
@@ -160,7 +157,6 @@ mod test_securities {
assert_eq!(1, ret.len());
assert_eq!(&expt, ret.get(0).unwrap());
let sec = mock_currency_2();
let expt = Security {
id: 2,
+14 -8
View File
@@ -1,11 +1,11 @@
use rusqlite::{Result, Transaction};
use crate::db::{_securities, _users};
use crate::db::_accounts::create_checking_account;
use crate::db::_external_id::UserId;
use crate::db::_securities::{NewSecurity, Security, SecurityType};
use crate::db::_users::User;
use crate::db::accounts::Account;
use crate::db::{_securities, _users};
use crate::util::CustomResult;
pub fn mock_currency_1() -> NewSecurity {
@@ -42,14 +42,20 @@ pub fn setup_users_and_securities(tx: &Transaction) -> Result<(Vec<User>, Vec<Se
Ok((users, securities))
}
pub fn setup_basic_environment(tx: &Transaction) -> CustomResult<(Vec<User>, Vec<Security>, Vec<Account>)> {
pub fn setup_basic_environment(
tx: &Transaction,
) -> CustomResult<(Vec<User>, Vec<Security>, Vec<Account>)> {
let (users, securities) = setup_users_and_securities(tx).unwrap();
let acc1 = create_checking_account(tx,
&securities.get(0).unwrap().id(),
&users.get(0).unwrap().id())?;
let acc2 = create_checking_account(tx,
&securities.get(1).unwrap().id(),
&users.get(1).unwrap().id())?;
let acc1 = create_checking_account(
tx,
&securities.get(0).unwrap().id(),
&users.get(0).unwrap().id(),
)?;
let acc2 = create_checking_account(
tx,
&securities.get(1).unwrap().id(),
&users.get(1).unwrap().id(),
)?;
Ok((users, securities, vec![acc1, acc2]))
}
+9 -7
View File
@@ -1,7 +1,6 @@
use rusqlite::{Connection, params, Result, Transaction, Row};
use serde::{Serialize};
use crate::db::_external_id::{UserId, Id};
use crate::db::_external_id::{Id, UserId};
use rusqlite::{params, Connection, Result, Row, Transaction};
use serde::Serialize;
#[derive(Debug, Serialize)]
pub struct User {
@@ -17,8 +16,10 @@ impl User {
}
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))?;
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);
}
@@ -39,7 +40,8 @@ fn get_user_by_id(tx: &Transaction, id: i32) -> Result<User> {
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)?
let ret: Vec<User> = stmt
.query_map([], _user_from_row)?
.filter(Result::is_ok)
.map(Result::unwrap)
.collect();
+8 -14
View File
@@ -1,10 +1,10 @@
mod _users;
mod _securities;
mod _accounts;
mod _cheques;
mod _external_id;
mod _test_utils;
mod _interests;
mod _securities;
mod _test_utils;
mod _users;
pub mod external_id {
pub use super::_external_id::*;
@@ -19,14 +19,10 @@ pub mod users {
}
pub mod accounts {
pub use super::_accounts::{Account,
AccountChangeReceipt,
create_checking_account,
create_security_emitter_account,
is_owner,
get_account_by_id,
list_accounts,
add_to_account};
pub use super::_accounts::{
add_to_account, create_checking_account, create_security_emitter_account,
get_account_by_id, is_owner, list_accounts, Account, AccountChangeReceipt,
};
}
pub mod cheques {
@@ -34,7 +30,5 @@ pub mod cheques {
}
pub mod test_utils {
pub use super::_test_utils::{mock_currency_1,
mock_currency_2,
setup_basic_environment};
pub use super::_test_utils::{mock_currency_1, mock_currency_2, setup_basic_environment};
}
+1 -3
View File
@@ -5,6 +5,7 @@ use std::fs::File;
use std::io::{BufReader, Read, Write};
use std::path::PathBuf;
use crate::util::{CustomResult, Error};
use apache_avro::types::Value;
use apache_avro::Schema::Union;
use apache_avro::{
@@ -13,8 +14,6 @@ use apache_avro::{
use resource::resource_str;
use rusqlite::Connection;
use serde::{Deserialize, Serialize};
use crate::util::{CustomResult, Error};
#[derive(Deserialize, Serialize, Debug, PartialEq)]
struct CacheChequeEvent {
@@ -50,7 +49,6 @@ mod test {
use super::*;
#[test]
fn test_basic() -> CustomResult<()> {
let file = tempfile().unwrap();
+22 -18
View File
@@ -1,7 +1,7 @@
use std::path::PathBuf;
use clap::{Arg, ArgAction, ArgMatches, Command, value_parser};
use clap::parser::ValuesRef;
use clap::{value_parser, Arg, ArgAction, ArgMatches, Command};
use log::{debug, error, info};
use rusqlite::Connection;
use serde::Serialize;
@@ -99,18 +99,14 @@ fn new_user_command_line_parser() -> Command {
}
fn ls_user_command_line_parser() -> Command {
return Command::new("ls")
.long_flag("ls")
.about("List all users.");
return Command::new("ls").long_flag("ls").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)))
.arg(clap::arg!(<PATH> ... "Stuff to add").value_parser(value_parser!(PathBuf)))
}
fn user_command_line_parser() -> Command {
@@ -136,16 +132,19 @@ fn ls_securities_command_line_parser() -> Command {
fn run_web_ui_command_line_parser() -> Command {
return Command::new("run")
.arg(Arg::new("port")
.long("port")
.value_parser(clap::value_parser!(u16).range(3000..))
.action(ArgAction::Set)
.default_value("3200"))
.arg(Arg::new("enable-template-autoreload")
.long("enable-template-autoreload")
.required(false)
.num_args(0)
.help("Reload underlying template (for development)")
.arg(
Arg::new("port")
.long("port")
.value_parser(clap::value_parser!(u16).range(3000..))
.action(ArgAction::Set)
.default_value("3200"),
)
.arg(
Arg::new("enable-template-autoreload")
.long("enable-template-autoreload")
.required(false)
.num_args(0)
.help("Reload underlying template (for development)"),
)
.about("Run web user interface.");
}
@@ -356,7 +355,12 @@ mod cli_test {
self.process_events_count += 1;
}
fn run_webui(&mut self, conn: &mut Connection, port: u16, enable_template_autoreload: bool) {
fn run_webui(
&mut self,
conn: &mut Connection,
port: u16,
enable_template_autoreload: bool,
) {
self.run_webui_count += 1;
}
}
+14 -9
View File
@@ -1,7 +1,7 @@
use std::time::Instant;
use resource::resource_str;
use rusqlite::{Connection, Transaction};
use sha2::{Digest, Sha256};
use resource::{resource_str};
use std::time::Instant;
#[derive(Debug)]
pub enum IntegrityError {
@@ -33,10 +33,10 @@ fn get_id_of_new_migration(tx: &Transaction) -> Result<usize, rusqlite::Error> {
return tx.query_row(q, (), |row| row.get::<usize, usize>(0));
}
pub fn migrate(conn: &mut Connection, migrations: &Vec<String>) -> Result<(), Error> {
let tx = conn.transaction().map_err(|e| Error::Sql(e))?;
tx.execute(INITIAL_MIGRATION, ()).map_err(|e| Error::Sql(e))?;
tx.execute(INITIAL_MIGRATION, ())
.map_err(|e| Error::Sql(e))?;
let id = get_id_of_new_migration(&tx).map_err(|e| Error::Sql(e))?;
for (i, migration) in migrations.iter().enumerate() {
@@ -56,13 +56,13 @@ fn apply_migration(tx: &Transaction, id: usize, migration: &String) -> Result<()
let q = "INSERT INTO shitty_migrations(id, sha256) VALUES (?1, ?2)";
tx.execute(q, (&id, &hash)).map_err(|e| Error::Sql(e))?;
let now = Instant::now();
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";
tx.execute(q, (elapsed_time.as_secs_f32(), &id)).map_err(|e| Error::Sql(e))?;
tx.execute(q, (elapsed_time.as_secs_f32(), &id))
.map_err(|e| Error::Sql(e))?;
return Ok(());
}
@@ -76,7 +76,9 @@ fn compute_migration_sha256(migration: &String) -> String {
fn check_migration(tx: &Transaction, id: usize, migration: &String) -> Result<(), Error> {
let q = "SELECT sha256 FROM shitty_migrations WHERE id = ?1";
let res = tx.query_row(q, (&id, ), |row| row.get::<usize, String>(0)).map_err(|e| Error::Sql(e))?;
let res = tx
.query_row(q, (&id,), |row| row.get::<usize, String>(0))
.map_err(|e| Error::Sql(e))?;
let expected = compute_migration_sha256(migration);
if !res.eq(&expected) {
return Err(Error::Integrity(IntegrityError::MigrationCheckFailed {
@@ -91,11 +93,14 @@ fn check_migration(tx: &Transaction, id: usize, migration: &String) -> Result<()
fn get_migrations() -> Vec<String> {
let mut ret = Vec::new();
ret.push(resource_str!("migrations/sql/create_tables_001.sql").as_ref().to_string());
ret.push(
resource_str!("migrations/sql/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);
+115 -75
View File
@@ -1,4 +1,4 @@
use rusqlite::{Connection, ffi, params, Savepoint, Transaction};
use rusqlite::{ffi, params, Connection, Savepoint, Transaction};
use crate::db::accounts;
use crate::db::accounts::get_account_by_id;
@@ -6,22 +6,32 @@ use crate::db::cheques::Cheque;
use crate::db::external_id::AccountId;
use crate::util::*;
fn _move_securities(tx: &Savepoint,
cheque: &Cheque,
account_id: &AccountId) -> CustomResult<(accounts::AccountChangeReceipt, accounts::AccountChangeReceipt)> {
fn _move_securities(
tx: &Savepoint,
cheque: &Cheque,
account_id: &AccountId,
) -> CustomResult<(
accounts::AccountChangeReceipt,
accounts::AccountChangeReceipt,
)> {
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)?;
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));
}
Ok((drawer_receipt, payee_receipt))
}
fn _add_cheque_to_cashed_cheques_log(tx: &Connection,
cheque: &Cheque,
drawer_receipt: &accounts::AccountChangeReceipt,
payee_receipt: &accounts::AccountChangeReceipt) -> Result<(), Error> {
fn _add_cheque_to_cashed_cheques_log(
tx: &Connection,
cheque: &Cheque,
drawer_receipt: &accounts::AccountChangeReceipt,
payee_receipt: &accounts::AccountChangeReceipt,
) -> Result<(), Error> {
let q = "
INSERT INTO cashed_cheques(
drawer_user_id,
@@ -36,49 +46,69 @@ fn _add_cheque_to_cashed_cheques_log(tx: &Connection,
)
VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)
";
let params = params![cheque.drawer_user_id,
cheque.drawer_account_id,
drawer_receipt.version,
cheque.payee_user_id,
payee_receipt.account_id,
payee_receipt.version,
cheque.cheque_number,
cheque.amount,
cheque.security_id];
let params = params![
cheque.drawer_user_id,
cheque.drawer_account_id,
drawer_receipt.version,
cheque.payee_user_id,
payee_receipt.account_id,
payee_receipt.version,
cheque.cheque_number,
cheque.amount,
cheque.security_id
];
let ret = tx.execute(q, params).map_err(Error::SqlError);
match ret {
Ok(_) => Ok(()),
Err(Error::SqlError(rusqlite::Error::SqliteFailure(ffi::Error { code: _, extended_code: 2067 }, _))) =>
Err(Error::DoubleSpendingError(format!("Cheque number {} from user {} has already been cached.", cheque.cheque_number, cheque.drawer_user_id))),
Err(a) => Err(a)
Err(Error::SqlError(rusqlite::Error::SqliteFailure(
ffi::Error {
code: _,
extended_code: 2067,
},
_,
))) => Err(Error::DoubleSpendingError(format!(
"Cheque number {} from user {} has already been cached.",
cheque.cheque_number, cheque.drawer_user_id
))),
Err(a) => Err(a),
}
}
fn cash_cheque_unsafe(savepoint: &Savepoint,
cheque: &Cheque,
account_id: &AccountId) -> CustomResult<()> {
fn cash_cheque_unsafe(
savepoint: &Savepoint,
cheque: &Cheque,
account_id: &AccountId,
) -> CustomResult<()> {
if cheque.amount < 0 {
return Err(Error::InvalidAmount(format!("The cheque {:?} has a negative amount.", cheque)));
return Err(Error::InvalidAmount(format!(
"The cheque {:?} has a negative amount.",
cheque
)));
}
if !accounts::is_owner(&savepoint, &cheque.drawer_account_id, &cheque.drawer_user_id)? {
return Err(Error::InvalidDrawer(format!("The drawer is the security owner: {:?}", cheque)));
if !accounts::is_owner(
&savepoint,
&cheque.drawer_account_id,
&cheque.drawer_user_id,
)? {
return Err(Error::InvalidDrawer(format!(
"The drawer is the security owner: {:?}",
cheque
)));
}
if !accounts::is_owner(&savepoint, account_id, &cheque.payee_user_id)? {
return Err(Error::InvalidPayee(format!("Trying to cash the cheque {:?} in \
an account {:?} that is not owned by the payee.", cheque, account_id)));
return Err(Error::InvalidPayee(format!(
"Trying to cash the cheque {:?} in \
an account {:?} that is not owned by the payee.",
cheque, account_id
)));
}
let (drawer_receipt, payee_receipt) = _move_securities(&savepoint, cheque, account_id)?;
_add_cheque_to_cashed_cheques_log(&savepoint,
cheque,
&drawer_receipt,
&payee_receipt)?;
_add_cheque_to_cashed_cheques_log(&savepoint, cheque, &drawer_receipt, &payee_receipt)?;
Ok(())
}
fn cash_cheque(tx: &mut Transaction,
cheque: &Cheque,
account_id: &AccountId) -> CustomResult<()> {
fn cash_cheque(tx: &mut Transaction, cheque: &Cheque, account_id: &AccountId) -> CustomResult<()> {
let mut savepoint = tx.savepoint().map_err(Error::SqlError)?;
let ret = cash_cheque_unsafe(&savepoint, cheque, account_id);
match ret {
@@ -86,13 +116,10 @@ fn cash_cheque(tx: &mut Transaction,
savepoint.rollback().map_err(Error::SqlError)?;
ret
}
Ok(_) => {
savepoint.commit().map_err(Error::SqlError)
}
Ok(_) => savepoint.commit().map_err(Error::SqlError),
}
}
#[cfg(test)]
mod test_caching_cheques {
use rusqlite::Connection;
@@ -118,7 +145,8 @@ mod test_caching_cheques {
let sec = securities.get(0).unwrap();
let drawer = users.get(0).unwrap();
let payee = users.get(1).unwrap();
let security_emitter_account = create_security_emitter_account(&tx, &sec.id(), &drawer.id()).unwrap();
let security_emitter_account =
create_security_emitter_account(&tx, &sec.id(), &drawer.id()).unwrap();
let payee_account = create_checking_account(&tx, &sec.id(), &payee.id()).unwrap();
tx.commit().unwrap();
@@ -127,9 +155,13 @@ mod test_caching_cheques {
(conn, security_emitter, payee, payee_account.id())
}
fn _create_100_secs_in_payee_account(tx: &mut Transaction, security_emitter: &Drawer, payee: &Payee, payee_account: &AccountId) {
let cheque = Cheque::new(
security_emitter.clone(), payee.clone(), 100, 99);
fn _create_100_secs_in_payee_account(
tx: &mut Transaction,
security_emitter: &Drawer,
payee: &Payee,
payee_account: &AccountId,
) {
let cheque = Cheque::new(security_emitter.clone(), payee.clone(), 100, 99);
cash_cheque(tx, &cheque, &payee_account).unwrap();
}
@@ -144,7 +176,8 @@ mod test_caching_cheques {
let mut tx = conn.transaction().unwrap();
_create_100_secs_in_payee_account(&mut tx, &security_emitter, &payee, &payee_account);
let reg_account = create_checking_account(&tx, &security_emitter.2, &security_emitter.0).unwrap();
let reg_account =
create_checking_account(&tx, &security_emitter.2, &security_emitter.0).unwrap();
tx.commit().unwrap();
@@ -154,14 +187,12 @@ mod test_caching_cheques {
return (conn, drawer, new_payee, reg_account.id());
}
#[test]
fn test_happy_path_currency_emitter() -> CustomResult<()> {
let (mut conn, security_emitter, payee, payee_account) = setup_security_emitter();
let mut tx = conn.transaction().unwrap();
let cheque = Cheque::new(
security_emitter, payee, 1000, 99);
let cheque = Cheque::new(security_emitter, payee, 1000, 99);
cash_cheque(&mut tx, &cheque, &payee_account)?;
tx.commit().unwrap();
@@ -178,8 +209,7 @@ mod test_caching_cheques {
let (mut conn, drawer, payee, payee_account) = setup_basic();
let mut tx = conn.transaction().unwrap();
let cheque = Cheque::new(
drawer.clone(), payee, 25, 10001);
let cheque = Cheque::new(drawer.clone(), payee, 25, 10001);
cash_cheque(&mut tx, &cheque, &payee_account)?;
@@ -192,16 +222,22 @@ mod test_caching_cheques {
Ok(())
}
fn test_negative_cheque(conn: &mut Connection, drawer: Drawer, payee: Payee, payee_account: AccountId) -> CustomResult<()> {
fn test_negative_cheque(
conn: &mut Connection,
drawer: Drawer,
payee: Payee,
payee_account: AccountId,
) -> CustomResult<()> {
let mut tx = conn.transaction().unwrap();
let cheque = Cheque::new(
drawer, payee, -1, 99);
let cheque = Cheque::new(drawer, payee, -1, 99);
let ret = cash_cheque(&mut tx, &cheque, &payee_account);
match ret {
Err(Error::InvalidAmount(_)) => Ok(()),
_ => Err(Error::Testing("Expected invalid amount error here.".to_string()))
_ => Err(Error::Testing(
"Expected invalid amount error here.".to_string(),
)),
}
}
@@ -217,15 +253,15 @@ mod test_caching_cheques {
test_negative_cheque(&mut conn, drawer, payee, payee_account)
}
fn test_double_spend(conn: &mut Connection, drawer: Drawer, payee: Payee, payee_account: AccountId) -> CustomResult<()> {
fn test_double_spend(
conn: &mut Connection,
drawer: Drawer,
payee: Payee,
payee_account: AccountId,
) -> CustomResult<()> {
let mut tx = conn.transaction().unwrap();
let cheque = Cheque::new(
drawer,
payee,
10,
99,
);
let cheque = Cheque::new(drawer, payee, 10, 99);
cash_cheque(&mut tx, &cheque, &payee_account)?;
tx.commit().unwrap();
@@ -234,7 +270,9 @@ mod test_caching_cheques {
let ret = cash_cheque(&mut tx, &cheque, &payee_account);
match ret {
Err(Error::DoubleSpendingError(_)) => Ok(()),
_ => Err(Error::Testing("Expected double spending error here.".to_string()))
_ => Err(Error::Testing(
"Expected double spending error here.".to_string(),
)),
}
}
@@ -250,21 +288,21 @@ mod test_caching_cheques {
test_double_spend(&mut conn, drawer, payee, payee_account)
}
fn test_invalid_drawer(conn: &mut Connection, drawer: Drawer, payee: Payee, payee_account: AccountId) -> CustomResult<()> {
fn test_invalid_drawer(
conn: &mut Connection,
drawer: Drawer,
payee: Payee,
payee_account: AccountId,
) -> CustomResult<()> {
let mut tx = conn.transaction().unwrap();
let not_currency_owner_user_id = UserId::from_id(drawer.0.get_id() + 72623);
let not_currency_owner: Drawer = (not_currency_owner_user_id, drawer.1, drawer.2);
let cheque = Cheque::new(
not_currency_owner,
payee,
100,
99,
);
let cheque = Cheque::new(not_currency_owner, payee, 100, 99);
let ret = cash_cheque(&mut tx, &cheque, &payee_account);
match ret {
Err(Error::InvalidDrawer(_)) => Ok(()),
_ => Err(Error::Testing("Expected invalid drawer here.".to_string()))
_ => Err(Error::Testing("Expected invalid drawer here.".to_string())),
}
}
@@ -280,22 +318,24 @@ mod test_caching_cheques {
test_invalid_drawer(&mut conn, drawer, payee, payee_account)
}
fn test_invalid_payee(conn: &mut Connection, security_emitter: Drawer, payee: Payee) -> 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 tx = conn.transaction().unwrap();
let wrong_payee_account = &security_emitter.1;
let cheque = Cheque::new(
security_emitter.clone(), payee.clone(), 1000, 99);
let cheque = Cheque::new(security_emitter.clone(), payee.clone(), 1000, 99);
let ret = cash_cheque(&mut tx, &cheque, wrong_payee_account);
match ret {
Err(Error::InvalidPayee(_)) => Ok(()),
_ => Err(Error::Testing("Expected invalid payee here.".to_string()))
_ => Err(Error::Testing("Expected invalid payee here.".to_string())),
}
}
#[test]
fn test_invalid_payee_security_emitter_cheque() -> CustomResult<()> {
let (mut conn, security_emitter, payee, payee_account) = setup_security_emitter();
+2 -3
View File
@@ -3,12 +3,12 @@ mod views;
use actix_utils::future::{ready, Ready};
use minijinja;
use minijinja_autoreload::AutoReloader;
use std::{collections::HashMap, env, fs, io, path::PathBuf};
use std::io::{Error, ErrorKind};
use std::path::Path;
use std::{collections::HashMap, env, fs, io, path::PathBuf};
use actix_web::{
dev::{ServiceResponse},
dev::ServiceResponse,
error,
http::{header::ContentType, StatusCode},
middleware::{ErrorHandlerResponse, ErrorHandlers, Logger},
@@ -54,7 +54,6 @@ impl FromRequest for MiniJinjaRenderer {
}
}
#[tokio::main]
pub async fn web_ui_main(port: u16, enable_template_autoreload: bool) -> std::io::Result<()> {
// The closure is invoked every time the environment is outdated to recreate it.
+2 -2
View File
@@ -1,6 +1,6 @@
use std::collections::HashMap;
use actix_web::{Responder, web};
use crate::webui::MiniJinjaRenderer;
use actix_web::{web, Responder};
use std::collections::HashMap;
#[actix_web::get("/")]
pub async fn index(
Executable
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
find ${PROJECT_ROOT}/src -name '*.rs' | xargs rustfmt --edition 2021