use crate::models::prelude::AssetType;
use crate::{models::*, BuildQueryParametersExt};
#[derive(Clone, PartialEq, Debug)]
pub struct BaseAsset(AssetType);
#[derive(PartialEq, Debug)]
pub struct NoBaseAsset;
#[derive(Clone, PartialEq, Debug)]
pub struct CounterAsset(AssetType);
#[derive(PartialEq, Debug)]
pub struct NoCounterAsset;
#[derive(Default, Clone)]
pub struct NoResolution;
#[derive(PartialEq, Debug, Default, Clone)]
pub struct Resolution(pub ResolutionData);
#[derive(PartialEq, Debug, Default, Clone)]
pub enum ResolutionData {
#[default]
Duration60000,
Duration300000,
Duration900000,
Duration3600000,
Duration604800000,
}
impl std::fmt::Display for ResolutionData {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
ResolutionData::Duration60000 => write!(f, "60000"), ResolutionData::Duration300000 => write!(f, "300000"), ResolutionData::Duration900000 => write!(f, "900000"), ResolutionData::Duration3600000 => write!(f, "3600000"), ResolutionData::Duration604800000 => write!(f, "604800000"), }
}
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct TradeAggregationsRequest<B = NoBaseAsset, C = NoCounterAsset, R = NoResolution> {
pub base_asset: B,
pub counter_asset: C,
pub start_time: Option<i64>,
pub end_time: Option<i64>,
pub resolution: R,
pub offset: Option<String>,
pub limit: Option<u8>,
pub order: Option<Order>,
}
impl TradeAggregationsRequest<NoBaseAsset, NoCounterAsset, NoResolution> {
pub fn new() -> Self {
TradeAggregationsRequest {
base_asset: NoBaseAsset,
counter_asset: NoCounterAsset,
resolution: NoResolution,
start_time: None,
end_time: None,
offset: None,
limit: None,
order: None,
}
}
}
impl<B, C, R> TradeAggregationsRequest<B, C, R> {
pub fn set_base_asset(
self,
base_asset: AssetType,
) -> Result<TradeAggregationsRequest<BaseAsset, C, R>, String> {
Ok(TradeAggregationsRequest {
base_asset: BaseAsset(base_asset),
counter_asset: self.counter_asset,
start_time: self.start_time,
end_time: self.end_time,
offset: self.offset,
resolution: self.resolution,
limit: self.limit,
order: self.order,
})
}
pub fn set_counter_asset(
self,
counter_asset: AssetType,
) -> Result<TradeAggregationsRequest<B, CounterAsset, R>, String> {
Ok(TradeAggregationsRequest {
base_asset: self.base_asset,
counter_asset: CounterAsset(counter_asset),
start_time: self.start_time,
end_time: self.end_time,
offset: self.offset,
resolution: self.resolution,
limit: self.limit,
order: self.order,
})
}
pub fn set_resolution(
self,
resolution: Resolution,
) -> Result<TradeAggregationsRequest<B, C, Resolution>, String> {
Ok(TradeAggregationsRequest {
base_asset: self.base_asset,
counter_asset: self.counter_asset,
start_time: self.start_time,
end_time: self.end_time,
offset: self.offset,
resolution,
limit: self.limit,
order: self.order,
})
}
pub fn set_start_time(self, start_time: Option<i64>) -> Result<Self, String> {
Ok(Self { start_time, ..self })
}
pub fn set_end_time(self, end_time: Option<i64>) -> Result<Self, String> {
Ok(Self { end_time, ..self })
}
pub fn set_limit(self, limit: u8) -> Result<Self, String> {
if !(1..=200).contains(&limit) {
Err("Limit must be between 1 and 200.".to_string())
} else {
Ok(Self {
limit: Some(limit),
..self
})
}
}
pub fn set_order(self, order: Order) -> Result<Self, String> {
Ok(Self {
order: Some(order),
..self
})
}
}
impl<B, C> TradeAggregationsRequest<B, C, Resolution> {
pub fn set_offset(self, offset: u64) -> Result<Self, String> {
const ONE_HOUR: &u64 = &360000;
const ONE_DAY: &u64 = &86400000;
let resolution = format!("{}", &self.resolution.0).parse::<u64>().unwrap();
let conditions = [
(
&resolution < ONE_HOUR,
"Resolution must be greater than 1 hour when setting offset.",
),
(&offset % ONE_HOUR != 0, "Offset must be in whole hours."),
(
&offset > &resolution,
"Offset must be smaller than the resolution.",
),
(&offset > ONE_DAY, "Offset must be smaller than 24 hours."),
];
for (condition, message) in conditions {
if condition {
return Err(message.to_string());
}
}
Ok(Self {
offset: Some(offset.to_string()),
..self
})
}
}
impl Request for TradeAggregationsRequest<BaseAsset, CounterAsset, Resolution> {
fn get_query_parameters(&self) -> String {
let asset_parameters = vec![&self.base_asset.0, &self.counter_asset.0]
.iter()
.enumerate()
.fold(Vec::new(), |mut parameters, (i, asset)| {
let asset_type_prefix = if i == 0 {
"base_asset_type="
}
else {
"&counter_asset_type="
};
match asset {
AssetType::Native => parameters.push(format!("{}native", asset_type_prefix)),
AssetType::Alphanumeric4(asset_data)
| AssetType::Alphanumeric12(asset_data) => {
let asset_type = match asset {
AssetType::Alphanumeric4(_) => "credit_alphanum4",
AssetType::Alphanumeric12(_) => "credit_alphanum12",
_ => "", };
let asset_issuer_prefix = if i == 0 {
"&base_asset_issuer="
} else {
"&counter_asset_issuer="
};
let asset_code_prefix = if i == 0 {
"&base_asset_code="
} else {
"&counter_asset_code="
};
parameters.push(format!(
"{}{}{}{}{}{}",
asset_type_prefix,
asset_type,
asset_code_prefix,
asset_data.asset_code,
asset_issuer_prefix,
asset_data.asset_issuer
));
}
}
parameters
})
.join("");
vec![
Some(asset_parameters),
Some(format!("resolution={}", self.resolution.0)),
self.start_time
.as_ref()
.map(|s| format!("start_time={}", s)),
self.end_time.as_ref().map(|e| format!("end_time={}", e)),
self.limit.as_ref().map(|l| format!("limit={}", l)),
self.order.as_ref().map(|o| format!("order={}", o)),
]
.build_query_parameters()
}
fn build_url(&self, base_url: &str) -> String {
format!(
"{}/{}{}",
base_url,
super::TRADE_AGGREGATIONS_PATH,
self.get_query_parameters()
)
}
}