I should be working...
This commit is contained in:
1 parent
a6d45edd41
commit
7ecbf250a0
8 files changed
+88
-45
No files matched your search
@@ -0,0 +1,177 @@
|
||||
use rusqlite::{Connection, params, Result, Row, ToSql, Transaction};
|
||||
use serde::Serialize;
|
||||
|
||||
|
||||
#[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 {
|
||||
pub id: i32,
|
||||
security_type: SecurityType,
|
||||
symbol: String,
|
||||
}
|
||||
|
||||
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 {
|
||||
security_type: SecurityType,
|
||||
symbol: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
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 get_security_by_id(tx, tx.last_insert_rowid() as i32);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
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 = mock_currency_1();
|
||||
let tx = conn.transaction()?;
|
||||
create_security(&tx, &sec)?;
|
||||
return tx.commit();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_security() -> Result<(), rusqlite::Error> {
|
||||
let mut conn = setup();
|
||||
assert_eq!(0, list_securities(&conn)?.len());
|
||||
|
||||
let sec = mock_currency_1();
|
||||
let tx = conn.transaction()?;
|
||||
create_security(&tx, &sec)?;
|
||||
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)?;
|
||||
tx.commit()?;
|
||||
let ret = list_securities(&conn)?;
|
||||
assert_eq!(2, ret.len());
|
||||
assert_eq!(&expt, ret.get(1).unwrap());
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user