rustisan_core/
utils.rs

1//! Utility functions and helpers for the Rustisan framework
2//!
3//! This module provides common utility functions used throughout the framework.
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::fmt;
9use std::time::{SystemTime, UNIX_EPOCH};
10use uuid::Uuid;
11use crate::errors::{Result, RustisanError};
12
13/// Generates a random string of specified length
14pub fn random_string(length: usize) -> String {
15    use rand::Rng;
16    const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
17    let mut rng = rand::thread_rng();
18
19    (0..length)
20        .map(|_| {
21            let idx = rng.gen_range(0..CHARSET.len());
22            CHARSET[idx] as char
23        })
24        .collect()
25}
26
27/// Generates a UUID v4 string
28pub fn uuid() -> String {
29    Uuid::new_v4().to_string()
30}
31
32/// Gets current timestamp in seconds
33pub fn timestamp() -> u64 {
34    SystemTime::now()
35        .duration_since(UNIX_EPOCH)
36        .unwrap_or_default()
37        .as_secs()
38}
39
40/// Gets current timestamp in milliseconds
41pub fn timestamp_millis() -> u64 {
42    SystemTime::now()
43        .duration_since(UNIX_EPOCH)
44        .unwrap_or_default()
45        .as_millis() as u64
46}
47
48/// Converts a timestamp to DateTime<Utc>
49pub fn timestamp_to_datetime(timestamp: u64) -> DateTime<Utc> {
50    DateTime::from_timestamp(timestamp as i64, 0).unwrap_or_else(|| Utc::now())
51}
52
53/// Slugifies a string (converts to URL-friendly format)
54pub fn slugify(input: &str) -> String {
55    input
56        .chars()
57        .map(|c| {
58            if c.is_alphanumeric() {
59                c.to_ascii_lowercase()
60            } else if c.is_whitespace() || c == '-' || c == '_' {
61                '-'
62            } else {
63                '\0'
64            }
65        })
66        .filter(|&c| c != '\0')
67        .collect::<String>()
68        .split('-')
69        .filter(|s| !s.is_empty())
70        .collect::<Vec<&str>>()
71        .join("-")
72}
73
74/// Converts camelCase to snake_case
75pub fn camel_to_snake(input: &str) -> String {
76    let mut result = String::new();
77    for (i, ch) in input.chars().enumerate() {
78        if i > 0 && ch.is_uppercase() {
79            result.push('_');
80        }
81        result.push(ch.to_lowercase().next().unwrap_or(ch));
82    }
83    result
84}
85
86/// Converts snake_case to camelCase
87pub fn snake_to_camel(input: &str) -> String {
88    let mut result = String::new();
89    let mut capitalize_next = false;
90
91    for ch in input.chars() {
92        if ch == '_' {
93            capitalize_next = true;
94        } else if capitalize_next {
95            result.push(ch.to_uppercase().next().unwrap_or(ch));
96            capitalize_next = false;
97        } else {
98            result.push(ch);
99        }
100    }
101
102    result
103}
104
105/// Converts PascalCase to snake_case
106pub fn pascal_to_snake(input: &str) -> String {
107    camel_to_snake(input)
108}
109
110/// Converts snake_case to PascalCase
111pub fn snake_to_pascal(input: &str) -> String {
112    input
113        .split('_')
114        .map(|word| {
115            let mut chars = word.chars();
116            match chars.next() {
117                None => String::new(),
118                Some(first) => first.to_uppercase().collect::<String>() + &chars.as_str(),
119            }
120        })
121        .collect()
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn test_random_string() {
130        let s = random_string(10);
131        assert_eq!(s.len(), 10);
132    }
133
134    #[test]
135    fn test_uuid() {
136        let u = uuid();
137        assert!(u.len() > 0);
138        assert!(u.contains('-'));
139    }
140
141    #[test]
142    fn test_slugify() {
143        assert_eq!(slugify("Hello World"), "hello-world");
144        assert_eq!(slugify("Test  Multiple   Spaces"), "test-multiple-spaces");
145        assert_eq!(slugify("Special!@#Characters"), "specialcharacters");
146    }
147
148    #[test]
149    fn test_camel_to_snake() {
150        assert_eq!(camel_to_snake("camelCase"), "camel_case");
151        assert_eq!(camel_to_snake("HTTPRequest"), "h_t_t_p_request");
152        assert_eq!(camel_to_snake("simple"), "simple");
153    }
154
155    #[test]
156    fn test_snake_to_camel() {
157        assert_eq!(snake_to_camel("snake_case"), "snakeCase");
158        assert_eq!(snake_to_camel("simple"), "simple");
159        assert_eq!(snake_to_camel("multiple_words_here"), "multipleWordsHere");
160    }
161
162    #[test]
163    fn test_snake_to_pascal() {
164        assert_eq!(snake_to_pascal("snake_case"), "SnakeCase");
165        assert_eq!(snake_to_pascal("simple"), "Simple");
166        assert_eq!(snake_to_pascal("multiple_words_here"), "MultipleWordsHere");
167    }
168
169    #[test]
170    fn test_pascal_to_snake() {
171        assert_eq!(pascal_to_snake("PascalCase"), "pascal_case");
172        assert_eq!(pascal_to_snake("HTTPRequest"), "h_t_t_p_request");
173    }
174}