use self::Items::*;
use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
#[derive(Eq, PartialEq, Hash)]
pub enum Items {
LineType,
SourceExchange,
SourceCurrency,
DestinationExchange,
DestinationCurrency,
}
impl Items {
pub fn get_label(&self) -> String {
match self {
LineType => "EXCHANGE_RATE_REQUEST".to_string(),
SourceExchange => "source_exchange".to_string(),
SourceCurrency => "source_currency".to_string(),
DestinationExchange => "source_exchange".to_string(),
DestinationCurrency => "destination_exchange".to_string(),
}
}
}
impl fmt::Display for Items {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.get_label())
}
}
pub struct ExchangeRateRequest<N> {
source_exchange: N,
source_currency: N,
destination_exchange: N,
destination_currency: N,
}
impl<N> ExchangeRateRequest<N>
where
N: Clone + FromStr,
<N as FromStr>::Err: fmt::Debug,
{
pub const LINE_TYPE: &'static str = "EXCHANGE_RATE_REQUEST";
pub fn new(
source_exchange: N,
source_currency: N,
destination_exchange: N,
destination_currency: N,
) -> Self {
Self {
source_exchange,
source_currency,
destination_exchange,
destination_currency,
}
}
pub fn get_index(&self) -> (N, N, N, N) {
(
self.source_exchange.clone(),
self.source_currency.clone(),
self.destination_exchange.clone(),
self.destination_currency.clone(),
)
}
pub fn get_source_exchange(&self) -> &N {
&self.source_exchange
}
pub fn get_source_currency(&self) -> &N {
&self.source_currency
}
pub fn get_destination_exchange(&self) -> &N {
&self.destination_exchange
}
pub fn get_destination_currency(&self) -> &N {
&self.destination_currency
}
pub fn parse_line(line: &str) -> Result<ExchangeRateRequest<N>, Vec<String>> {
let mut iter = line.split_whitespace();
let mut values = HashMap::new();
let mut errors: Vec<String> = Vec::new();
for item in &[
LineType,
SourceExchange,
SourceCurrency,
DestinationExchange,
DestinationCurrency,
] {
let value: Option<&str> = iter.next();
match value {
Some(s) => {
values.insert(item, s);
}
None => {
errors.push(format!("The line item <{}> is missing!", item));
}
}
}
if !errors.is_empty() {
return Err(errors);
}
let line_type = values[&LineType].to_uppercase();
if line_type != Self::LINE_TYPE {
errors.push(format!(
"The line item type identifier at the beginning of the line {} is wrong!",
Self::LINE_TYPE
));
return Err(errors);
}
let source_exchange = values[&SourceExchange].to_uppercase().parse::<N>();
if source_exchange.is_err() {
errors.push(format!(
"The line item <{}> can not be parsed (wrong format)!",
&SourceExchange
));
}
let source_currency = values[&SourceCurrency].to_uppercase().parse::<N>();
if source_currency.is_err() {
errors.push(format!(
"The line item <{}> can not be parsed (wrong format)!",
&SourceCurrency
));
}
let destination_exchange = values[&DestinationExchange].to_uppercase().parse::<N>();
if destination_exchange.is_err() {
errors.push(format!(
"The line item <{}> can not be parsed (wrong format)!",
&DestinationExchange
));
}
let destination_currency = values[&DestinationCurrency].to_uppercase().parse::<N>();
if destination_currency.is_err() {
errors.push(format!(
"The line item <{}> can not be parsed (wrong format)!",
&DestinationCurrency
));
}
if !errors.is_empty() {
return Err(errors);
}
Ok(Self::new(
source_exchange.unwrap(),
source_currency.unwrap(),
destination_exchange.unwrap(),
destination_currency.unwrap(),
))
}
}
#[cfg(test)]
mod tests {
use crate::request::exchange_rate_request::ExchangeRateRequest;
use crate::request::exchange_rate_request::Items::*;
#[test]
fn parse_line() {
let line = "EXCHANGE_RATE_REQUEST KRAKEN BTC GDAX ETH";
let rate_request = ExchangeRateRequest::<String>::parse_line(&line.to_string());
assert!(rate_request.is_ok());
let rate_request = rate_request.unwrap();
assert_eq!(rate_request.source_exchange, "KRAKEN");
assert_eq!(rate_request.source_currency, "BTC");
assert_eq!(rate_request.destination_exchange, "GDAX");
assert_eq!(rate_request.destination_currency, "ETH");
}
#[test]
fn parse_line_with_wrong_line_type() {
let line = "WRONG_LINE_TYPE KRAKEN BTC GDAX ETH";
let price_update = ExchangeRateRequest::<String>::parse_line(&line.to_string());
assert!(price_update.is_err());
let mut errors = price_update.err().unwrap();
assert_eq!(
errors.pop().unwrap(),
format!(
"The line item type identifier at the beginning of the line {} is wrong!",
ExchangeRateRequest::<String>::LINE_TYPE
)
);
assert!(errors.pop().is_none());
}
#[test]
fn parse_line_with_missing_values() {
let line = "";
let price_update = ExchangeRateRequest::<String>::parse_line(&line.to_string());
assert!(price_update.is_err());
let mut errors = price_update.err().unwrap();
assert_eq!(
errors.pop().unwrap(),
format!("The line item <{}> is missing!", DestinationCurrency)
);
assert_eq!(
errors.pop().unwrap(),
format!("The line item <{}> is missing!", DestinationExchange)
);
assert_eq!(
errors.pop().unwrap(),
format!("The line item <{}> is missing!", SourceCurrency)
);
assert_eq!(
errors.pop().unwrap(),
format!("The line item <{}> is missing!", SourceExchange)
);
assert_eq!(
errors.pop().unwrap(),
format!("The line item <{}> is missing!", LineType)
);
assert!(errors.pop().is_none());
}
}