Skip to main content

rusteal_codegen/
naming.rs

1pub fn to_snake_case(name: &str) -> String {
2    let mut result = String::with_capacity(name.len() + 8);
3    let chars: Vec<char> = name.chars().collect();
4
5    for (i, &ch) in chars.iter().enumerate() {
6        if ch.is_ascii_uppercase() {
7            if i > 0 {
8                let prev = chars[i - 1];
9
10                let new_word = prev.is_ascii_lowercase()
11                    || prev.is_ascii_digit()
12                    || (prev.is_ascii_uppercase()
13                        && i + 1 < chars.len()
14                        && chars[i + 1].is_ascii_lowercase());
15
16                if new_word {
17                    result.push('_');
18                }
19            }
20
21            result.push(ch.to_ascii_lowercase());
22        } else {
23            result.push(ch);
24        }
25    }
26
27    result
28}
29
30pub fn strip_bool_prefix(name: &str) -> String {
31    if name.starts_with('b') && name.len() > 1 && name.as_bytes()[1].is_ascii_uppercase() {
32        to_snake_case(&name[1..])
33    } else {
34        to_snake_case(name)
35    }
36}
37
38const RESERVED_WORDS: &[&str] = &[
39    "as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn", "for",
40    "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref", "return",
41    "self", "Self", "static", "struct", "super", "trait", "true", "type", "unsafe", "use", "where",
42    "while", "async", "await", "dyn", "abstract", "become", "box", "do", "final", "macro",
43    "override", "priv", "typeof", "unsized", "virtual", "yield", "try",
44];
45
46pub fn is_reserved(name: &str) -> bool {
47    RESERVED_WORDS.contains(&name)
48}
49
50pub fn escape_reserved(name: &str) -> String {
51    match name {
52        "Self" | "self" | "super" | "crate" => format!("{name}_"),
53        _ if is_reserved(name) => format!("r#{name}"),
54        _ => name.to_string(),
55    }
56}
57
58#[allow(dead_code)]
59pub fn strip_ue_prefix(cpp_name: &str) -> &str {
60    if cpp_name.len() > 1 {
61        let first = cpp_name.as_bytes()[0];
62        let second = cpp_name.as_bytes()[1];
63
64        if (first == b'A' || first == b'U') && second.is_ascii_uppercase() {
65            return &cpp_name[1..];
66        }
67    }
68
69    cpp_name
70}
71
72#[allow(dead_code)]
73pub fn strip_struct_prefix(cpp_name: &str) -> &str {
74    if cpp_name.len() > 1
75        && cpp_name.as_bytes()[0] == b'F'
76        && cpp_name.as_bytes()[1].is_ascii_uppercase()
77    {
78        &cpp_name[1..]
79    } else {
80        cpp_name
81    }
82}
83
84#[allow(dead_code)]
85pub fn to_module_name(name: &str) -> String {
86    let snake = to_snake_case(name);
87    escape_reserved(&snake)
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_to_snake_case() {
96        assert_eq!(to_snake_case("AActor"), "a_actor");
97        assert_eq!(to_snake_case("GetObjectCount"), "get_object_count");
98        assert_eq!(to_snake_case("bNetTemporary"), "b_net_temporary");
99        assert_eq!(to_snake_case("HTTPServer"), "http_server");
100        assert_eq!(to_snake_case("FVector"), "f_vector");
101        assert_eq!(to_snake_case("ECollisionChannel"), "e_collision_channel");
102        assert_eq!(to_snake_case("URL"), "url");
103
104        assert_eq!(
105            to_snake_case("K2_GetActorLocation"),
106            "k2_get_actor_location"
107        );
108    }
109
110    #[test]
111    fn test_strip_bool_prefix() {
112        assert_eq!(strip_bool_prefix("bNetTemporary"), "net_temporary");
113        assert_eq!(strip_bool_prefix("bHidden"), "hidden");
114        assert_eq!(strip_bool_prefix("boolean"), "boolean");
115    }
116
117    #[test]
118    fn test_escape_reserved() {
119        assert_eq!(escape_reserved("Self"), "Self_");
120        assert_eq!(escape_reserved("crate"), "crate_");
121        assert_eq!(escape_reserved("type"), "r#type");
122        assert_eq!(escape_reserved("r#move"), "r#move");
123        assert_eq!(escape_reserved("move"), "r#move");
124        assert_eq!(escape_reserved("actor"), "actor");
125    }
126
127    #[test]
128    fn test_strip_ue_prefix() {
129        assert_eq!(strip_ue_prefix("AActor"), "Actor");
130        assert_eq!(strip_ue_prefix("UObject"), "Object");
131        assert_eq!(strip_ue_prefix("FVector"), "FVector");
132    }
133}