-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
53 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,78 +1,96 @@ | ||
use crate::response::ValueResponse; | ||
use serde::{Deserialize, Serialize}; | ||
use std::marker::PhantomData; | ||
use std::time::SystemTime; | ||
use time::format_description::well_known::iso8601::EncodedConfig; | ||
use time::format_description::well_known::Iso8601; | ||
use time::formatting::Formattable; | ||
use time::macros::format_description; | ||
use time::parsing::Parsable; | ||
use time::{format_description, OffsetDateTime}; | ||
|
||
pub(crate) struct TimeParam<const CONFIG: EncodedConfig>(SystemTime); | ||
pub(crate) trait FormatWrapper { | ||
type Format: 'static + ?Sized + Parsable + Formattable; | ||
|
||
// `time` crate doesn't expose config from `Iso8601`, so we have to extract one manually via inference | ||
pub(crate) const fn config_from<const CONFIG: EncodedConfig>(_: Iso8601<CONFIG>) -> EncodedConfig { | ||
CONFIG | ||
const FORMAT: &'static Self::Format; | ||
} | ||
|
||
impl<const CONFIG: EncodedConfig> std::fmt::Debug for TimeParam<CONFIG> { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
self.0.fmt(f) | ||
} | ||
pub(crate) struct Iso8601; | ||
|
||
impl FormatWrapper for Iso8601 { | ||
type Format = format_description::well_known::Iso8601; | ||
|
||
const FORMAT: &'static Self::Format = &Self::Format::DEFAULT; | ||
} | ||
|
||
pub(crate) struct Fits; | ||
|
||
impl FormatWrapper for Fits { | ||
type Format = [format_description::BorrowedFormatItem<'static>]; | ||
|
||
const FORMAT: &'static Self::Format = format_description!( | ||
"[year]-[month]-[day]T[hour]:[minute]:[second][optional [.[subsecond digits:3]]]" | ||
); | ||
} | ||
|
||
impl<const CONFIG: EncodedConfig> From<SystemTime> for TimeParam<CONFIG> { | ||
#[derive(Debug)] | ||
pub(crate) struct TimeParam<F>(OffsetDateTime, PhantomData<F>); | ||
|
||
impl<F> From<SystemTime> for TimeParam<F> { | ||
fn from(value: SystemTime) -> Self { | ||
Self(value) | ||
Self(value.into(), PhantomData) | ||
} | ||
} | ||
|
||
impl<const CONFIG: EncodedConfig> From<TimeParam<CONFIG>> for SystemTime { | ||
fn from(wrapper: TimeParam<CONFIG>) -> Self { | ||
wrapper.0 | ||
impl<F> From<TimeParam<F>> for SystemTime { | ||
fn from(wrapper: TimeParam<F>) -> Self { | ||
wrapper.0.into() | ||
} | ||
} | ||
|
||
impl<const CONFIG: EncodedConfig> Serialize for TimeParam<CONFIG> { | ||
impl<F: FormatWrapper> Serialize for TimeParam<F> { | ||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { | ||
time::OffsetDateTime::from(self.0) | ||
.format(&Iso8601::<CONFIG>) | ||
self.0 | ||
.format(&F::FORMAT) | ||
.map_err(serde::ser::Error::custom)? | ||
.serialize(serializer) | ||
} | ||
} | ||
|
||
impl<'de, const CONFIG: EncodedConfig> Deserialize<'de> for TimeParam<CONFIG> { | ||
impl<'de, F: FormatWrapper> Deserialize<'de> for TimeParam<F> { | ||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { | ||
struct Visitor<const CONFIG: EncodedConfig>; | ||
struct Visitor<F>(PhantomData<F>); | ||
|
||
impl<const CONFIG: EncodedConfig> serde::de::Visitor<'_> for Visitor<CONFIG> { | ||
type Value = TimeParam<{ CONFIG }>; | ||
impl<F: FormatWrapper> serde::de::Visitor<'_> for Visitor<F> { | ||
type Value = TimeParam<F>; | ||
|
||
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
formatter.write_str("a date string") | ||
} | ||
|
||
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> { | ||
match time::OffsetDateTime::parse(value, &Iso8601::<CONFIG>) { | ||
Ok(value) => Ok(TimeParam(value.into())), | ||
match time::PrimitiveDateTime::parse(value, &F::FORMAT) { | ||
Ok(value) => Ok(TimeParam(value.assume_utc(), PhantomData)), | ||
Err(err) => Err(serde::de::Error::custom(err)), | ||
} | ||
} | ||
} | ||
|
||
deserializer.deserialize_str(Visitor) | ||
deserializer.deserialize_str(Visitor(PhantomData)) | ||
} | ||
} | ||
|
||
#[derive(Debug, Serialize, Deserialize)] | ||
#[serde(bound = "F: FormatWrapper")] | ||
#[serde(transparent)] | ||
pub(crate) struct TimeResponse<const CONFIG: EncodedConfig>(ValueResponse<TimeParam<CONFIG>>); | ||
pub(crate) struct TimeResponse<F>(ValueResponse<TimeParam<F>>); | ||
|
||
impl<const CONFIG: EncodedConfig> From<SystemTime> for TimeResponse<CONFIG> { | ||
impl<F> From<SystemTime> for TimeResponse<F> { | ||
fn from(value: SystemTime) -> Self { | ||
Self(TimeParam::from(value).into()) | ||
} | ||
} | ||
|
||
impl<const CONFIG: EncodedConfig> From<TimeResponse<CONFIG>> for SystemTime { | ||
fn from(wrapper: TimeResponse<CONFIG>) -> Self { | ||
impl<F> From<TimeResponse<F>> for SystemTime { | ||
fn from(wrapper: TimeResponse<F>) -> Self { | ||
wrapper.0.into().into() | ||
} | ||
} |