This commit is contained in:
Lewis Diamond 2021-03-14 19:41:33 -04:00
parent da582df6eb
commit d837b60327
2 changed files with 83 additions and 15 deletions

View File

@ -41,11 +41,18 @@ deposit,1,3,2.0
withdrawal,1,4,1.5
withdrawal,2,5,3.0";
let expected = vec![Transaction {tx_type: TransactionType::Deposit, amount: 10000, client: 1, tx: 1}];
let expected = vec![
Transaction {tx_type: TransactionType::Deposit, amount: 10000, client: 1, tx: 1},
Transaction {tx_type: TransactionType::Deposit, amount: 20000, client: 2, tx: 2},
Transaction {tx_type: TransactionType::Deposit, amount: 20000, client: 1, tx: 3},
Transaction {tx_type: TransactionType::Withdrawal, amount: 15000, client: 1, tx: 4},
Transaction {tx_type: TransactionType::Withdrawal, amount: 30000, client: 2, tx: 5},
];
let txs = parse(data.as_bytes()).collect::<Vec<Transaction>>().await;
assert_eq!(expected, txs);
});
}
#[test]
fn valid_csv_with_whitespaces_is_parsed() {
block_on(async {
@ -57,9 +64,62 @@ deposit, 1, 3, 2.0
withdrawal, 1, 4, 1.5
withdrawal, 2, 5, 3.0";
let txs = parse(data.as_bytes());
let result: Vec<Transaction> = txs.collect::<Vec<Transaction>>().await;
result.iter().for_each(|t| eprintln!("{:?}", t));
let expected = vec![
Transaction {tx_type: TransactionType::Deposit, amount: 10000, client: 1, tx: 1},
Transaction {tx_type: TransactionType::Deposit, amount: 20000, client: 2, tx: 2},
Transaction {tx_type: TransactionType::Deposit, amount: 20000, client: 1, tx: 3},
Transaction {tx_type: TransactionType::Withdrawal, amount: 15000, client: 1, tx: 4},
Transaction {tx_type: TransactionType::Withdrawal, amount: 30000, client: 2, tx: 5},
];
let txs = parse(data.as_bytes()).collect::<Vec<Transaction>>().await;
assert_eq!(expected, txs);
});
}
#[test]
fn amounts_are_parsed_correctly() {
block_on(async {
let data = "\
type,client,tx,amount
deposit,1,1,1.0001
deposit,2,2,2.0010
deposit,1,3,10.01
withdrawal,1,4,01.10
withdrawal,2,5,10.0110101";
let expected = vec![
Transaction {tx_type: TransactionType::Deposit, amount: 10001, client: 1, tx: 1},
Transaction {tx_type: TransactionType::Deposit, amount: 20010, client: 2, tx: 2},
Transaction {tx_type: TransactionType::Deposit, amount: 100100, client: 1, tx: 3},
Transaction {tx_type: TransactionType::Withdrawal, amount: 11000, client: 1, tx: 4},
Transaction {tx_type: TransactionType::Withdrawal, amount: 100110, client: 2, tx: 5},
];
let txs = parse(data.as_bytes()).collect::<Vec<Transaction>>().await;
assert_eq!(expected, txs);
});
}
#[test]
fn invalid_amoutns_are_filtered() {
block_on(async {
let data = "\
type,client,tx,amount
deposit,1,1,99999999999999999
deposit,2,2,18446744073709551615
deposit,1,3,18446744073709551616
withdrawal,1,4,0
withdrawal,2,5,-1
withdrawal,1,6,-99999999999999999
withdrawal,1,6,-18446744073709551615
withdrawal,1,7,-18446744073709551616";
let expected = vec![
Transaction {tx_type: TransactionType::Withdrawal, amount: 0, client: 1, tx: 4},
];
let txs = parse(data.as_bytes()).collect::<Vec<Transaction>>().await;
println!("PARSE: {}", "0.1".parse::<f32>().unwrap());
assert_eq!(expected, txs);
});
}
}

View File

@ -1,5 +1,6 @@
use serde::{Deserialize, Deserializer};
use serde::de;
use serde::{Deserialize, Deserializer};
const PRECISION: u32 = 4;
#[derive(Eq, PartialEq, Debug, Deserialize)]
#[serde(rename_all = "lowercase")]
@ -12,24 +13,31 @@ pub enum TransactionType {
pub struct Transaction {
#[serde(rename = "type")]
pub tx_type: TransactionType,
pub client: usize,
pub client: u16,
pub tx: usize,
/// Amount of the smallest unit, e.g. 0.0001 as per the specification
#[serde(deserialize_with = "de_amount")]
pub amount: usize,
}
fn de_amount<'de, D>(deserializer: D) -> Result<usize, D::Error> where D: Deserializer<'de> {
fn de_amount<'de, D>(deserializer: D) -> Result<usize, D::Error>
where
D: Deserializer<'de>,
{
//TODO validate for input such as `.100` so it doesn't give 100
let deserialized = String::deserialize(deserializer)?;
let mut splitted = deserialized.split('.');
let mut ret: usize = 0;
let mut factor = 4;
while let Some(s) = splitted.next() {
ret += s.parse::<usize>().map_err(de::Error::custom)? * 10usize.pow(factor);
if factor < 1 { break;}
factor -= 4;
};
let units = splitted
.next()
.map_or(Ok(0usize), |v| v.parse::<usize>())
.map_err(de::Error::custom)?
.checked_mul(10usize.pow(PRECISION))
.ok_or_else(|| de::Error::custom("Value too large"))?;
//TODO improve this to avoid `format!`
let dec = splitted
.next()
.map_or(Ok(0usize), |v| format!("{:0<4.4}", v).parse::<usize>())
.map_err(de::Error::custom)?;
Ok(ret)
Ok(units + dec)
}