Skip to main content

alien_core/
id_utils.rs

1use nanoid::nanoid;
2use regex::Regex;
3
4/// Defines different ID types with their configuration
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub enum IdType {
7    Event,
8}
9
10/// Configuration for ID generation
11#[derive(Debug, Clone)]
12pub struct IdConfig {
13    prefix: &'static str,
14    lowercase: bool,
15    length: usize,
16}
17
18impl IdConfig {
19    const fn new(prefix: &'static str, lowercase: bool, length: usize) -> Self {
20        Self {
21            prefix,
22            lowercase,
23            length,
24        }
25    }
26}
27
28/// Get configuration for a specific ID type
29const fn get_id_config(id_type: IdType) -> IdConfig {
30    match id_type {
31        IdType::Event => IdConfig::new("event", false, 28),
32    }
33}
34
35pub const ALPHABET_LOWERCASE: &[char] = &[
36    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
37    'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
38];
39
40pub const ALPHABET_MIXED_CASE: &[char] = &[
41    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
42    'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
43    'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
44    'v', 'w', 'x', 'y', 'z',
45];
46
47/// Generate a new ID for the specified type
48pub fn new_id(id_type: IdType) -> String {
49    let config = get_id_config(id_type);
50    let alphabet = if config.lowercase {
51        ALPHABET_LOWERCASE
52    } else {
53        ALPHABET_MIXED_CASE
54    };
55
56    let length = config.length;
57
58    let id = nanoid!(length, &alphabet);
59
60    format!("{}_{}", config.prefix, id)
61}
62
63/// Generate a deterministic ID example for a given type
64pub fn generate_id_example(id_type: IdType) -> String {
65    let config = get_id_config(id_type);
66
67    // Determine character set based on lowercase flag
68    let char_set = if config.lowercase {
69        ALPHABET_LOWERCASE
70    } else {
71        ALPHABET_MIXED_CASE
72    };
73
74    // Length of the suffix
75    let suffix_length = config.length;
76
77    // Seed generation from the prefix
78    let mut seed = 0u32;
79    for c in config.prefix.chars() {
80        seed = (seed.wrapping_mul(31) + c as u32) % 4_294_967_295;
81    }
82
83    // Generate the suffix using the pseudo-random function
84    let mut suffix = String::with_capacity(suffix_length);
85    for _ in 0..suffix_length {
86        // Pseudo-random number generator
87        seed = (seed.wrapping_mul(1_664_525) + 1_013_904_223) % 4_294_967_295;
88        let random_value = seed as f64 / 4_294_967_295.0;
89
90        let index = (random_value * char_set.len() as f64).floor() as usize;
91        suffix.push(char_set[index]);
92    }
93
94    // Return the complete ID
95    format!("{}_{}", config.prefix, suffix)
96}
97
98/// Convert camel case to spaced lowercase
99fn convert_camel_to_spaces(input: &str) -> String {
100    let re = Regex::new(r"([a-z])([A-Z])").unwrap();
101    re.replace_all(input, "$1 $2").to_lowercase()
102}
103
104/// Capitalize the first letter of a string
105fn capitalize_first_letter(input: &str) -> String {
106    let mut chars = input.chars();
107    match chars.next() {
108        None => String::new(),
109        Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
110    }
111}
112
113/// Generate a regex pattern for validating IDs of a specific type
114pub fn id_regex_pattern(id_type: IdType) -> String {
115    let config = get_id_config(id_type);
116    let alphabet_regex = if config.lowercase {
117        "0-9a-z"
118    } else {
119        "0-9a-zA-Z"
120    };
121
122    format!(
123        "^{}_[{}]{{{}}}$",
124        config.prefix, alphabet_regex, config.length
125    )
126}
127
128/// Generate an error message for invalid IDs
129pub fn id_error_message(id_type: IdType) -> String {
130    let config = get_id_config(id_type);
131    let type_name = convert_camel_to_spaces(&format!("{:?}", id_type));
132    let alphabet_desc = if config.lowercase {
133        "lowercase letters and numbers"
134    } else {
135        "alphanumeric characters"
136    };
137
138    format!(
139        "{} ID must start with {}_ and can only contain {}.",
140        capitalize_first_letter(&type_name),
141        config.prefix,
142        alphabet_desc
143    )
144}