use self::exchange_rate_request::ExchangeRateRequest;
use self::price_update::PriceUpdate;
use crate::IndexMapTrait;
use indexmap::map::{Entry, IndexMap};
use std::clone::Clone;
use std::fmt::Debug;
use std::io::BufRead;
use std::str::FromStr;
mod exchange_rate_request;
mod price_update;
pub struct Request<N, E> {
price_updates: IndexMap<(N, N, N), PriceUpdate<N, E>>,
rate_requests: IndexMap<(N, N, N, N), ExchangeRateRequest<N>>,
}
impl<N, E> Request<N, E>
where
N: Clone + FromStr + IndexMapTrait,
<N as FromStr>::Err: Debug,
E: FromStr,
<E as FromStr>::Err: Debug,
{
fn new() -> Self {
let price_updates = IndexMap::new();
let rate_requests = IndexMap::new();
Self {
price_updates,
rate_requests,
}
}
pub fn read_from<I: BufRead>(input: &mut I) -> Self {
let mut request = Self::new();
for line in input.lines() {
if let Ok(s) = line {
request.process_line(&s);
}
}
request
}
fn process_line(&mut self, line: &str) {
let mut iter = line.split_whitespace();
if let Some(first_item) = iter.next() {
match first_item.to_uppercase().as_ref() {
ExchangeRateRequest::<N>::LINE_TYPE => {
match ExchangeRateRequest::<N>::parse_line(line) {
Ok(rate_request) => self.add_rate_request(rate_request),
Err(errors) => panic!(
"Errors occurred while processing input lines, errors: {:?}!",
errors
),
}
}
_ => match PriceUpdate::<N, E>::parse_line(line) {
Ok(price_update) => self.add_price_update(price_update),
Err(errors) => panic!(
"Errors occurred while processing input lines, errors: {:?}!",
errors
),
},
}
}
}
fn add_rate_request(&mut self, rate_request: ExchangeRateRequest<N>) {
self.rate_requests
.insert(rate_request.get_index(), rate_request);
}
fn add_price_update(&mut self, price_update: PriceUpdate<N, E>) {
let entry = self.price_updates.entry(price_update.get_index());
match entry {
Entry::Occupied(o) => {
let existing = o.get();
if price_update.get_timestamp() > existing.get_timestamp() {
*o.into_mut() = price_update;
}
}
Entry::Vacant(v) => {
v.insert(price_update);
}
}
}
pub fn get_price_updates(&self) -> &IndexMap<(N, N, N), PriceUpdate<N, E>> {
&self.price_updates
}
pub fn get_rate_requests(&self) -> &IndexMap<(N, N, N, N), ExchangeRateRequest<N>> {
&self.rate_requests
}
}
#[cfg(test)]
mod tests {
use crate::request::Request;
use std::io::BufReader;
#[test]
fn process_line() {
let mut request = Request::<String, f32>::new();
let price_update_line =
String::from("2017-11-01T09:42:23+00:00 KRAKEN BTC USD 1000.0 0.0009");
request.process_line(&price_update_line);
assert_eq!(request.price_updates.len(), 1);
assert_eq!(request.rate_requests.len(), 0);
let price_update_line = String::from("EXCHANGE_RATE_REQUEST KRAKEN BTC GDAX ETH");
request.process_line(&price_update_line);
assert_eq!(request.price_updates.len(), 1);
assert_eq!(request.rate_requests.len(), 1);
}
#[test]
fn read_from() {
let text_input = "2017-11-01T09:42:23+00:00 KRAKEN BTC USD 1000.0 0.0009
2018-11-01T09:42:23+00:00 KRAKEN ETH USD 100.0 0.001
EXCHANGE_RATE_REQUEST KRAKEN BTC GDAX ETH
EXCHANGE_RATE_REQUEST GDAX BTC KRAKEN USD"
.as_bytes();
let mut input = BufReader::new(text_input);
let request = Request::<String, f32>::read_from(&mut input);
assert_eq!(request.price_updates.len(), 2);
assert_eq!(request.rate_requests.len(), 2);
}
#[test]
fn read_from_skip_empty_lines() {
let text_input = "2017-11-01T09:42:23+00:00 KRAKEN BTC USD 1000.0 0.0009
\n
2018-11-01T09:42:23+00:00 KRAKEN ETH USD 100.0 0.001
\n\n
EXCHANGE_RATE_REQUEST KRAKEN BTC GDAX ETH
EXCHANGE_RATE_REQUEST KRAKEN ETH GDAX USD
EXCHANGE_RATE_REQUEST KRAKEN COIN GDAX USD
EXCHANGE_RATE_REQUEST GDAX BTC KRAKEN USD"
.as_bytes();
let mut input = BufReader::new(text_input);
let request = Request::<String, f32>::read_from(&mut input);
assert_eq!(request.price_updates.len(), 2);
assert_eq!(request.rate_requests.len(), 4);
}
}