Skip to content

Commit

Permalink
MOD: Deprecate start_date and end_date in response
Browse files Browse the repository at this point in the history
  • Loading branch information
nmacholl committed May 14, 2024
1 parent cce535f commit 67a1f4a
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 35 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 0.9.0 - TBD

#### Enhancements
- Added `start` and `end` fields to the `DatasetRange` struct which provide time resolution and an exclusive end date

#### Deprecations
- The `start_date` and `end_date` fields of the `DatasetRange` struct are deprecated and will be removed in a future release

## 0.8.0 - 2024-04-01

#### Enhancements
Expand Down
30 changes: 30 additions & 0 deletions src/deserialize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! Custom deserializers
use serde::Deserialize;
use time::format_description::well_known::iso8601::Iso8601;

const LEGACY_DATE_TIME_FORMAT: &[time::format_description::BorrowedFormatItem<'static>] =
time::macros::format_description!("[year]-[month]-[day] [hour]:[minute]:[second][optional [.[subsecond digits:6]]][optional [+[offset_hour]:[offset_minute]]]");

pub(crate) fn deserialize_date_time<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<time::OffsetDateTime, D::Error> {
let dt_str = String::deserialize(deserializer)?;
time::PrimitiveDateTime::parse(&dt_str, &Iso8601::DEFAULT)
.map(|dt| dt.assume_utc())
.or_else(|_| time::OffsetDateTime::parse(&dt_str, LEGACY_DATE_TIME_FORMAT))
.map_err(serde::de::Error::custom)
}

pub(crate) fn deserialize_opt_date_time<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Option<time::OffsetDateTime>, D::Error> {
if let Some(dt_str) = Option::<String>::deserialize(deserializer)? {
time::PrimitiveDateTime::parse(&dt_str, &Iso8601::DEFAULT)
.map(|dt| dt.assume_utc())
.or_else(|_| time::OffsetDateTime::parse(&dt_str, LEGACY_DATE_TIME_FORMAT))
.map(Some)
.map_err(serde::de::Error::custom)
} else {
Ok(None)
}
}
31 changes: 1 addition & 30 deletions src/historical/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use time::OffsetDateTime;
use tokio::io::BufWriter;
use typed_builder::TypedBuilder;

use crate::deserialize::{deserialize_date_time, deserialize_opt_date_time};
use crate::{historical::check_http_error, Error, Symbols};

use super::{handle_response, DateTimeRange};
Expand Down Expand Up @@ -424,36 +425,6 @@ pub struct DownloadParams {
pub filename_to_download: Option<String>,
}

const LEGACY_DATE_TIME_FORMAT: &[time::format_description::BorrowedFormatItem<'static>] =
time::macros::format_description!("[year]-[month]-[day] [hour]:[minute]:[second][optional [.[subsecond digits:6]]][offset_hour]:[offset_minute]");
const DATE_TIME_FORMAT: &[time::format_description::BorrowedFormatItem<'static>] = time::macros::format_description!(
"[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:9]Z"
);

fn deserialize_date_time<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<time::OffsetDateTime, D::Error> {
let dt_str = String::deserialize(deserializer)?;
time::PrimitiveDateTime::parse(&dt_str, DATE_TIME_FORMAT)
.map(|dt| dt.assume_utc())
.or_else(|_| time::OffsetDateTime::parse(&dt_str, LEGACY_DATE_TIME_FORMAT))
.map_err(serde::de::Error::custom)
}

fn deserialize_opt_date_time<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Option<time::OffsetDateTime>, D::Error> {
if let Some(dt_str) = Option::<String>::deserialize(deserializer)? {
time::PrimitiveDateTime::parse(&dt_str, DATE_TIME_FORMAT)
.map(|dt| dt.assume_utc())
.or_else(|_| time::OffsetDateTime::parse(&dt_str, LEGACY_DATE_TIME_FORMAT))
.map(Some)
.map_err(serde::de::Error::custom)
} else {
Ok(None)
}
}

impl SplitDuration {
/// Converts the enum to its `str` representation.
pub const fn as_str(&self) -> &'static str {
Expand Down
73 changes: 68 additions & 5 deletions src/historical/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use std::{collections::HashMap, num::NonZeroU64, str::FromStr};

use crate::deserialize::deserialize_date_time;
use dbn::{Encoding, SType, Schema};
use reqwest::RequestBuilder;
use serde::{Deserialize, Deserializer};
Expand Down Expand Up @@ -272,16 +273,44 @@ pub struct DatasetConditionDetail {
}

/// The available range for a dataset.
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DatasetRange {
/// The start of the available range.
pub start: time::OffsetDateTime,
/// The start date of the available range.
#[serde(deserialize_with = "deserialize_date")]
#[deprecated(since = "0.9.0", note = "Use `start` instead.")]
pub start_date: time::Date,
/// The end date of the available range.
#[serde(deserialize_with = "deserialize_date")]
/// The end of the available range (exclusive).
pub end: time::OffsetDateTime,
/// The end date of the available range (inclusive).
#[deprecated(since = "0.9.0", note = "Use `end` instead.")]
pub end_date: time::Date,
}

#[allow(deprecated)]
impl<'de> Deserialize<'de> for DatasetRange {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Helper {
#[serde(deserialize_with = "deserialize_date_time")]
start: time::OffsetDateTime,
#[serde(deserialize_with = "deserialize_date_time")]
end: time::OffsetDateTime,
}
let partial = Helper::deserialize(deserializer)?;

Ok(DatasetRange {
start: partial.start,
start_date: partial.start.date(),
end: partial.end,
end_date: (partial.end - time::Duration::days(1)).date(),
})
}
}

/// The parameters for several metadata requests.
#[derive(Debug, Clone, TypedBuilder, PartialEq, Eq)]
pub struct GetQueryParams {
Expand Down Expand Up @@ -425,7 +454,7 @@ impl AddToQuery<GetQueryParams> for reqwest::RequestBuilder {
mod tests {
use reqwest::StatusCode;
use serde_json::json;
use time::macros::date;
use time::macros::{date, datetime};
use wiremock::{
matchers::{basic_auth, method, path, query_param},
Mock, MockServer, ResponseTemplate,
Expand Down Expand Up @@ -633,7 +662,10 @@ mod tests {
.and(query_param("dataset", DATASET))
.respond_with(
ResponseTemplate::new(StatusCode::OK.as_u16()).set_body_json(json!({
"start": "2019-07-07T00:00:00.000000000Z",
"start_date": "2019-07-07",
// test both time formats
"end": "2023-07-20T00:00:00.000000000Z",
"end_date": "2023-07-19",
})),
)
Expand All @@ -646,7 +678,38 @@ mod tests {
)
.unwrap();
let range = target.metadata().get_dataset_range(DATASET).await.unwrap();
assert_eq!(range.start, datetime!(2019 - 07 - 07 00:00:00+00:00));
assert_eq!(range.start_date, date!(2019 - 07 - 07));
assert_eq!(range.end, datetime!(2023 - 07 - 20 00:00:00.000000+00:00));
assert_eq!(range.end_date, date!(2023 - 07 - 19));
}

#[tokio::test]
async fn test_get_dataset_range_no_dates() {
const DATASET: &str = "XNAS.ITCH";
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.and(basic_auth(API_KEY, ""))
.and(path(format!("/v{API_VERSION}/metadata.get_dataset_range")))
.and(query_param("dataset", DATASET))
.respond_with(
ResponseTemplate::new(StatusCode::OK.as_u16()).set_body_json(json!({
"start": "2019-07-07T00:00:00.000000000Z",
"end": "2023-07-20T00:00:00.000000000Z",
})),
)
.mount(&mock_server)
.await;
let mut target = HistoricalClient::with_url(
mock_server.uri(),
API_KEY.to_owned(),
HistoricalGateway::Bo1,
)
.unwrap();
let range = target.metadata().get_dataset_range(DATASET).await.unwrap();
assert_eq!(range.start, datetime!(2019 - 07 - 07 00:00:00+00:00));
assert_eq!(range.start_date, date!(2019 - 07 - 07));
assert_eq!(range.end, datetime!(2023 - 07 - 20 00:00:00.000000+00:00));
assert_eq!(range.end_date, date!(2023 - 07 - 19));
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(clippy::missing_errors_doc)]

mod deserialize;
pub mod error;
#[cfg(feature = "historical")]
pub mod historical;
Expand Down

0 comments on commit 67a1f4a

Please sign in to comment.