Files
nym/nym-api/nym-api-requests/src/helpers.rs

109 lines
4.0 KiB
Rust

// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use time::OffsetDateTime;
// just to have something, even if not accurate to generate the swagger docs
#[derive(JsonSchema)]
pub struct PlaceholderJsonSchemaImpl {}
pub(crate) const fn unix_epoch() -> OffsetDateTime {
OffsetDateTime::UNIX_EPOCH
}
pub(crate) mod overengineered_offset_date_time_serde {
use crate::helpers::unix_epoch;
use nym_serde_helpers::date::DATE_FORMAT;
use serde::de::Visitor;
use serde::ser::Error;
use serde::{Deserializer, Serialize, Serializer};
use std::fmt::Formatter;
use time::format_description::well_known::Rfc3339;
use time::format_description::{modifier, BorrowedFormatItem, Component};
use time::OffsetDateTime;
struct OffsetDateTimeVisitor;
// copied from time library because they keep it private -.-
const DEFAULT_OFFSET_DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
BorrowedFormatItem::Compound(DATE_FORMAT),
BorrowedFormatItem::Literal(b" "),
BorrowedFormatItem::Compound(TIME_FORMAT),
BorrowedFormatItem::Literal(b" "),
BorrowedFormatItem::Compound(UTC_OFFSET_FORMAT),
];
const TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
BorrowedFormatItem::Component(Component::Hour(modifier::Hour::default())),
BorrowedFormatItem::Literal(b":"),
BorrowedFormatItem::Component(Component::Minute(modifier::Minute::default())),
BorrowedFormatItem::Literal(b":"),
BorrowedFormatItem::Component(Component::Second(modifier::Second::default())),
BorrowedFormatItem::Literal(b"."),
BorrowedFormatItem::Component(Component::Subsecond(modifier::Subsecond::default())),
];
const UTC_OFFSET_FORMAT: &[BorrowedFormatItem<'_>] = &[
BorrowedFormatItem::Component(Component::OffsetHour({
let mut m = modifier::OffsetHour::default();
m.sign_is_mandatory = true;
m
})),
BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[
BorrowedFormatItem::Literal(b":"),
BorrowedFormatItem::Component(Component::OffsetMinute(
modifier::OffsetMinute::default(),
)),
BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[
BorrowedFormatItem::Literal(b":"),
BorrowedFormatItem::Component(Component::OffsetSecond(
modifier::OffsetSecond::default(),
)),
])),
])),
];
impl<'de> Visitor<'de> for OffsetDateTimeVisitor {
type Value = OffsetDateTime;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("an rfc3339 or human-readable `OffsetDateTime`")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
// first try rfc3339, if that fails use default human-readable impl from time,
// finally fallback to default unix epoch
Ok(OffsetDateTime::parse(v, &Rfc3339).unwrap_or_else(|_| {
OffsetDateTime::parse(v, &DEFAULT_OFFSET_DATE_TIME_FORMAT)
.unwrap_or_else(|_| unix_epoch())
}))
}
}
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(OffsetDateTimeVisitor)
}
pub(crate) fn serialize<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// serialize it with human-readable format for compatibility with eclipse and nutella clients
// in the future change it back to rfc3339
datetime
.format(&DEFAULT_OFFSET_DATE_TIME_FORMAT)
.map_err(S::Error::custom)?
.serialize(serializer)
}
}
// reimport the module to not break existing imports
pub(crate) use nym_serde_helpers::date as date_serde;