cuid2_timeless/
utils.rs

1use gethostname::gethostname;
2use num_bigint::{self, BigUint};
3use num_integer;
4use std;
5#[cfg(feature = "regex")]
6use regex;
7
8/// Default counter function
9pub fn create_counter(mut count: isize) -> CounterFunctionType {
10    return Box::new(move || {
11        count += 1;
12        return count;
13    });
14}
15
16/// Random function type
17pub type RandomFunctionType = Box<dyn FnMut() -> f64>;
18/// Create counter function type
19pub type CreateCounterFunctionType = Box<dyn Fn(isize) -> CounterFunctionType>;
20/// Counter function type
21pub type CounterFunctionType = Box<dyn FnMut() -> isize>;
22/// Fingerprint function type
23pub type FingerPrintFunctionType =
24    Box<dyn Fn(&mut RandomFunctionType, Option<String>) -> String>;
25
26/// Stupidities
27const BIG_LENGTH: usize = 32;
28const SHENANIGANS: [char; 36] = [
29    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
30    't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
31];
32const SHENANIGANS_LENGTH: u128 = SHENANIGANS.len() as u128;
33const SHENANIGANS_LOWERCASE: [char; 26] = [
34    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
35    't', 'u', 'v', 'w', 'x', 'y', 'z',
36];
37
38/// Creates a machine-specific fingerprint
39pub fn create_fingerprint(
40    random_number_generator: &mut RandomFunctionType,
41    fingerprint_data: Option<String>,
42) -> String {
43    let new_fingerprint_data: String;
44    if fingerprint_data.is_none() {
45        let process_id = std::process::id();
46        let host_name = gethostname();
47        let env_vars = std::env::vars()
48            .map(|(k, _)| return k)
49            .collect::<Vec<String>>()
50            .join("");
51        new_fingerprint_data =
52            process_id.to_string() + &host_name.to_string_lossy() + env_vars.as_str();
53    } else {
54        new_fingerprint_data = fingerprint_data.unwrap();
55    }
56    let fingerprint = new_fingerprint_data
57        + create_entropy(random_number_generator, Some(BIG_LENGTH))
58            .unwrap()
59            .as_str();
60    return create_hash(Some(fingerprint))[0..BIG_LENGTH].to_string();
61}
62
63/// Creates hash based on data and hash it with SHA based on feature flag you specified
64pub fn create_hash(data: Option<String>) -> String {
65    let actual_data = data.unwrap_or("".to_string());
66    
67    #[cfg(feature="sha2")]
68    use sha2::{self, Digest};
69    #[cfg(feature="sha2")]
70    let mut hasher = sha2::Sha512::new();
71    #[cfg(feature="sha3")]
72    use sha3::{self, Digest};
73    #[cfg(feature="sha3")]
74    let mut hasher = sha3::Sha3_512::new();
75
76    hasher.update(actual_data);
77    let hashed_value = hasher.finalize();
78
79    let hashed_int = num_bigint::BigUint::from_bytes_be(&hashed_value);
80    return base36_encode(hashed_int);
81}
82
83/// Creates entropy by length
84pub fn create_entropy(
85    random_number_generator: &mut RandomFunctionType,
86    length: Option<usize>,
87) -> Result<String, ()> {
88    let actual_length = length.unwrap_or(4);
89    if actual_length < 1 {
90        return Err(());
91    }
92    let mut entropy = String::new();
93    while entropy.len() < actual_length {
94        entropy += base36_encode(num_bigint::BigUint::from(
95            (random_number_generator() * 36.0).floor() as u128,
96        ))
97        .as_str();
98    }
99    return Ok(entropy);
100}
101
102/// Base36 Encoder
103pub fn base36_encode(mut number: num_bigint::BigUint) -> String {
104    let mut encoded_string = String::new();
105    let mut modular: num_bigint::BigUint;
106    while number != BigUint::from(0 as usize) {
107        (number, modular) = num_integer::div_rem(number, SHENANIGANS_LENGTH.into());
108        encoded_string = SHENANIGANS[modular.to_string().parse::<usize>().unwrap()].to_string()
109            + &encoded_string;
110    }
111    if encoded_string.len() == 0 {
112        return "0".to_string();
113    }
114    return encoded_string;
115}
116
117/// Randomly selects a letter from [`RandomFunctionType`] value
118pub fn create_letter(random_number_generator: &mut RandomFunctionType) -> char {
119    return SHENANIGANS_LOWERCASE
120        [(random_number_generator() * (SHENANIGANS_LOWERCASE.len() as f64)) as usize];
121}
122
123#[cfg(feature = "regex")]
124/// Checks if this is valid [`crate::Cuid`]
125pub fn is_cuid(id: String, min_length: Option<usize>, max_length: Option<usize>) -> bool {
126    let length = id.len();
127    let re = r"^[0-9a-z]+$";
128    if length >= min_length.unwrap_or(2) && length <= max_length.unwrap_or(crate::generator::MAXIMUM_LENGTH) {
129        return regex::Regex::new(re).unwrap().is_match(&id)
130    }
131    return false;
132}