Skip to main content

sqlcx_core/
utils.rs

1// ── Shared string utilities ────────────────────────────────────────────────────
2
3/// Split PascalCase/camelCase words by inserting underscores before capital letters.
4pub fn split_words(s: &str) -> String {
5    let mut out = String::with_capacity(s.len() + 4);
6    let chars: Vec<char> = s.chars().collect();
7    for (i, &c) in chars.iter().enumerate() {
8        if c.is_uppercase() && i > 0 && chars[i - 1].is_lowercase() {
9            out.push('_');
10        }
11        out.push(c);
12    }
13    out
14}
15
16/// Convert snake_case, PascalCase, or camelCase to PascalCase.
17pub fn pascal_case(s: &str) -> String {
18    split_words(s)
19        .split('_')
20        .filter(|w| !w.is_empty())
21        .map(|w| {
22            let mut c = w.chars();
23            match c.next() {
24                None => String::new(),
25                Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
26            }
27        })
28        .collect()
29}
30
31/// Convert snake_case, PascalCase, or camelCase to camelCase.
32pub fn camel_case(s: &str) -> String {
33    let p = pascal_case(s);
34    let mut c = p.chars();
35    match c.next() {
36        None => String::new(),
37        Some(f) => f.to_lowercase().collect::<String>() + c.as_str(),
38    }
39}
40
41/// Convert PascalCase or camelCase to snake_case.
42/// Already snake_case names pass through unchanged.
43pub fn snake_case(name: &str) -> String {
44    let mut out = String::with_capacity(name.len() + 4);
45    let chars: Vec<char> = name.chars().collect();
46    for (i, &c) in chars.iter().enumerate() {
47        if c.is_uppercase() && i > 0 && chars[i - 1].is_lowercase() {
48            out.push('_');
49        }
50        out.push(c.to_ascii_lowercase());
51    }
52    out
53}
54
55/// Escape a string for embedding in a JS/TS double-quoted literal.
56/// Mirrors: JSON.stringify(str).slice(1, -1)
57pub fn escape_string(s: &str) -> String {
58    let serialized = serde_json::to_string(s).unwrap_or_else(|_| format!("\"{}\"", s));
59    // Strip the outer quotes that serde_json adds
60    serialized[1..serialized.len() - 1].to_string()
61}
62
63// ── Tests ─────────────────────────────────────────────────────────────────────
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn pascal_case_snake() {
71        assert_eq!(pascal_case("user_status"), "UserStatus");
72    }
73
74    #[test]
75    fn pascal_case_single() {
76        assert_eq!(pascal_case("users"), "Users");
77    }
78
79    #[test]
80    fn camel_case_snake() {
81        assert_eq!(camel_case("get_user"), "getUser");
82    }
83
84    #[test]
85    fn split_words_pascal() {
86        assert_eq!(split_words("GetUser"), "Get_User");
87    }
88
89    #[test]
90    fn escape_string_quotes() {
91        assert_eq!(escape_string("he said \"hi\""), "he said \\\"hi\\\"");
92    }
93}