boltffi_bindgen 0.24.0

Code generation library for BoltFFI - generates Swift, Kotlin, and TypeScript bindings
Documentation
pub struct NamingConvention;

impl NamingConvention {
    pub fn function_name(name: &str) -> String {
        Self::escape_keyword(name)
    }

    pub fn param_name(name: &str) -> String {
        Self::escape_keyword(name)
    }

    pub fn is_valid_module_name(name: &str) -> bool {
        Self::is_identifier(name) && !Self::is_python_keyword(name)
    }

    fn escape_keyword(name: &str) -> String {
        if Self::is_python_keyword(name) {
            format!("{name}_")
        } else {
            name.to_string()
        }
    }

    fn is_identifier(name: &str) -> bool {
        let mut characters = name.chars();
        let Some(first_character) = characters.next() else {
            return false;
        };

        (first_character == '_' || first_character.is_alphabetic())
            && characters.all(|character| character == '_' || character.is_alphanumeric())
    }

    fn is_python_keyword(name: &str) -> bool {
        matches!(
            name,
            "False"
                | "None"
                | "True"
                | "and"
                | "as"
                | "assert"
                | "async"
                | "await"
                | "break"
                | "case"
                | "class"
                | "continue"
                | "def"
                | "del"
                | "elif"
                | "else"
                | "except"
                | "finally"
                | "for"
                | "from"
                | "global"
                | "if"
                | "import"
                | "in"
                | "is"
                | "lambda"
                | "match"
                | "nonlocal"
                | "not"
                | "or"
                | "pass"
                | "raise"
                | "return"
                | "try"
                | "type"
                | "while"
                | "with"
                | "yield"
        )
    }
}

#[cfg(test)]
mod tests {
    use super::NamingConvention;

    #[test]
    fn escapes_python_keywords() {
        assert_eq!(NamingConvention::function_name("class"), "class_");
        assert_eq!(NamingConvention::function_name("match"), "match_");
        assert_eq!(NamingConvention::param_name("from"), "from_");
        assert_eq!(NamingConvention::param_name("type"), "type_");
    }

    #[test]
    fn leaves_non_keywords_unchanged() {
        assert_eq!(NamingConvention::function_name("echo_i32"), "echo_i32");
        assert_eq!(NamingConvention::param_name("value"), "value");
    }

    #[test]
    fn validates_python_module_names() {
        assert!(NamingConvention::is_valid_module_name("demo_runtime"));
        assert!(!NamingConvention::is_valid_module_name("demo-runtime"));
        assert!(!NamingConvention::is_valid_module_name("demo.runtime"));
        assert!(!NamingConvention::is_valid_module_name("3demo"));
        assert!(!NamingConvention::is_valid_module_name("class"));
    }
}