libgir/
nameutil.rs

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
28// unused :(
29// pub fn strip_suffix<'a>(name: &'a str, suffix: &str) -> Option<&'a str> {
30// if name.ends_with(suffix) {
31// Some(&name[..name.len() - suffix.len()])
32// }
33// else {
34// None
35// }
36// }
37
38pub 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
45/// Crate name with underscores for `use` statement
46pub 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
60/// Crate name with '-' for Cargo.toml etc.
61pub 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
89// If the mangling happened, guaranteed to return Owned.
90pub 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]
195    // fn strip_prefix_g() {
196    // assert_eq!(strip_prefix("G", "GBusType"), "BusType");
197    // assert_eq!(strip_prefix("G", "G_BUS_TYPE_NONE"), "BUS_TYPE_NONE");
198    // }
199    //
200    // #[test]
201    // fn strip_prefix_gtk() {
202    // assert_eq!(strip_prefix("Gtk", "GtkAlign"), "Align");
203    // assert_eq!(strip_prefix("Gtk", "GTK_ALIGN_FILL"), "ALIGN_FILL");
204    // }
205
206    #[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}