1use core::time::Duration;
6use mls_rs_codec::{MlsDecode, MlsEncode, MlsSize};
7
8#[cfg(target_arch = "wasm32")]
9use wasm_bindgen::prelude::*;
10
11#[cfg_attr(all(feature = "ffi", not(test)), safer_ffi_gen::ffi_type)]
19#[derive(
20 Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, MlsSize, MlsEncode, MlsDecode,
21)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
24#[repr(transparent)]
25pub struct MlsTime {
26 seconds: u64,
27}
28
29impl MlsTime {
30 pub fn from_duration_since_epoch(duration: Duration) -> MlsTime {
32 Self::from(duration)
33 }
34
35 pub fn seconds_since_epoch(&self) -> u64 {
37 self.seconds
38 }
39}
40
41impl core::ops::Sub<MlsTime> for MlsTime {
42 type Output = Duration;
43
44 fn sub(self, rhs: Self) -> Duration {
45 Duration::from_secs(self.seconds - rhs.seconds)
46 }
47}
48
49impl core::ops::Sub<Duration> for MlsTime {
50 type Output = MlsTime;
51
52 fn sub(self, rhs: Duration) -> MlsTime {
53 MlsTime::from(self.seconds - rhs.as_secs())
54 }
55}
56
57impl core::ops::Add<Duration> for MlsTime {
58 type Output = MlsTime;
59
60 fn add(self, rhs: Duration) -> MlsTime {
61 MlsTime::from(self.seconds + rhs.as_secs())
62 }
63}
64
65#[cfg(all(not(target_arch = "wasm32"), feature = "std"))]
66impl MlsTime {
67 pub fn now() -> Self {
69 Self {
70 seconds: std::time::SystemTime::now()
71 .duration_since(std::time::SystemTime::UNIX_EPOCH)
72 .unwrap_or_default()
73 .as_secs(),
74 }
75 }
76}
77
78impl From<u64> for MlsTime {
79 fn from(value: u64) -> Self {
80 Self { seconds: value }
81 }
82}
83
84impl From<Duration> for MlsTime {
85 fn from(value: Duration) -> MlsTime {
86 Self {
87 seconds: value.as_secs(),
88 }
89 }
90}
91
92impl From<MlsTime> for Duration {
93 fn from(value: MlsTime) -> Duration {
94 Duration::from_secs(value.seconds)
95 }
96}
97
98#[cfg(all(not(target_arch = "wasm32"), feature = "std"))]
99#[derive(Debug, thiserror::Error)]
100#[error("Overflow while adding {0:?}")]
101pub struct TimeOverflow(Duration);
103
104#[cfg(all(not(target_arch = "wasm32"), feature = "std"))]
105impl TryFrom<MlsTime> for std::time::SystemTime {
106 type Error = TimeOverflow;
107
108 fn try_from(value: MlsTime) -> Result<std::time::SystemTime, Self::Error> {
109 let duration = Duration::from(value);
110 std::time::SystemTime::UNIX_EPOCH
111 .checked_add(duration)
112 .ok_or(TimeOverflow(duration))
113 }
114}
115
116#[cfg(all(not(target_arch = "wasm32"), feature = "std"))]
117impl TryFrom<std::time::SystemTime> for MlsTime {
118 type Error = std::time::SystemTimeError;
119
120 fn try_from(value: std::time::SystemTime) -> Result<MlsTime, Self::Error> {
121 let duration = value.duration_since(std::time::SystemTime::UNIX_EPOCH)?;
122 Ok(MlsTime::from(duration))
123 }
124}
125
126#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
127#[wasm_bindgen(inline_js = r#"
128export function date_now() {
129 return Date.now();
130}"#)]
131extern "C" {
132 fn date_now() -> f64;
133}
134
135#[cfg(all(target_arch = "wasm32", target_os = "emscripten"))]
136extern "C" {
137 #[link_name = "emscripten_date_now"]
138 fn date_now() -> f64;
139}
140
141#[cfg(target_arch = "wasm32")]
142impl MlsTime {
143 pub fn now() -> Self {
144 Self {
145 seconds: (unsafe { date_now() } / 1000.0) as u64,
146 }
147 }
148}