hyperid/
lib.rs

1//! Superfast id generator
2//!
3//! # Example
4//!
5//! ```
6//! use hyperid::HyperId;
7//!
8//! let mut hyperid = HyperId::default();
9//!
10//! let id1 = hyperid.generate();
11//! let id2 = hyperid.generate();
12//!
13//! assert_ne!(id1, id2);
14//! #[cfg(feature = "url_safe")]
15//! println!("{}", id1.to_url_safe()); // prints "100300792492935192884946730361868995562-15"
16//! ```
17
18#![cfg_attr(docsrs, feature(doc_cfg))]
19// Import uuid
20use uuid::Uuid;
21
22/// Id generator. Every instance create different generator.
23/// ```
24/// use hyperid::HyperId;
25/// let mut hyperid = HyperId::default();
26///
27/// let id = hyperid.generate();
28/// let id2 = hyperid.generate();
29///
30/// assert_ne!(id, id2);
31///
32/// let id = hyperid.get();
33/// let id2 = hyperid.get();
34///
35/// assert_eq!(id, id2);
36/// ```
37pub struct HyperId {
38    uuid: Uuid,
39    c: u8,
40}
41
42impl HyperId {
43    /// Create a new HyperId instance
44    /// ```
45    /// use hyperid::HyperId;
46    /// let mut hyperid = HyperId::new();
47    /// ```
48    pub fn new() -> Self {
49        let uuid = Uuid::new_v4();
50        let c: u8 = 0;
51
52        Self { uuid, c }
53    }
54
55    /// Create a new HyperId instance starting from a given id
56    /// ```
57    /// use hyperid::{HyperId, Id};
58    /// let bytes = vec![0; 17];
59    /// let id = Id::from_bytes(bytes).unwrap();
60    /// let hyperid = HyperId::from_id(id);
61    /// let id = hyperid.get();
62    /// assert_eq!(vec![0; 17], id.into_bytes());
63    /// ```
64    pub fn from_id(id: Id) -> Self {
65        let uuid = id.uuid_as_128;
66        let uuid = Uuid::from_u128(uuid);
67        let c = id.c;
68
69        Self { uuid, c }
70    }
71
72    /// Return the latest generated Id
73    /// ```
74    /// use hyperid::HyperId;
75    /// let mut hyperid = HyperId::new();
76    /// let id1 = hyperid.get();
77    /// let id2 = hyperid.get();
78    /// assert_eq!(id1, id2);
79    /// ```
80    pub fn get(&self) -> Id {
81        Id {
82            uuid_as_128: self.uuid.as_u128(),
83            c: self.c,
84        }
85    }
86
87    /// Generate the Id and returns it
88    /// ```
89    /// use hyperid::HyperId;
90    /// let mut hyperid = HyperId::new();
91    /// let id1 = hyperid.get();
92    /// let id2 = hyperid.generate();
93    /// assert_ne!(id1, id2);
94    /// ```
95    pub fn generate(&mut self) -> Id {
96        self.c = self.c.checked_add(1).unwrap_or(0);
97        if self.c == 0 {
98            self.uuid = Uuid::new_v4();
99        }
100
101        Id {
102            uuid_as_128: self.uuid.as_u128(),
103            c: self.c,
104        }
105    }
106}
107
108impl Default for HyperId {
109    fn default() -> Self {
110        Self::new()
111    }
112}
113/// Structure for keeping data
114#[derive(PartialEq, Debug, Clone, Copy, Eq, Hash)]
115pub struct Id {
116    uuid_as_128: u128,
117    c: u8,
118}
119
120impl Id {
121    /// Return a bytes representation of id
122    /// ```
123    /// use hyperid::HyperId;
124    /// let mut hyperid = HyperId::new();
125    /// let id = hyperid.get();
126    /// println!("{:?}", id.into_bytes());
127    /// ```
128    pub fn into_bytes(self) -> Vec<u8> {
129        let uuid_as_128 = self.uuid_as_128;
130        let mut bytes = uuid_as_128.to_be_bytes().to_vec();
131        bytes.push(self.c);
132        bytes
133    }
134
135    /// Build Id instance from bytes
136    /// ```
137    /// use hyperid::{HyperId, Id};
138    /// let bytes = vec![0; 17];
139    /// let id = Id::from_bytes(bytes).unwrap();
140    /// assert_eq!(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], id.into_bytes());
141    /// ```
142    pub fn from_bytes(mut bytes: Vec<u8>) -> Result<Self, ParseIdError> {
143        if bytes.len() != 17 {
144            return Err(ParseIdError::WrongByteSize);
145        }
146        let c = bytes.pop().unwrap();
147        let mut arr = [0u8; 16];
148        bytes.swap_with_slice(&mut arr);
149        Ok(Self {
150            uuid_as_128: u128::from_be_bytes(arr),
151            c,
152        })
153    }
154
155    /// Return an url safe string
156    /// ```
157    /// use hyperid::HyperId;
158    /// let mut hyperid = HyperId::new();
159    /// let id = hyperid.get();
160    /// println!("{}", id.to_url_safe()); // 3ZAYYJilG7vHTqiUuaQdFg.0
161    /// ```
162    #[cfg_attr(docsrs, doc(cfg(feature = "url_safe")))]
163    #[cfg(feature = "url_safe")]
164    pub fn to_url_safe(&self) -> String {
165        let uuid_as_bytes = self.uuid_as_128.to_le_bytes();
166        let str = base64::encode_config(uuid_as_bytes, base64::URL_SAFE_NO_PAD);
167        format!("{}.{}", str, self.c)
168    }
169
170    /// Return an url safe string
171    /// ```
172    /// use hyperid::{HyperId, Id};
173    /// let mut hyperid = HyperId::new();
174    /// let id1 = hyperid.get();
175    /// let s = id1.to_url_safe();
176    /// let id2 = Id::from_url_safe(s).unwrap();
177    /// assert_eq!(id1, id2);
178    /// ```
179    #[cfg_attr(docsrs, doc(cfg(feature = "url_safe")))]
180    #[cfg(feature = "url_safe")]
181    pub fn from_url_safe(s: String) -> Result<Id, ParseIdError> {
182        let mut split = s.split('.');
183        let uuid_as_128 = split
184            .next()
185            .ok_or(ParseIdError::NoBaseFound)
186            .and_then(|uuid_as_128| {
187                base64::decode_config(uuid_as_128, base64::URL_SAFE_NO_PAD)
188                    .or(Err(ParseIdError::NoBaseFound))
189                    .map(|mut v| {
190                        let mut arr = [0u8; 16];
191                        v.swap_with_slice(&mut arr);
192                        arr
193                    })
194                    .map(u128::from_le_bytes)
195            });
196        let c = split
197            .next()
198            .ok_or(ParseIdError::NoCounterFound)
199            .and_then(|c| c.parse::<u8>().map_err(|_| ParseIdError::NoCounterFound));
200
201        match (uuid_as_128, c) {
202            (Ok(uuid_as_128), Ok(c)) => Ok(Id { uuid_as_128, c }),
203            (Err(err), _) | (_, Err(err)) => Err(err),
204        }
205    }
206}
207
208#[derive(Debug)]
209pub enum ParseIdError {
210    NoBaseFound,
211    NoCounterFound,
212    WrongByteSize,
213}
214
215#[cfg(test)]
216mod tests {
217    use std::collections::HashMap;
218
219    use super::*;
220
221    #[test]
222    fn starts_from_zero() {
223        let hyperid = HyperId::default();
224        assert_eq!(hyperid.c, 0);
225    }
226
227    #[test]
228    fn get_return_equal_id() {
229        let hyperid = HyperId::default();
230        assert_eq!(hyperid.get(), hyperid.get());
231    }
232
233    #[test]
234    fn generate_change_internal_state() {
235        let mut hyperid = HyperId::default();
236
237        let c = hyperid.c;
238        hyperid.generate();
239        assert_ne!(hyperid.c, c);
240    }
241
242    #[test]
243    fn generate_returns_different_id() {
244        let mut hyperid = HyperId::default();
245
246        let previous_id = hyperid.get();
247        let next_id = hyperid.generate();
248        assert_ne!(previous_id, next_id);
249    }
250
251    #[test]
252    fn on_255_generate_a_new_base() {
253        let mut hyperid = HyperId::default();
254
255        let base = hyperid.uuid;
256
257        for _ in 0..255 {
258            hyperid.generate();
259        }
260        let new_base = hyperid.uuid;
261        assert_eq!(base, new_base);
262
263        hyperid.generate();
264
265        let new_base = hyperid.uuid;
266        assert_ne!(base, new_base);
267    }
268
269    #[test]
270    fn different_instances_have_different_base() {
271        let hyperid1 = HyperId::default();
272        let hyperid2 = HyperId::default();
273        assert_ne!(hyperid1.uuid, hyperid2.uuid);
274    }
275
276    #[test]
277    fn into_bytes() {
278        let hyperid = HyperId::default();
279
280        let id = hyperid.get();
281
282        let id_bytes = id.into_bytes();
283
284        let id_from_decode = Id::from_bytes(id_bytes).unwrap();
285
286        assert_eq!(hyperid.get(), id_from_decode);
287    }
288
289    #[test]
290    fn id_could_be_the_key_of_hashmap() {
291        let hyperid = HyperId::default();
292
293        let id = hyperid.get();
294
295        let mut map = HashMap::new();
296        map.insert(id, true);
297    }
298
299    #[cfg(feature = "url_safe")]
300    #[test]
301    fn url_safe_encode_decode() {
302        let hyperid = HyperId::default();
303
304        let id = hyperid.get();
305
306        let id_str = id.to_url_safe();
307
308        let id_from_decode = Id::from_url_safe(id_str).unwrap();
309
310        assert_eq!(id, id_from_decode);
311    }
312}