use serde::de::DeserializeOwned;
use reqwest::Response;
use crate::error::Error;
use crate::{CCUnit, CCAPIEndpoint};
use crate::schemas::data_api::spot::CCSpotInstrumentStatus;
use crate::schemas::data_api::news::{CCNewsLang, CCNewsSourceID, CCNewsSourceType, CCNewsStatus};
#[cfg(feature = "debug")]
fn debug() -> Result<bool, Error> {
dotenv::dotenv()?;
let debug: bool = serde_json::from_str(&std::env::var("CCDATA_API_DEBUG")?)?;
Ok(debug)
}
pub trait Market {
fn to_string(&self) -> String;
}
fn vec_to_str(vec: &Vec<String>) -> String {
let mut s: String = String::from("");
for v in vec {
s.push_str(&v);
s.push_str(&",");
}
s.pop();
s
}
fn optional_vec_arguments(o: Option<Vec<String>>, api_argument: String) -> String {
let mut s: String = String::from("");
match o {
Some(v) => {
s.push_str(&api_argument);
s.push_str(&vec_to_str(&v));
},
None => (),
}
s
}
pub enum Param<'a> {
Symbol { v: &'a str },
Instrument { v: &'a str },
Instruments { v: &'a Vec<String> },
ChainAsset { v: &'a str },
Asset {v: &'a str},
Assets {v: Vec<String>},
ToTs {v: Option<i64>},
ToTimestamp { v: Option<i64> },
Limit { v: Option<usize> },
Market {v: String},
InstrumentStatus { v: CCSpotInstrumentStatus },
OCCoreBlockNumber { v: i64 },
OCCoreAddress { v: &'a str },
OCCoreQuoteAsset { v: &'a str },
NewsLanguage { v: CCNewsLang },
NewsSourceID { v: CCNewsSourceID },
NewsCategories { v: Option<Vec<String>> },
NewsExcludeCategories { v: Option<Vec<String>> },
NewsSourceType { v: CCNewsSourceType },
NewsStatus { v: CCNewsStatus },
}
impl<'a> Param<'a> {
fn add_param_to_url(&self, url: &mut String) -> () {
let url_param: String = match self {
Self::Symbol { v } => format!("&fsym={}", v),
Self::Instrument { v } => format!("&instrument={}", v),
Self::Instruments { v } => format!("&instruments={}", vec_to_str(v.to_owned())),
Self::ChainAsset { v } => format!("&chain_asset={}", v),
Self::Asset { v } => format!("&asset={}", v),
Self::Assets { v } => format!("&assets={}", v.join(",")),
Self::ToTs { v } => match v {
Some(v_) => format!("&toTs={}", v_),
None => String::from(""),
},
Self::ToTimestamp { v } => match v {
Some(v_) => format!("&to_ts={}", v_),
None => String::from(""),
},
Self::Limit { v } => match v {
Some(v_) => format!("&limit={}", v_),
None => String::from("&limit=2000"),
},
Self::Market { v } => format!("&market={}", v),
Self::InstrumentStatus { v } => format!("&instrument_status={}", v.to_string()),
Self::OCCoreBlockNumber { v } => format!("&block_number={}", v),
Self::OCCoreAddress { v } => format!("&address={}", v),
Self::OCCoreQuoteAsset { v } => format!(""e_asset={}", v),
Self::NewsLanguage { v } => format!("&lang={}", v.to_string()),
Self::NewsSourceID { v } => format!("&source_ids={}", v.to_string()),
Self::NewsCategories { v } => optional_vec_arguments(v.to_owned(), String::from("&categories=")),
Self::NewsExcludeCategories { v } => optional_vec_arguments(v.to_owned(), String::from("&exclude_categories=")),
Self::NewsSourceType { v } => format!("&source_type={}", v.to_string()),
Self::NewsStatus { v } => format!("&status={}", v.to_string()),
};
url.push_str(&url_param);
}
}
async fn process_request<T: DeserializeOwned>(url: String) -> Result<T, Error> {
let response: Response = reqwest::get(url).await?;
let response_body: String = response.text().await?;
#[cfg(feature = "debug")]
if debug()? { println!("{:?}", response_body) }
let response_body: String = response_body.replace("{}", "null");
let data: T = serde_json::from_str(&response_body)?;
Ok(data)
}
pub async fn call_api_endpoint<'a, R: DeserializeOwned>(
api_key: &str, endpoint: CCAPIEndpoint, unit: CCUnit, params: Vec<Param<'a>>, additional_params: Option<String>) -> Result<R, Error>
{
let mut url: String = endpoint.url(&unit);
url.push_str(&format!("?api_key={}", api_key));
for param in params {
param.add_param_to_url(&mut url);
}
match additional_params {
Some(v) => url.push_str(&v),
None => (),
}
process_request::<R>(url).await
}