1use std::{borrow::Cow, collections::HashMap, path::*, sync::OnceLock};
2
3use crate::case::*;
4
5static CRATE_NAME_OVERRIDES: OnceLock<HashMap<String, String>> = OnceLock::new();
6
7pub(crate) fn set_crate_name_overrides(overrides: HashMap<String, String>) {
8 assert!(
9 CRATE_NAME_OVERRIDES.set(overrides).is_ok(),
10 "Crate name overrides already set"
11 );
12}
13
14fn get_crate_name_override(crate_name: &str) -> Option<String> {
15 CRATE_NAME_OVERRIDES
16 .get()
17 .and_then(|overrides| overrides.get(crate_name).cloned())
18}
19
20pub fn split_namespace_name(name: &str) -> (Option<&str>, &str) {
21 let mut parts = name.split('.');
22 let name = parts.next_back().unwrap();
23 let ns = parts.next_back();
24 assert!(ns.is_none() || parts.next().is_none());
25 (ns, name)
26}
27
28pub fn file_name_sys(name: &str) -> String {
39 let mut path = PathBuf::from(name);
40 let added = path.set_extension("rs");
41 assert!(added);
42 path.to_str().unwrap().into()
43}
44
45pub fn crate_name(name: &str) -> String {
47 let name = name.replace('-', "_").to_snake();
48 let crate_name = if let Some(name_without_prefix) = name.strip_prefix("g_") {
49 name_without_prefix.to_owned()
50 } else {
51 name
52 };
53 if let Some(crate_name) = get_crate_name_override(&crate_name) {
54 crate_name
55 } else {
56 crate_name
57 }
58}
59
60pub fn exported_crate_name(crate_name: &str) -> String {
62 crate_name.replace('_', "-")
63}
64
65pub fn module_name(name: &str) -> String {
66 mangle_keywords(name.to_snake()).into_owned()
67}
68
69pub fn enum_member_name(name: &str) -> String {
70 if name.starts_with(char::is_alphabetic) {
71 name.to_camel()
72 } else {
73 format!("_{}", name.to_camel())
74 }
75}
76
77pub fn bitfield_member_name(name: &str) -> String {
78 if name.starts_with(char::is_alphabetic) {
79 name.to_uppercase()
80 } else {
81 format!("_{}", name.to_uppercase())
82 }
83}
84
85pub fn needs_mangling(name: &str) -> bool {
86 keywords().contains_key(name)
87}
88
89pub fn mangle_keywords<'a, S: Into<Cow<'a, str>>>(name: S) -> Cow<'a, str> {
91 let name = name.into();
92 if let Some(s) = keywords().get(&*name) {
93 s.clone().into()
94 } else {
95 name
96 }
97}
98
99fn keywords() -> &'static HashMap<&'static str, String> {
100 static KEYWORDS: OnceLock<HashMap<&'static str, String>> = OnceLock::new();
101 KEYWORDS.get_or_init(|| {
102 [
103 "abstract", "alignof", "as", "async", "await", "become", "box", "break", "const",
104 "continue", "crate", "do", "dyn", "else", "enum", "extern", "false", "final", "fn",
105 "for", "if", "impl", "in", "let", "loop", "macro", "match", "mod", "move", "mut",
106 "offsetof", "override", "priv", "proc", "pub", "pure", "ref", "return", "Self", "self",
107 "sizeof", "static", "struct", "super", "trait", "true", "try", "type", "typeof",
108 "unsafe", "unsized", "use", "virtual", "where", "while", "yield",
109 ]
110 .iter()
111 .map(|k| (*k, format!("{k}_")))
112 .collect()
113 })
114}
115
116pub fn signal_to_snake(signal: &str) -> String {
117 signal.replace("::", "_").replace('-', "_")
118}
119
120pub fn lib_name_to_toml(name: &str) -> String {
121 name.to_string().replace(['-', '.'], "_")
122}
123
124pub fn use_glib_type(env: &crate::env::Env, import: &str) -> String {
125 format!(
126 "{}::{}",
127 if env.library.is_glib_crate() {
128 "crate"
129 } else {
130 "glib"
131 },
132 import
133 )
134}
135
136pub fn use_glib_if_needed(env: &crate::env::Env, import: &str) -> String {
137 format!(
138 "{}{}",
139 if env.library.is_glib_crate() {
140 ""
141 } else {
142 "glib::"
143 },
144 import
145 )
146}
147
148pub fn use_gio_type(env: &crate::env::Env, import: &str) -> String {
149 format!(
150 "{}::{}",
151 if env.library.is_crate("Gio") {
152 "crate"
153 } else {
154 "gio"
155 },
156 import
157 )
158}
159
160pub fn use_gtk_type(env: &crate::env::Env, import: &str) -> String {
161 format!(
162 "{}::{}",
163 if env.library.is_crate("Gtk") {
164 "crate"
165 } else {
166 "gtk"
167 },
168 import
169 )
170}
171
172pub fn is_gstring(name: &str) -> bool {
173 name == "GString" || name.ends_with("::GString")
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn split_no_namespace() {
182 let (ns, name) = split_namespace_name("GObject");
183 assert_eq!(ns, None);
184 assert_eq!(name, "GObject");
185 }
186
187 #[test]
188 fn split_full_name() {
189 let (ns, name) = split_namespace_name("Gtk.StatusIcon");
190 assert_eq!(ns, Some("Gtk"));
191 assert_eq!(name, "StatusIcon");
192 }
193
194 #[test]
207 fn crate_name_works() {
208 assert_eq!(crate_name("GdkPixbuf"), "gdk_pixbuf");
209 assert_eq!(crate_name("GLib"), "glib");
210 assert_eq!(crate_name("GObject"), "gobject");
211 assert_eq!(crate_name("Gtk"), "gtk");
212 }
213
214 #[test]
215 fn file_name_sys_works() {
216 assert_eq!(file_name_sys("funcs"), "funcs.rs");
217 }
218
219 #[test]
220 fn signal_to_snake_works() {
221 assert_eq!(signal_to_snake("changed"), "changed");
222 assert_eq!(signal_to_snake("move-active"), "move_active");
223 }
224
225 #[test]
226 fn lib_name_to_toml_works() {
227 assert_eq!(lib_name_to_toml("gstreamer-1.0"), "gstreamer_1_0");
228 }
229}