Skip to main content

twine_codec/dataset/
timestamp.rs

1// Copyright (c) 2025 Jake Swensen
2// SPDX-License-Identifier: MPL-2.0
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8use twine_rs_macros::Tlv;
9
10#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
11pub struct Authoritative(pub bool);
12
13impl Authoritative {
14    pub(crate) fn is_authoritative(&self) -> bool {
15        self.0
16    }
17}
18
19#[derive(Copy, Clone, Default, Eq, PartialEq, Tlv)]
20#[tlv(variants = [("Active", tlv_type = 0x0e), ("Pending", tlv_type = 0x33)], tlv_length = 8, derive_inner)]
21pub struct Timestamp(u64);
22
23impl Timestamp {
24    #[cfg(any(test, feature = "std"))]
25    pub fn now(auth: Authoritative) -> Self {
26        use std::time::{SystemTime, UNIX_EPOCH};
27
28        let now = SystemTime::now();
29        let seconds = now.duration_since(UNIX_EPOCH).unwrap().as_secs();
30
31        Self::from((seconds, 0, auth))
32    }
33
34    pub fn seconds(&self) -> u64 {
35        self.0 >> 16
36    }
37
38    pub fn ticks(&self) -> u16 {
39        ((self.0 >> 1) & 0x7fff) as u16
40    }
41
42    pub fn is_authoritative(&self) -> bool {
43        (self.0 & 0x1) != 0
44    }
45}
46impl core::fmt::Display for Timestamp {
47    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
48        write!(f, "{}", self.seconds(),)
49    }
50}
51
52impl core::fmt::Debug for Timestamp {
53    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54        write!(
55            f,
56            "Timestamp {{ seconds: {}, ticks: {}, authoritative: {} }}",
57            self.seconds(),
58            self.ticks(),
59            self.is_authoritative()
60        )
61    }
62}
63
64impl From<(u64, u16, Authoritative)> for Timestamp {
65    fn from(parts: (u64, u16, Authoritative)) -> Self {
66        let (seconds, ticks, auth) = parts;
67        let seconds = seconds << 16;
68        let ticks = (ticks as u64) & 0xfffe;
69        let auth = if auth.is_authoritative() { 1u64 } else { 0u64 };
70
71        Self(seconds | ticks | auth)
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    use twine_tlv::TryEncodeTlv;
80
81    #[test]
82    fn success_from_parts() {
83        let timestamp = Timestamp::from((0x1234_5678, 0x9abc, Authoritative(true)));
84        assert_eq!(timestamp.0, 0x1234_5678_9abd_u64);
85
86        assert_eq!(timestamp.seconds(), 0x1234_5678);
87        assert_eq!(timestamp.ticks(), 0x9abc >> 1);
88        assert!(timestamp.is_authoritative());
89    }
90
91    #[test]
92    fn success_active_timestamp_to_tlv() {
93        let timestamp = Timestamp::from((0x1234_5678, 0x9abc, Authoritative(true)));
94        let timestamp = ActiveTimestamp::from(timestamp);
95
96        let mut buffer = [0u8; 2 + 8];
97        let written = timestamp.try_encode_tlv(&mut buffer).unwrap();
98
99        assert_eq!(written, 10);
100        assert_eq!(buffer[0], 0x0e); // TLV Type
101        assert_eq!(buffer[1], 0x08); // TLV Length
102        assert_eq!(&buffer[2..], &0x1234_5678_9abd_u64.to_be_bytes()[..]);
103    }
104
105    #[test]
106    fn success_pending_timestamp_to_tlv() {
107        let timestamp = Timestamp::from((0x1234_5678, 0x9abc, Authoritative(true)));
108        let timestamp = PendingTimestamp::from(timestamp);
109
110        let mut buffer = [0u8; 2 + 8];
111        let written = timestamp.try_encode_tlv(&mut buffer).unwrap();
112
113        assert_eq!(written, 10);
114        assert_eq!(buffer[0], 0x33); // TLV Type
115        assert_eq!(buffer[1], 0x08); // TLV Length
116        assert_eq!(&buffer[2..], &0x1234_5678_9abd_u64.to_be_bytes()[..]);
117    }
118}