cuid2_timeless/
generator.rs

1#[cfg(feature = "random")]
2use rand::{self, Rng};
3
4use crate::{
5    errors,
6    utils,
7};
8
9/// ~22k hosts before 50% chance of initial counter collision
10/// with a remaining counter range of 9.0e+15 in JavaScript.
11pub const INITIAL_COUNT_MAX: usize = 476782367;
12/// Default length when called [`Cuid::default()`]
13pub const DEFAULT_LENGTH: usize = 24;
14/// Maximum length for [`Cuid::generate()`]
15pub const MAXIMUM_LENGTH: usize = 98;
16
17/// CUID2 implementation
18pub struct Cuid {
19    random: utils::RandomFunctionType,
20    counter: utils::CounterFunctionType,
21    length: usize,
22    fingerprint: String,
23}
24
25#[cfg(feature = "random")]
26impl Default for Cuid {
27    fn default() -> Self {
28        let mut randomity = rand::thread_rng();
29        let randomed: f64 = randomity.gen();
30        let mut wrapper_rand: Box<dyn FnMut() -> f64> = Box::new(move || randomity.gen());
31        Cuid {
32            fingerprint: utils::create_fingerprint(&mut wrapper_rand, None),
33            random: wrapper_rand,
34            counter: Box::new(utils::create_counter(
35                (randomed * INITIAL_COUNT_MAX as f64) as isize,
36            )),
37            length: DEFAULT_LENGTH,
38        }
39    }
40}
41
42impl Cuid {
43    #[inline]
44    /// Initialize new [`Cuid`] instance without default configurations
45    pub fn new(
46        mut random: utils::RandomFunctionType,
47        counter: utils::CreateCounterFunctionType,
48        length: usize,
49        fingerprint: utils::FingerPrintFunctionType,
50    ) -> Self {
51        let randomed = random();
52        let created = counter((randomed * INITIAL_COUNT_MAX as f64) as isize);
53        Cuid {
54            fingerprint: fingerprint(&mut random, None),
55            random,
56            counter: created,
57            length,
58        }
59    }
60    #[inline]
61    /// Generate a CUID
62    pub fn generate(&mut self, length: Option<usize>) -> Result<String, errors::Errors> {
63        let actual_length = length.unwrap_or(self.length);
64        if actual_length > MAXIMUM_LENGTH {
65            return Err(errors::Errors::ExceededMaximumLengthGenerateCuidError);
66        }
67
68        let first_letter = utils::create_letter(&mut self.random);
69
70        let base36_time = utils::base36_encode(
71            std::time::SystemTime::now()
72                .duration_since(std::time::UNIX_EPOCH)
73                .unwrap()
74                .as_nanos()
75                .into(),
76        );
77        let base36_count = utils::base36_encode(((self.counter)() as usize).into());
78
79        let salt = match utils::create_entropy(&mut self.random, Some(actual_length))
80            .map_err(|_| errors::Errors::LessThanOneEntropyError)
81        {
82            Ok(s) => s,
83            Err(e) => {
84                return Err(e);
85            }
86        };
87        let hash_input = base36_time + &salt + &base36_count + &self.fingerprint;
88
89        return Ok(first_letter.to_string()
90            + &utils::create_hash(Some(hash_input))[0..actual_length]);
91    }
92}
93
94#[cfg(feature = "random")]
95/// A function wrapper for generating [`Cuid`] with default configurations
96pub fn cuid_wrapper() -> Box<dyn FnMut() -> Result<String, errors::Errors>> {
97    let mut cuid = Cuid::default();
98    return Box::new(move || cuid.generate(None));
99}