1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//! Timetoken type.

use std::time::{SystemTime, SystemTimeError};

/// # PubNub Timetoken
///
/// This is the timetoken structure that PubNub uses as a stream index.
/// It allows clients to resume streaming from where they left off for added
/// resiliency.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
pub struct Timetoken {
    /// Timetoken
    pub t: u64,
    /// Origin region
    pub r: u32,
}

impl Timetoken {
    /// Create a `Timetoken`.
    ///
    /// # Arguments
    ///
    /// - `time` - A [`SystemTime`] representing when the message was received
    ///            by the PubNub global network.
    /// - `region` - An internal region identifier for the originating region.
    ///
    /// `region` may be set to `0` if you have nothing better to use. The
    /// combination of a time and region gives us a vector clock that represents
    /// the message origin in spacetime; when and where the message was created.
    /// Using an appropriate `region` is important for delivery semantics in a
    /// global distributed system.
    ///
    /// # Errors
    ///
    /// Returns an error when the input `time` argument cannot be transformed
    /// into a duration.
    ///
    /// # Example
    ///
    /// ```
    /// use pubnub_core::data::timetoken::Timetoken;
    /// use std::time::SystemTime;
    ///
    /// let now = SystemTime::now();
    /// let timetoken = Timetoken::new(now, 0)?;
    /// # Ok::<(), std::time::SystemTimeError>(())
    /// ```
    pub fn new(time: SystemTime, region: u32) -> Result<Self, SystemTimeError> {
        let time = time.duration_since(SystemTime::UNIX_EPOCH)?;
        let secs = time.as_secs();
        let nanos = time.subsec_nanos();

        // Format the timetoken with the appropriate resolution
        let t = (secs * 10_000_000) | (u64::from(nanos) / 100);

        Ok(Self { t, r: region })
    }
}

impl Default for Timetoken {
    #[must_use]
    fn default() -> Self {
        Self {
            t: u64::default(),
            r: u32::default(),
        }
    }
}

impl std::fmt::Display for Timetoken {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        write!(fmt, "{{ t: {}, r: {} }}", self.t, self.r)
    }
}