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