tinc_build/codegen/
prost_sanatize.rs

1use heck::{ToSnakeCase, ToUpperCamelCase};
2
3pub(crate) fn sanitize_identifier(s: impl AsRef<str>) -> String {
4    let ident = s.as_ref();
5    // Use a raw identifier if the identifier matches a Rust keyword:
6    // https://doc.rust-lang.org/reference/keywords.html.
7    match ident {
8        // 2015 strict keywords.
9        | "as" | "break" | "const" | "continue" | "else" | "enum" | "false"
10        | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "match" | "mod" | "move" | "mut"
11        | "pub" | "ref" | "return" | "static" | "struct" | "trait" | "true"
12        | "type" | "unsafe" | "use" | "where" | "while"
13        // 2018 strict keywords.
14        | "dyn"
15        // 2015 reserved keywords.
16        | "abstract" | "become" | "box" | "do" | "final" | "macro" | "override" | "priv" | "typeof"
17        | "unsized" | "virtual" | "yield"
18        // 2018 reserved keywords.
19        | "async" | "await" | "try"
20        // 2024 reserved keywords.
21        | "gen" => format!("r#{ident}"),
22        // the following keywords are not supported as raw identifiers and are therefore suffixed with an underscore.
23        "_" | "super" | "self" | "Self" | "extern" | "crate" => format!("{ident}_"),
24        // the following keywords begin with a number and are therefore prefixed with an underscore.
25        s if s.starts_with(|c: char| c.is_numeric()) => format!("_{ident}"),
26        _ => ident.to_string(),
27    }
28}
29
30/// Converts a `camelCase` or `SCREAMING_SNAKE_CASE` identifier to a `lower_snake` case Rust field
31/// identifier.
32pub(crate) fn to_snake(s: impl AsRef<str>) -> String {
33    sanitize_identifier(s.as_ref().to_snake_case())
34}
35
36/// Converts a `snake_case` identifier to an `UpperCamel` case Rust type identifier.
37pub(crate) fn to_upper_camel(s: impl AsRef<str>) -> String {
38    sanitize_identifier(s.as_ref().to_upper_camel_case())
39}
40
41pub(crate) fn strip_enum_prefix(prefix: &str, name: &str) -> String {
42    let stripped = name.strip_prefix(prefix).unwrap_or(name);
43
44    // If the next character after the stripped prefix is not
45    // uppercase, then it means that we didn't have a true prefix -
46    // for example, "Foo" should not be stripped from "Foobar".
47    let stripped = if stripped.chars().next().map(char::is_uppercase).unwrap_or(false) {
48        stripped
49    } else {
50        name
51    };
52    sanitize_identifier(stripped)
53}