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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#![forbid(unsafe_code)]
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
use std::fmt;
use std::num::ParseIntError;
#[cfg(feature = "chrono_support")]
use chrono::NaiveDateTime;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Dflake {
raw: u64,
pub timestamp: u32,
pub worker_id: u8,
pub process_id: u8,
pub increment: u16,
}
impl Dflake {
pub fn raw(&self) -> u64 {
self.raw
}
#[cfg(feature = "chrono_support")]
pub fn datetime(&self) -> NaiveDateTime {
let seconds = self.timestamp / 1000;
NaiveDateTime::from_timestamp(seconds as i64, 0)
}
}
pub fn parse(input: u64) -> Dflake {
Dflake {
raw: input,
timestamp: ((input >> 22) + 1420070400000) as u32,
worker_id: ((input & 0x3E0000) >> 17) as u8,
process_id: ((input & 0x1F000) >> 12) as u8,
increment: (input & 0xFFF) as u16,
}
}
pub fn parse_str<T: AsRef<str>>(input: T) -> Result<Dflake, ParseError> {
let input = input.as_ref();
if input.chars().any(|ch| ch.is_whitespace()) {
return Err(ParseError::ContainsWhitespace);
}
if input.chars().any(|ch| !ch.is_numeric()) {
return Err(ParseError::InvalidChar);
}
if input.len() > 20 {
return Err(ParseError::TooLarge);
}
let num: u64 = input.parse()?;
Ok(parse(num))
}
#[derive(Debug)]
pub enum ParseError {
TooSmall,
TooLarge,
ContainsWhitespace,
InvalidChar,
ParseIntError(ParseIntError),
}
impl std::error::Error for ParseError {}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ParseError::*;
let message = match self {
TooSmall => "The input is too small".into(),
TooLarge => "The input is too large.".into(),
ContainsWhitespace => "The input contains whitespace.".into(),
InvalidChar => "The input contains an invalid character".into(),
ParseIntError(e) => format!("Failed to parse as an integer: {}", e),
};
write!(f, "{}", message)
}
}
impl From<ParseIntError> for ParseError {
fn from(e: ParseIntError) -> Self {
ParseError::ParseIntError(e)
}
}