use std::cmp;
use std::collections::HashMap;
use serde::Deserialize;
use crate::api::ApiClient;
use crate::deserialize::from_str;
use crate::error::{Error, Result};
use crate::vec_trait::FindData;
#[derive(Deserialize, Clone, Default)]
struct MetaData {
#[serde(rename = "1. Information")]
information: String,
#[serde(rename = "2. Digital Currency Code")]
digital_code: String,
#[serde(rename = "3. Digital Currency Name")]
digital_name: String,
#[serde(rename = "4. Market Code")]
market_code: String,
#[serde(rename = "5. Market Name")]
market_name: String,
#[serde(rename = "6. Last Refreshed")]
last_refreshed: String,
#[serde(rename = "7. Time Zone")]
time_zone: String,
}
#[derive(Default, Debug, Clone)]
pub struct Data {
time: String,
open: f64,
high: f64,
low: f64,
close: f64,
volume: f64,
}
impl Data {
#[must_use]
pub fn time(&self) -> &str {
&self.time
}
#[must_use]
pub fn open(&self) -> f64 {
self.open
}
#[must_use]
pub fn high(&self) -> f64 {
self.high
}
#[must_use]
pub fn low(&self) -> f64 {
self.low
}
#[must_use]
pub fn close(&self) -> f64 {
self.close
}
#[must_use]
pub fn volume(&self) -> f64 {
self.volume
}
}
#[derive(Default)]
pub struct Crypto {
meta_data: MetaData,
data: Vec<Data>,
}
impl Crypto {
#[must_use]
pub fn information(&self) -> &str {
self.return_meta_string("information")
}
#[must_use]
pub fn digital_code(&self) -> &str {
self.return_meta_string("digital code")
}
#[must_use]
pub fn digital_name(&self) -> &str {
self.return_meta_string("digital name")
}
#[must_use]
pub fn market_code(&self) -> &str {
self.return_meta_string("market code")
}
#[must_use]
pub fn market_name(&self) -> &str {
self.return_meta_string("market name")
}
#[must_use]
pub fn last_refreshed(&self) -> &str {
self.return_meta_string("last refreshed")
}
#[must_use]
pub fn time_zone(&self) -> &str {
self.return_meta_string("time zone")
}
#[must_use]
pub fn data(&self) -> &Vec<Data> {
&self.data
}
fn return_meta_string(&self, which_val: &str) -> &str {
match which_val {
"information" => &self.meta_data.information,
"digital code" => &self.meta_data.digital_code,
"digital name" => &self.meta_data.digital_name,
"market code" => &self.meta_data.market_code,
"market name" => &self.meta_data.market_name,
"time zone" => &self.meta_data.time_zone,
"last refreshed" => &self.meta_data.last_refreshed,
_ => "",
}
}
}
#[derive(Deserialize, Clone)]
struct DataHelper {
#[serde(rename = "1. open", deserialize_with = "from_str")]
open: f64,
#[serde(rename = "2. high", deserialize_with = "from_str")]
high: f64,
#[serde(rename = "3. low", deserialize_with = "from_str")]
low: f64,
#[serde(rename = "4. close", deserialize_with = "from_str")]
close: f64,
#[serde(rename = "5. volume", deserialize_with = "from_str")]
volume: f64,
}
#[derive(Deserialize)]
pub(crate) struct CryptoHelper {
#[serde(rename = "Meta Data")]
meta_data: Option<MetaData>,
#[serde(flatten)]
data: Option<HashMap<String, HashMap<String, DataHelper>>>,
}
impl CryptoHelper {
fn convert(self) -> Result<Crypto> {
if self.meta_data.is_none() || self.data.is_none() {
return Err(Error::EmptyResponse);
}
let mut vec_data = Vec::new();
for value in self.data.unwrap().values() {
for key in value.keys() {
let data_helper = value
.get(key)
.expect("failed to get value from crypto hashmap");
let data = Data {
time: key.to_string(),
open: data_helper.open,
high: data_helper.high,
low: data_helper.low,
close: data_helper.close,
volume: data_helper.volume,
};
vec_data.push(data);
}
}
Ok(Crypto {
data: vec_data,
meta_data: self.meta_data.unwrap(),
})
}
}
impl FindData for Vec<Data> {
fn find(&self, time: &str) -> Option<&<Self as IntoIterator>::Item> {
self.iter().find(|&data| data.time == time)
}
fn latest(&self) -> <Self as IntoIterator>::Item {
let mut latest = &Data::default();
for data in self {
if latest.time < data.time {
latest = data;
}
}
latest.clone()
}
fn latest_n(&self, n: usize) -> Result<Vec<&<Self as IntoIterator>::Item>> {
let mut time_list = self.iter().map(|data| &data.time).collect::<Vec<_>>();
time_list.sort_by_key(|w| cmp::Reverse(*w));
if n > time_list.len() {
return Err(Error::DesiredNumberOfDataNotPresent(time_list.len()));
}
let mut full_list = Vec::<&Data>::new();
for time in &time_list[0..n] {
full_list.push(self.find(time).unwrap());
}
Ok(full_list)
}
}
pub struct CryptoBuilder<'a> {
api_client: &'a ApiClient,
function: CryptoFunction,
symbol: &'a str,
market: &'a str,
}
impl<'a> CryptoBuilder<'a> {
crate::json_data_struct!(Crypto, CryptoHelper);
#[must_use]
pub fn new(
api_client: &'a ApiClient,
function: CryptoFunction,
symbol: &'a str,
market: &'a str,
) -> Self {
Self {
api_client,
function,
symbol,
market,
}
}
fn create_url(&self) -> String {
let function_name = match self.function {
CryptoFunction::Daily => "DIGITAL_CURRENCY_DAILY",
CryptoFunction::Weekly => "DIGITAL_CURRENCY_WEEKLY",
CryptoFunction::Monthly => "DIGITAL_CURRENCY_MONTHLY",
};
format!(
"query?function={function_name}&symbol={}&market={}",
&self.symbol, &self.market
)
}
}
#[derive(Clone)]
pub enum CryptoFunction {
Daily,
Weekly,
Monthly,
}