Files
escarmouche/src/db/_securities.rs
T

182 lines
5.1 KiB
Rust

use rusqlite::{Connection, OptionalExtension, params, Result, Row, ToSql, Transaction};
use serde::Serialize;
use crate::db::_external_id::{Id, SecurityId, UserId};
use crate::db::users::User;
#[derive(Debug, Serialize, PartialEq)]
pub enum SecurityType {
Currency,
Bond,
}
impl SecurityType {
fn from_string(s: String) -> SecurityType {
if s == "currency".to_string() {
return SecurityType::Currency;
}
if s == "bond".to_string() {
return SecurityType::Bond;
}
panic!("Impossible to convert string {} to SecurityType.", s);
}
}
impl ToSql for SecurityType {
#[inline]
fn to_sql(&self) -> Result<rusqlite::types::ToSqlOutput<'_>> {
Ok(rusqlite::types::ToSqlOutput::from(match self {
SecurityType::Currency => "currency",
SecurityType::Bond => "bond",
}))
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct Security {
id: i64,
security_type: SecurityType,
symbol: String,
}
impl Security {
pub fn id(&self) -> SecurityId {
SecurityId::from_id(self.id)
}
}
impl Security {
pub fn from_row(row: &Row) -> Result<Security> {
Ok(Security {
id: row.get("sec_id")?,
security_type: SecurityType::from_string(row.get("sec_security_type")?),
symbol: row.get("sec_symbol")?,
})
}
}
pub struct NewSecurity {
pub security_type: SecurityType,
pub symbol: String,
pub name: String,
}
pub fn create_security(tx: &Transaction,
sec: &NewSecurity,
owner: &UserId) -> 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)?;
let new_sec_id = tx.last_insert_rowid() as i32;
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
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 AS sec_id, security_type AS sec_security_type, symbol AS sec_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);
}
#[cfg(test)]
mod test_securities {
use rusqlite::{Connection, params};
use crate::db::_test_utils::*;
use crate::migration;
use super::*;
fn setup() -> (Connection, Vec<User>) {
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();
tx.commit().unwrap();
return (conn, users);
}
#[test]
fn test_security_types() -> Result<(), rusqlite::Error> {
let (mut conn, users) = 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, users) = setup();
let owner = users.get(0).unwrap();
let not_owner = users.get(1).unwrap();
let sec = mock_currency_1();
let tx = conn.transaction()?;
let sec = create_security(&tx, &sec, &owner.id())?;
tx.commit()
}
#[test]
fn test_list_security() -> Result<(), rusqlite::Error> {
let (mut conn, users) = setup();
let user = users.get(0).unwrap();
assert_eq!(0, list_securities(&conn)?.len());
let sec = mock_currency_1();
let tx = conn.transaction()?;
create_security(&tx, &sec, &user.id())?;
tx.commit()?;
let ret = list_securities(&conn)?;
let expt = Security {
id: 1,
security_type: SecurityType::Currency,
symbol: "P".to_string(),
};
assert_eq!(1, ret.len());
assert_eq!(&expt, ret.get(0).unwrap());
let sec = mock_currency_2();
let expt = Security {
id: 2,
security_type: SecurityType::Bond,
symbol: "B".to_string(),
};
let tx = conn.transaction()?;
create_security(&tx, &sec, &user.id())?;
tx.commit()?;
let ret = list_securities(&conn)?;
assert_eq!(2, ret.len());
assert_eq!(&expt, ret.get(1).unwrap());
return Ok(());
}
}