use anyhow::Result;
pub fn validate_generator_name(kind: &str, name: &str) -> Result<()> {
if name.is_empty() {
anyhow::bail!("{} name cannot be empty", kind);
}
if !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_') {
anyhow::bail!(
"{} name '{}' contains invalid characters. Use only letters, numbers, hyphens, and underscores.",
kind,
name
);
}
if !name.chars().next().map(|c| c.is_ascii_alphabetic()).unwrap_or(false) {
anyhow::bail!("{} name must start with a letter", kind);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rejects_empty() {
let err = validate_generator_name("Node", "").unwrap_err();
assert!(err.to_string().contains("cannot be empty"));
}
#[test]
fn rejects_invalid_characters() {
let err = validate_generator_name("Driver", "my driver!").unwrap_err();
assert!(err.to_string().contains("invalid characters"));
}
#[test]
fn rejects_leading_digit() {
let err = validate_generator_name("Behavior", "1patrol").unwrap_err();
assert!(err.to_string().contains("must start with a letter"));
}
#[test]
fn accepts_kebab_and_snake_case() {
assert!(validate_generator_name("Node", "my-motor_node").is_ok());
assert!(validate_generator_name("Node", "Motor2").is_ok());
}
#[test]
fn error_message_uses_supplied_kind() {
let err = validate_generator_name("Behavior", "").unwrap_err();
assert!(err.to_string().contains("Behavior name cannot be empty"));
}
}