1use chrono::{DateTime, SubsecRound, Utc};
2use core::{
3 ops::{Add, AddAssign, Sub, SubAssign},
4 time::Duration,
5};
6#[cfg_attr(
7 feature = "serde",
8 derive(::serde::Serialize, ::serde::Deserialize),
9 cfg_attr(feature = "abi", derive(::schemars::JsonSchema))
10)]
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[repr(transparent)]
13pub struct Deadline(
14 #[cfg_attr(all(feature = "serde", feature = "abi"), schemars(with = "String"))] DateTime<Utc>,
15);
16
17impl Deadline {
18 pub const UNIX_EPOCH: Self = Self::new(DateTime::UNIX_EPOCH);
19 pub const MAX: Self = Self::new(DateTime::<Utc>::MAX_UTC);
20
21 pub const fn new(d: DateTime<Utc>) -> Self {
22 Self(d)
23 }
24
25 #[cfg(target_arch = "wasm32")]
26 #[must_use]
27 pub fn now() -> Self {
28 Self(defuse_near_utils::time::now())
29 }
30
31 #[cfg(not(target_arch = "wasm32"))]
32 #[must_use]
33 #[inline]
34 pub fn now() -> Self {
35 Self(Utc::now())
36 }
37
38 #[must_use]
39 #[inline]
40 pub fn timeout(timeout: Duration) -> Self {
41 Self::now() + timeout
42 }
43
44 #[must_use]
45 #[inline]
46 pub fn has_expired(self) -> bool {
47 Self::now() > self
48 }
49
50 #[must_use]
54 #[inline]
55 pub fn trunc_subsecs(self) -> Self {
56 Self::new(self.into_timestamp().trunc_subsecs(0))
57 }
58
59 #[must_use]
60 #[inline]
61 pub const fn into_timestamp(self) -> DateTime<Utc> {
62 self.0
63 }
64}
65
66impl Add<Duration> for Deadline {
67 type Output = Self;
68
69 #[inline]
70 fn add(self, rhs: Duration) -> Self::Output {
71 Self(self.0 + rhs)
72 }
73}
74
75impl Sub<Duration> for Deadline {
76 type Output = Self;
77
78 fn sub(self, rhs: Duration) -> Self::Output {
79 Self(self.0 - rhs)
80 }
81}
82
83impl AddAssign<Duration> for Deadline {
84 #[inline]
85 fn add_assign(&mut self, rhs: Duration) {
86 self.0 += rhs;
87 }
88}
89
90impl SubAssign<Duration> for Deadline {
91 fn sub_assign(&mut self, rhs: Duration) {
92 self.0 -= rhs;
93 }
94}
95
96#[cfg(any(test, feature = "borsh"))]
97const _: () = {
98 use defuse_borsh_utils::adapters::{
99 BorshDeserializeAs, BorshSerializeAs, TimestampMicroSeconds, TimestampMilliSeconds,
100 TimestampNanoSeconds, TimestampSeconds,
101 };
102
103 macro_rules! impl_borsh_serde_as {
104 ($($a:ident,)+) => {
105 const _: () = {
106 $(
107 impl<I> BorshSerializeAs<Deadline> for $a<I>
108 where
109 $a<I>: BorshSerializeAs<DateTime<Utc>>,
110 {
111 fn serialize_as<W>(source: &Deadline, writer: &mut W) -> std::io::Result<()>
112 where
113 W: std::io::Write,
114 {
115 Self::serialize_as(&source.0, writer)
116 }
117 }
118
119 impl<I> BorshDeserializeAs<Deadline> for $a<I>
120 where
121 $a<I>: BorshDeserializeAs<DateTime<Utc>>,
122 {
123 fn deserialize_as<R>(reader: &mut R) -> std::io::Result<Deadline>
124 where
125 R: std::io::Read,
126 {
127 Self::deserialize_as(reader).map(Deadline)
128 }
129 }
130 )*
131 };
132 };
133}
134
135 impl_borsh_serde_as! {
136 TimestampSeconds, TimestampMilliSeconds, TimestampMicroSeconds, TimestampNanoSeconds,
137 }
138};
139
140#[cfg(test)]
141mod tests {
142 #[test]
143 fn schema_as_usage() {
144 use super::*;
145 use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
146 use chrono::TimeZone;
147 use defuse_borsh_utils::adapters::{As, TimestampNanoSeconds};
148
149 #[derive(BorshSerialize, BorshDeserialize, BorshSchema)]
150 struct S {
151 #[borsh(
152 serialize_with = "As::<TimestampNanoSeconds>::serialize",
153 deserialize_with = "As::<TimestampNanoSeconds>::deserialize",
154 schema(with_funcs(
155 declaration = "As::<TimestampNanoSeconds>::declaration",
156 definitions = "As::<TimestampNanoSeconds>::add_definitions_recursively",
157 ))
158 )]
159 pub deadline: Deadline,
160 }
161
162 let val = S {
163 deadline: Deadline::new(Utc.timestamp_opt(1_600_000_000, 123_456_789).unwrap()),
164 };
165 let bytes = borsh::to_vec(&val).unwrap();
166 let decoded = S::try_from_slice(&bytes).unwrap();
167 assert_eq!(val.deadline, decoded.deadline);
168 }
169}