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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#[allow(unused_imports)]
use crate::float_polyfill::FloatPolyfill;

/// A log2 representation of seconds used to describe the pacing of events in
/// PTP
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Interval(i8);

impl core::fmt::Debug for Interval {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("Interval")
            .field("seconds", &self.as_f64())
            .field("log_base_2", &self.0)
            .finish()
    }
}

impl Interval {
    /// An Interval of one second
    pub const ONE_SECOND: Self = Self(0);

    /// An Interval of two seconds
    pub const TWO_SECONDS: Self = Self(1);

    /// Construct an [`Interval`] from log2 seconds.
    ///
    /// # Example
    /// ```
    /// # use std::time::Duration;
    /// # use statime::time::Interval;
    /// assert_eq!(Interval::from_log_2(2).as_core_duration(), Duration::from_secs(4));
    /// assert_eq!(Interval::from_log_2(-2).as_core_duration(), Duration::from_millis(250));
    /// ```
    pub const fn from_log_2(log_2: i8) -> Self {
        Self(log_2)
    }

    /// Turn `self` into a number of seconds as [`f64`]
    ///
    /// # Example
    /// ```
    /// # use statime::time::Interval;
    /// assert_eq!(Interval::from_log_2(1).seconds(), 2.0);
    /// assert_eq!(Interval::from_log_2(-1).seconds(), 0.5);
    /// ```
    pub fn seconds(self) -> f64 {
        self.as_f64()
    }

    /// Turn this into a [`statime::time::Duration`](`crate::time::Duration`)
    ///
    /// # Example
    /// ```
    /// # use statime::time::{Duration, Interval};
    /// assert_eq!(Interval::from_log_2(3).as_duration(), Duration::from_secs(8));
    /// assert_eq!(Interval::from_log_2(-3).as_duration(), Duration::from_millis(125));
    /// ```
    pub fn as_duration(self) -> super::Duration {
        super::Duration::from_interval(self)
    }

    /// Turn this into a [`core::time::Duration`]
    ///
    /// # Example
    /// ```
    /// # use statime::time::{Interval};
    /// use core::time::Duration;
    /// assert_eq!(Interval::from_log_2(3).as_core_duration(), Duration::from_secs(8));
    /// assert_eq!(Interval::from_log_2(-3).as_core_duration(), Duration::from_millis(125));
    /// ```
    pub fn as_core_duration(self) -> core::time::Duration {
        core::time::Duration::from_secs_f64(self.seconds())
    }

    fn as_f64(self) -> f64 {
        2.0f64.powi(self.0 as i32)
    }

    /// Get the log2 of the numbers of seconds of this [`Interval`]
    ///
    /// # Example
    /// ```
    /// # use statime::time::{Interval};
    /// use core::time::Duration;
    /// assert_eq!(Interval::ONE_SECOND.as_log_2(), 0);
    /// assert_eq!(Interval::TWO_SECONDS.as_log_2(), 1);
    /// ```
    pub fn as_log_2(self) -> i8 {
        self.0
    }
}

impl From<i8> for Interval {
    fn from(value: i8) -> Self {
        Self::from_log_2(value)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn two() {
        assert_eq!(Interval::TWO_SECONDS.as_f64(), 2.0f64)
    }
}