Skip to main content

webe_id/
lib.rs

1use std::time::{Duration, SystemTime, SystemTimeError};
2
3pub type WebeID = u64;
4
5pub struct WebeIDFactory {
6    epoch: SystemTime,
7    last_duration_ms: u64,
8    node_id: u64,
9    sequence: u16,
10}
11
12#[derive(Debug)]
13pub enum WebeIDError {
14    BadEpoch,  // provided epoch is already out of max time frame from system time
15    BadLastDuration, // provided last time is already out of time frame from epoch
16    SequenceOverflow, // overflowed the 16bit sequence counter
17    SystemTimeError(SystemTimeError),
18    TimeRewind,  // system clock may have drifted backwards
19}
20
21impl From<SystemTimeError> for WebeIDError {
22    fn from(err: SystemTimeError) -> WebeIDError {
23        WebeIDError::SystemTimeError(err)
24    }
25}
26
27impl WebeIDFactory {
28    pub fn new(epoch: SystemTime, node_id: u8) -> Result<WebeIDFactory, WebeIDError> {
29        // check that provided epoch system time isn't already out of range of max time frame
30        SystemTime::now().duration_since(epoch + Duration::from_millis(std::u16::MAX as u64))?;
31
32        Ok(WebeIDFactory {
33            epoch: epoch,
34            last_duration_ms: 0,
35            node_id: (node_id as u64) << 16,
36            sequence: 0,
37        })
38    }
39
40    // 'last_time_ms' = last duration in ms since provided epoch
41    // same as 'new' but can provide last known run time - in case of planned system restarts
42    pub fn new_with_last_time(epoch: SystemTime, last_duration_ms: u64, node_id: u8) -> Result<WebeIDFactory, WebeIDError> {
43        let mut factory = WebeIDFactory::new(epoch, node_id)?;
44        // check that current duration since epoch is greater than last time since epoch
45        if SystemTime::now().duration_since(epoch)?.as_millis() <= last_duration_ms as u128 {
46            return Err(WebeIDError::BadLastDuration);
47        }
48        factory.last_duration_ms = last_duration_ms;
49        return Ok(factory);
50    }
51
52    pub fn next(&mut self) -> Result<WebeID, WebeIDError> {
53        let cur_duration_ms = SystemTime::now().duration_since(self.epoch)?.as_millis() as u64;
54        // for security - verify time has not gone backwards since factory was created.
55        if cur_duration_ms < self.last_duration_ms {return Err(WebeIDError::TimeRewind)}
56        if cur_duration_ms > self.last_duration_ms {self.sequence = 0} // reset sequence
57        self.last_duration_ms = cur_duration_ms;
58        let new_id = ((cur_duration_ms as u64) << 24) | (self.node_id) | (self.sequence as u64);
59        match self.sequence.checked_add(1) {
60            Some(new_sequence) => self.sequence = new_sequence,
61            None => return Err(WebeIDError::SequenceOverflow)
62        }
63        return Ok(new_id);   
64    }
65
66    pub fn epoch(&self) -> SystemTime {
67        return self.epoch;
68    }
69
70    pub fn last_duration_ms(&self) -> u64 {
71        return self.last_duration_ms;
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn test() {
81        let epoch = SystemTime::UNIX_EPOCH
82            .checked_add(Duration::from_millis(1546300800000)) // 01-01-2019 12:00:00 AM GMT
83            .expect("failed to create custom epoch");
84        let mut factory = WebeIDFactory::new(epoch, 0u8).unwrap();
85        let id = factory.next().unwrap();
86        println!("New ID: {:x}", id);
87    }
88}