vika_cli/generator/
utils.rs

1pub fn to_pascal_case(s: &str) -> String {
2    let mut result = String::new();
3    let mut capitalize_next = true;
4
5    for c in s.chars() {
6        if c == '_' || c == '-' || c == ' ' {
7            capitalize_next = true;
8        } else if capitalize_next {
9            result.push(c.to_uppercase().next().unwrap_or(c));
10            capitalize_next = false;
11        } else {
12            result.push(c);
13        }
14    }
15
16    result
17}
18
19pub fn to_camel_case(s: &str) -> String {
20    let pascal = to_pascal_case(s);
21    if pascal.is_empty() {
22        return pascal;
23    }
24
25    let mut chars = pascal.chars();
26    let first = chars.next().unwrap().to_lowercase().next().unwrap();
27    format!("{}{}", first, chars.as_str())
28}
29
30/// Sanitizes a property name to be a valid JavaScript identifier
31/// Returns the original name if valid, or the name in quotes if invalid
32pub fn sanitize_property_name(name: &str) -> String {
33    let first_char = name.chars().next();
34    let needs_quotes = match first_char {
35        Some(c) if c.is_ascii_digit() => true, // starts with number
36        _ => name.contains(' ') || name.contains('-') && !name.starts_with('-'), // contains spaces or hyphens (but not negative numbers)
37    };
38
39    if needs_quotes {
40        format!("\"{}\"", name)
41    } else {
42        name.to_string()
43    }
44}
45
46/// Sanitizes module names for use as directory/file names
47/// Replaces spaces with hyphens, converts to lowercase, and removes other invalid characters
48/// This ensures consistent casing across case-insensitive filesystems (like macOS)
49pub fn sanitize_module_name(name: &str) -> String {
50    name.replace(' ', "-")
51        .to_lowercase()
52        .chars()
53        .filter(|c| c.is_alphanumeric() || *c == '-' || *c == '_' || *c == '/')
54        .collect()
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60
61    #[test]
62    fn test_to_pascal_case_simple() {
63        assert_eq!(to_pascal_case("hello"), "Hello");
64        assert_eq!(to_pascal_case("world"), "World");
65    }
66
67    #[test]
68    fn test_to_pascal_case_with_underscore() {
69        assert_eq!(to_pascal_case("hello_world"), "HelloWorld");
70        assert_eq!(to_pascal_case("user_name"), "UserName");
71    }
72
73    #[test]
74    fn test_to_pascal_case_with_hyphen() {
75        assert_eq!(to_pascal_case("hello-world"), "HelloWorld");
76        assert_eq!(to_pascal_case("api-key"), "ApiKey");
77    }
78
79    #[test]
80    fn test_to_pascal_case_with_space() {
81        assert_eq!(to_pascal_case("hello world"), "HelloWorld");
82        assert_eq!(to_pascal_case("user name"), "UserName");
83    }
84
85    #[test]
86    fn test_to_pascal_case_mixed() {
87        assert_eq!(to_pascal_case("hello_world-test"), "HelloWorldTest");
88        assert_eq!(to_pascal_case("api_key-name"), "ApiKeyName");
89    }
90
91    #[test]
92    fn test_to_pascal_case_empty() {
93        assert_eq!(to_pascal_case(""), "");
94    }
95
96    #[test]
97    fn test_to_pascal_case_single_word() {
98        assert_eq!(to_pascal_case("test"), "Test");
99    }
100
101    #[test]
102    fn test_to_camel_case_simple() {
103        assert_eq!(to_camel_case("hello"), "hello");
104        assert_eq!(to_camel_case("world"), "world");
105    }
106
107    #[test]
108    fn test_to_camel_case_with_underscore() {
109        assert_eq!(to_camel_case("hello_world"), "helloWorld");
110        assert_eq!(to_camel_case("user_name"), "userName");
111    }
112
113    #[test]
114    fn test_to_camel_case_with_hyphen() {
115        assert_eq!(to_camel_case("hello-world"), "helloWorld");
116        assert_eq!(to_camel_case("api-key"), "apiKey");
117    }
118
119    #[test]
120    fn test_to_camel_case_empty() {
121        assert_eq!(to_camel_case(""), "");
122    }
123
124    #[test]
125    fn test_to_camel_case_single_word() {
126        assert_eq!(to_camel_case("test"), "test");
127    }
128
129    #[test]
130    fn test_to_camel_case_mixed() {
131        assert_eq!(to_camel_case("hello_world-test"), "helloWorldTest");
132    }
133
134    #[test]
135    fn test_sanitize_property_name_valid() {
136        assert_eq!(sanitize_property_name("name"), "name");
137        assert_eq!(sanitize_property_name("userName"), "userName");
138        assert_eq!(sanitize_property_name("_private"), "_private");
139    }
140
141    #[test]
142    fn test_sanitize_property_name_starts_with_number() {
143        assert_eq!(sanitize_property_name("2xl"), "\"2xl\"");
144        assert_eq!(sanitize_property_name("3xl"), "\"3xl\"");
145        assert_eq!(sanitize_property_name("404error"), "\"404error\"");
146    }
147
148    #[test]
149    fn test_sanitize_property_name_with_spaces() {
150        assert_eq!(
151            sanitize_property_name("Translation name"),
152            "\"Translation name\""
153        );
154        assert_eq!(sanitize_property_name("user name"), "\"user name\"");
155    }
156
157    #[test]
158    fn test_sanitize_module_name() {
159        assert_eq!(sanitize_module_name("cart"), "cart");
160        assert_eq!(sanitize_module_name("AI Chat"), "ai-chat");
161        assert_eq!(sanitize_module_name("admin/orders"), "admin/orders");
162        assert_eq!(sanitize_module_name("test module name"), "test-module-name");
163        assert_eq!(sanitize_module_name("Inventory"), "inventory");
164    }
165}