nym_api_requests/models/
schema_helpers.rs

1// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::helpers::unix_epoch;
5use cosmwasm_std::Uint128;
6use schemars::schema::{InstanceType, Schema, SchemaObject};
7use schemars::{JsonSchema, SchemaGenerator};
8use serde::{Deserialize, Deserializer, Serialize};
9use std::fmt;
10use std::fmt::{Display, Formatter};
11use std::ops::{Deref, DerefMut};
12use time::OffsetDateTime;
13use utoipa::ToSchema;
14
15#[derive(ToSchema)]
16#[schema(title = "Coin")]
17pub struct CoinSchema {
18    pub denom: String,
19    #[schema(value_type = String)]
20    pub amount: Uint128,
21}
22
23pub fn de_rfc3339_or_default<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
24where
25    D: Deserializer<'de>,
26{
27    Ok(time::serde::rfc3339::deserialize(deserializer).unwrap_or_else(|_| unix_epoch()))
28}
29
30// for all intents and purposes it's just OffsetDateTime, but we need JsonSchema...
31#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, ToSchema)]
32pub struct OffsetDateTimeJsonSchemaWrapper(
33    #[serde(
34        default = "unix_epoch",
35        with = "crate::helpers::overengineered_offset_date_time_serde"
36    )]
37    #[schema(inline)]
38    pub OffsetDateTime,
39);
40
41impl Default for OffsetDateTimeJsonSchemaWrapper {
42    fn default() -> Self {
43        OffsetDateTimeJsonSchemaWrapper(unix_epoch())
44    }
45}
46
47impl Display for OffsetDateTimeJsonSchemaWrapper {
48    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
49        Display::fmt(&self.0, f)
50    }
51}
52
53impl From<OffsetDateTimeJsonSchemaWrapper> for OffsetDateTime {
54    fn from(value: OffsetDateTimeJsonSchemaWrapper) -> Self {
55        value.0
56    }
57}
58
59impl From<OffsetDateTime> for OffsetDateTimeJsonSchemaWrapper {
60    fn from(value: OffsetDateTime) -> Self {
61        OffsetDateTimeJsonSchemaWrapper(value)
62    }
63}
64
65impl Deref for OffsetDateTimeJsonSchemaWrapper {
66    type Target = OffsetDateTime;
67
68    fn deref(&self) -> &Self::Target {
69        &self.0
70    }
71}
72
73impl DerefMut for OffsetDateTimeJsonSchemaWrapper {
74    fn deref_mut(&mut self) -> &mut Self::Target {
75        &mut self.0
76    }
77}
78
79// implementation taken from: https://github.com/GREsau/schemars/pull/207
80impl JsonSchema for OffsetDateTimeJsonSchemaWrapper {
81    fn is_referenceable() -> bool {
82        false
83    }
84
85    fn schema_name() -> String {
86        "DateTime".into()
87    }
88
89    fn json_schema(_: &mut SchemaGenerator) -> Schema {
90        SchemaObject {
91            instance_type: Some(InstanceType::String.into()),
92            format: Some("date-time".into()),
93            ..Default::default()
94        }
95        .into()
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use std::time::Duration;
103
104    #[test]
105    fn offset_date_time_json_schema_wrapper_serde_backwards_compat() {
106        let mut dummy = OffsetDateTimeJsonSchemaWrapper::default();
107        dummy.0 += Duration::from_millis(1);
108        let ser = serde_json::to_string(&dummy).unwrap();
109
110        assert_eq!("\"1970-01-01 00:00:00.001 +00:00:00\"", ser);
111
112        let human_readable = "\"2024-05-23 07:41:02.756283766 +00:00:00\"";
113        let rfc3339 = "\"2002-10-02T15:00:00Z\"";
114        let rfc3339_offset = "\"2002-10-02T10:00:00-05:00\"";
115
116        let de = serde_json::from_str::<OffsetDateTimeJsonSchemaWrapper>(human_readable).unwrap();
117        assert_eq!(de.0.unix_timestamp(), 1716450062);
118
119        let de = serde_json::from_str::<OffsetDateTimeJsonSchemaWrapper>(rfc3339).unwrap();
120        assert_eq!(de.0.unix_timestamp(), 1033570800);
121
122        let de = serde_json::from_str::<OffsetDateTimeJsonSchemaWrapper>(rfc3339_offset).unwrap();
123        assert_eq!(de.0.unix_timestamp(), 1033570800);
124
125        let de = serde_json::from_str::<OffsetDateTimeJsonSchemaWrapper>("\"nonsense\"").unwrap();
126        assert_eq!(de.0.unix_timestamp(), 0);
127    }
128}