sf/
id.rs

1use super::defs::{MAX_SEQ, MAX_TS, MAX_WID, TS_SHIFT, WID_SHIFT};
2use crate::errors::Error;
3use std::fmt;
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6pub struct Id(i64);
7
8impl Id {
9    /// New Id instance, context need to make sure these limits are satisfied.
10    ///
11    /// # Panics
12    ///
13    /// Panics if ts, wid, seq out of limit.
14    pub fn new(ts: u64, wid: u16, seq: u16) -> Self {
15        // general Id should not be nil
16        assert!(ts != 0);
17        // limits
18        assert!(ts <= MAX_TS);
19        assert!(wid <= MAX_WID);
20        assert!(seq <= MAX_SEQ);
21
22        Id((ts << TS_SHIFT) as i64
23            | i64::from(wid << WID_SHIFT)
24            | i64::from(seq))
25    }
26
27    #[cfg(feature = "const_fn")]
28    pub const fn nil() -> Self {
29        Id(0)
30    }
31
32    #[cfg(not(feature = "const_fn"))]
33    pub fn nil() -> Id {
34        Id(0)
35    }
36
37    /// From_i64 require a positive i64 input, or else returns nil.
38    pub fn from_i64(i: i64) -> Self {
39        if i <= 0 {
40            return Id::nil();
41        }
42
43        let u = i as u64;
44        let ts = u >> TS_SHIFT;
45        let wid = ((u >> WID_SHIFT) % (u64::from(MAX_WID) + 1)) as u16;
46        let seq = (u % (u64::from(MAX_SEQ) + 1)) as u16;
47
48        Id::new(ts, wid, seq)
49    }
50
51    pub fn from_slice(b: &[u8]) -> Result<Self, Error> {
52        const BYTES_LEN: usize = 8;
53        let len = b.len();
54
55        if len != BYTES_LEN {
56            return Err(Error::BytesError("bytes too short".to_owned()));
57        }
58
59        let i = i64::from(b[0]) << 56
60            | i64::from(b[1]) << 48
61            | i64::from(b[2]) << 40
62            | i64::from(b[3]) << 32
63            | i64::from(b[4]) << 24
64            | i64::from(b[5]) << 16
65            | i64::from(b[6]) << 8
66            | i64::from(b[7]);
67
68        Ok(Id(i))
69    }
70
71    /// As bytes Big-Endian.
72    pub fn as_bytes(self) -> [u8; 8] {
73        let b1: u8 = ((self.0 >> 56) & 0xff) as u8;
74        let b2: u8 = ((self.0 >> 48) & 0xff) as u8;
75        let b3: u8 = ((self.0 >> 40) & 0xff) as u8;
76        let b4: u8 = ((self.0 >> 32) & 0xff) as u8;
77        let b5: u8 = ((self.0 >> 24) & 0xff) as u8;
78        let b6: u8 = ((self.0 >> 16) & 0xff) as u8;
79        let b7: u8 = ((self.0 >> 8) & 0xff) as u8;
80        let b8: u8 = (self.0 & 0xff) as u8;
81
82        [b1, b2, b3, b4, b5, b6, b7, b8]
83    }
84
85    pub fn is_nil(self) -> bool {
86        self.0 == 0
87    }
88}
89
90impl fmt::LowerHex for Id {
91    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
92        write!(f, "{:x}", self.0)
93    }
94}
95
96impl fmt::UpperHex for Id {
97    #[inline]
98    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99        write!(f, "{:X}", self.0)
100    }
101}
102
103impl Into<i64> for Id {
104    fn into(self) -> i64 {
105        self.0
106    }
107}
108
109impl<'a> Into<i64> for &'a Id {
110    fn into(self) -> i64 {
111        self.0
112    }
113}
114
115impl Into<Id> for i64 {
116    fn into(self) -> Id {
117        Id::from_i64(self)
118    }
119}
120
121impl<'a> Into<Id> for &'a i64 {
122    fn into(self) -> Id {
123        Id::from_i64(*self)
124    }
125}
126
127impl Default for Id {
128    #[inline]
129    fn default() -> Self {
130        Id::nil()
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::Id;
137
138    #[test]
139    #[should_panic]
140    fn test_bad_worker_id() {
141        let _id = Id::new(0, 1024, 0);
142    }
143
144    #[test]
145    fn test_id_nil() {
146        let id = Id::nil();
147
148        assert_eq!(id, 0.into());
149    }
150
151    #[test]
152    fn test_id_from_i64_success() {
153        // general success
154        let i = 18_427_598_719_680_512;
155        let id = Id::from_i64(i);
156
157        assert_eq!(i, id.into());
158
159        // nil success
160        let i = 0;
161        let id = Id::from_i64(i);
162
163        assert_eq!(i, id.into());
164    }
165
166    #[test]
167    #[should_panic]
168    fn test_id_from_i64_fail() {
169        // timestamp is zero
170        let i = 1;
171        let _ = Id::from_i64(i);
172    }
173
174    #[test]
175    fn test_id_from_i64_nil() {
176        // negative number returns nil
177        let i: i64 = -1;
178        let id = Id::from_i64(i);
179
180        assert!(id.is_nil());
181
182        // 0 returns nil
183        let i: i64 = 0;
184        let id = Id::from_i64(i);
185
186        assert!(id.is_nil());
187    }
188}