Skip to main content

miniextendr_macros/
naming.rs

1//! Naming helpers for the static symbols the `#[miniextendr]` attribute emits.
2//!
3//! These formatters live in one place so the attribute macro and any later
4//! consumer (e.g. a registration-chasing linter) can compute the exact same
5//! identifiers from a source `syn::Ident`.
6
7/// Identifier for the generated `const &str` holding the R wrapper source.
8///
9/// Returns `R_WRAPPER_{RUST_IDENT}` (uppercased).
10pub(crate) fn r_wrapper_const_ident_for(rust_ident: &syn::Ident) -> syn::Ident {
11    let upper = rust_ident.to_string().to_uppercase();
12    quote::format_ident!("R_WRAPPER_{upper}")
13}
14
15/// Convert a PascalCase string to snake_case.
16///
17/// Inserts an underscore before each uppercase letter (except the first),
18/// then lowercases the entire result. For example, `"InProgress"` becomes
19/// `"in_progress"`.
20pub(crate) fn to_snake_case(s: &str) -> String {
21    let mut out = String::new();
22    for (i, c) in s.char_indices() {
23        if c.is_uppercase() && i > 0 {
24            out.push('_');
25        }
26        out.extend(c.to_lowercase());
27    }
28    out
29}
30
31/// Convert a PascalCase string to kebab-case (`InProgress` → `in-progress`).
32pub(crate) fn to_kebab_case(s: &str) -> String {
33    to_snake_case(s).replace('_', "-")
34}
35
36/// Apply a `rename_all` transformation to a variant name.
37///
38/// Supports `"snake_case"`, `"kebab-case"`, `"lower"`, `"upper"`. Returns the
39/// name unchanged if `rename_all` is `None` or unrecognised.
40pub(crate) fn apply_rename_all(name: &str, rename_all: Option<&str>) -> String {
41    match rename_all {
42        Some("snake_case") => to_snake_case(name),
43        Some("kebab-case") => to_kebab_case(name),
44        Some("lower") => name.to_lowercase(),
45        Some("upper") => name.to_uppercase(),
46        _ => name.to_string(),
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn snake_case() {
56        assert_eq!(to_snake_case("HelloWorld"), "hello_world");
57        assert_eq!(to_snake_case("InProgress"), "in_progress");
58        assert_eq!(to_snake_case("ABC"), "a_b_c");
59        assert_eq!(to_snake_case("Red"), "red");
60    }
61
62    #[test]
63    fn kebab_case() {
64        assert_eq!(to_kebab_case("HelloWorld"), "hello-world");
65        assert_eq!(to_kebab_case("InProgress"), "in-progress");
66    }
67
68    #[test]
69    fn rename_all() {
70        assert_eq!(
71            apply_rename_all("HelloWorld", Some("snake_case")),
72            "hello_world"
73        );
74        assert_eq!(
75            apply_rename_all("HelloWorld", Some("kebab-case")),
76            "hello-world"
77        );
78        assert_eq!(apply_rename_all("HelloWorld", Some("lower")), "helloworld");
79        assert_eq!(apply_rename_all("HelloWorld", Some("upper")), "HELLOWORLD");
80        assert_eq!(apply_rename_all("HelloWorld", None), "HelloWorld");
81    }
82}