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