use std::num::NonZeroUsize;
use jiff::{RoundMode, Timestamp, TimestampRound, Unit};
use serde::{Deserialize, Serialize, Serializer};
use super::error::Error;
use crate::common::enums::OhlcvPeriod;
pub const HISTORY_LIMIT_MAX: u32 = 100_000;
#[derive(Debug, Clone, Serialize)]
pub struct HistoryQuery {
#[serde(serialize_with = "serialize_time")]
pub time_start: Timestamp,
#[serde(serialize_with = "serialize_time")]
pub time_end: Timestamp,
pub limit: u32,
}
impl HistoryQuery {
pub(super) fn validate(&self) -> Result<(), Error> {
if provider_time(self.time_start)? >= provider_time(self.time_end)? {
return Err(Error::Validation(
"time_start must precede time_end at CoinAPI's 100 ns resolution".into(),
));
}
if !(1..=HISTORY_LIMIT_MAX).contains(&self.limit) {
return Err(Error::Validation(format!(
"limit must be in [1, {HISTORY_LIMIT_MAX}]"
)));
}
Ok(())
}
}
fn serialize_time<S: Serializer>(time: &Timestamp, serializer: S) -> Result<S::Ok, S::Error> {
let time = provider_time(*time).map_err(serde::ser::Error::custom)?;
serializer.collect_str(&format_args!("{time:.7}"))
}
pub(super) fn provider_time(time: Timestamp) -> Result<Timestamp, Error> {
time.round(
TimestampRound::new()
.smallest(Unit::Nanosecond)
.increment(100)
.mode(RoundMode::Ceil),
)
.map_err(|e| Error::Validation(format!("timestamp out of range: {e}")))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HistoryRange {
pub time_start: Timestamp,
pub time_end: Timestamp,
pub limit: Option<NonZeroUsize>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct ExchangesQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub filter_exchange_id: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SymbolsQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub filter_symbol_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter_asset_id: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct BookQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub limit_levels: Option<u32>,
}
#[derive(Debug, Clone, Serialize)]
pub struct TradesQuery {
#[serde(flatten)]
pub range: HistoryQuery,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_id: Option<bool>,
}
#[derive(Debug, Clone, Serialize)]
pub struct OhlcvQuery {
pub period_id: OhlcvPeriod,
#[serde(flatten)]
pub range: HistoryQuery,
}