use crate::utils::error::{Error, Result};
use regex::Regex;
use std::fmt;
#[derive(Debug, Clone)]
pub struct NamingError {
message: String,
violation_type: ViolationType,
suggestion: Option<String>,
}
impl NamingError {
pub fn new(violation_type: ViolationType, message: String, suggestion: Option<String>) -> Self {
Self {
message,
violation_type,
suggestion,
}
}
pub fn violation_type(&self) -> &ViolationType {
&self.violation_type
}
pub fn suggestion(&self) -> Option<&str> {
self.suggestion.as_deref()
}
pub fn message(&self) -> &str {
&self.message
}
}
impl fmt::Display for NamingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)?;
if let Some(suggestion) = &self.suggestion {
write!(f, " (suggestion: {})", suggestion)?;
}
Ok(())
}
}
impl std::error::Error for NamingError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ViolationType {
InvalidTypeName,
InvalidFunctionName,
InvalidErrorName,
InvalidCommandName,
InvalidHandlerName,
InvalidModuleName,
}
impl fmt::Display for ViolationType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ViolationType::InvalidTypeName => write!(f, "InvalidTypeName"),
ViolationType::InvalidFunctionName => write!(f, "InvalidFunctionName"),
ViolationType::InvalidErrorName => write!(f, "InvalidErrorName"),
ViolationType::InvalidCommandName => write!(f, "InvalidCommandName"),
ViolationType::InvalidHandlerName => write!(f, "InvalidHandlerName"),
ViolationType::InvalidModuleName => write!(f, "InvalidModuleName"),
}
}
}
pub struct NamingValidator {
type_pattern: Regex,
function_pattern: Regex,
error_pattern: Regex,
command_pattern: Regex,
handler_pattern: Regex,
module_pattern: Regex,
}
impl NamingValidator {
pub fn new() -> Result<Self> {
Ok(Self {
type_pattern: Regex::new(r"^[A-Z][a-zA-Z0-9]*$").map_err(|e| {
Error::invalid_input(format!("Failed to compile type pattern: {}", e))
})?,
function_pattern: Regex::new(r"^[a-z][a-z0-9]*(_[a-z0-9]+)*$").map_err(|e| {
Error::invalid_input(format!("Failed to compile function pattern: {}", e))
})?,
error_pattern: Regex::new(r"^[A-Z][a-zA-Z0-9]*Error$").map_err(|e| {
Error::invalid_input(format!("Failed to compile error pattern: {}", e))
})?,
command_pattern: Regex::new(r"^[a-z]([a-z0-9-]*[a-z0-9])?$").map_err(|e| {
Error::invalid_input(format!("Failed to compile command pattern: {}", e))
})?,
handler_pattern: Regex::new(r"^(handle_[a-z][a-z0-9_]*|[a-z][a-z0-9_]*_handler)$")
.map_err(|e| {
Error::invalid_input(format!("Failed to compile handler pattern: {}", e))
})?,
module_pattern: Regex::new(r"^[a-z][a-z0-9]*(_[a-z0-9]+)*$").map_err(|e| {
Error::invalid_input(format!("Failed to compile module pattern: {}", e))
})?,
})
}
pub fn validate_type_name(&self, name: &str) -> Result<()> {
if self.type_pattern.is_match(name) {
Ok(())
} else {
Err(Error::invalid_input(
NamingError::new(
ViolationType::InvalidTypeName,
format!("Type name '{}' is not valid PascalCase", name),
Some(format!("Expected PascalCase, got '{}'", name)),
)
.to_string(),
))
}
}
pub fn validate_function_name(&self, name: &str) -> Result<()> {
if self.function_pattern.is_match(name) {
Ok(())
} else {
Err(Error::invalid_input(
NamingError::new(
ViolationType::InvalidFunctionName,
format!("Function name '{}' is not valid snake_case", name),
Some(
"snake_case: lowercase with underscores (e.g., execute_validate)"
.to_string(),
),
)
.to_string(),
))
}
}
pub fn validate_error_name(&self, name: &str) -> Result<()> {
if self.error_pattern.is_match(name) {
Ok(())
} else {
Err(Error::invalid_input(
NamingError::new(
ViolationType::InvalidErrorName,
format!(
"Error type '{}' does not follow PascalCase + Error pattern",
name
),
Some(
"Format: PascalCase + Error suffix (e.g., ConfigError, ValidationError)"
.to_string(),
),
)
.to_string(),
))
}
}
pub fn validate_command_name(&self, name: &str) -> Result<()> {
if self.command_pattern.is_match(name) {
Ok(())
} else {
Err(Error::invalid_input(
NamingError::new(
ViolationType::InvalidCommandName,
format!("Command name '{}' is not valid kebab-case", name),
Some("kebab-case: lowercase with hyphens (e.g., batch-validate)".to_string()),
)
.to_string(),
))
}
}
pub fn validate_handler_name(&self, name: &str) -> Result<()> {
if self.handler_pattern.is_match(name) {
Ok(())
} else {
Err(Error::invalid_input(
NamingError::new(
ViolationType::InvalidHandlerName,
format!("Handler name '{}' does not follow valid pattern", name),
Some("Valid: handle_X or X_handler (e.g., handle_sync, marketplace_install_handler)".to_string()),
).to_string()
))
}
}
pub fn validate_module_name(&self, name: &str) -> Result<()> {
if self.module_pattern.is_match(name) {
Ok(())
} else {
Err(Error::invalid_input(
NamingError::new(
ViolationType::InvalidModuleName,
format!("Module name '{}' is not valid snake_case", name),
Some("snake_case: lowercase with underscores (e.g., my_module)".to_string()),
)
.to_string(),
))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validate_type_names() {
let validator = NamingValidator::new().unwrap();
assert!(validator.validate_type_name("GenerateFileOptions").is_ok());
assert!(validator.validate_type_name("ValidateInput").is_ok());
assert!(validator.validate_type_name("CodeGraphBuilder").is_ok());
assert!(validator.validate_type_name("MyStruct").is_ok());
assert!(validator.validate_type_name("A").is_ok());
assert!(validator.validate_type_name("generateFileOptions").is_err());
assert!(validator
.validate_type_name("generate_file_options")
.is_err());
assert!(validator.validate_type_name("GENERATEFILEOPTIONS").is_err());
}
#[test]
fn test_validate_function_names() {
let validator = NamingValidator::new().unwrap();
assert!(validator.validate_function_name("execute_validate").is_ok());
assert!(validator.validate_function_name("generate_file").is_ok());
assert!(validator.validate_function_name("parse_manifest").is_ok());
assert!(validator.validate_function_name("with_var").is_ok());
assert!(validator.validate_function_name("foo").is_ok());
assert!(validator.validate_function_name("ExecuteValidate").is_err());
assert!(validator
.validate_function_name("EXECUTE_VALIDATE")
.is_err());
assert!(validator
.validate_function_name("_execute_validate")
.is_err());
}
#[test]
fn test_validate_error_names() {
let validator = NamingValidator::new().unwrap();
assert!(validator.validate_error_name("ConfigError").is_ok());
assert!(validator.validate_error_name("ValidationError").is_ok());
assert!(validator.validate_error_name("NamingError").is_ok());
assert!(validator.validate_error_name("GenerationError").is_ok());
assert!(validator.validate_error_name("ConfigFailure").is_err());
assert!(validator.validate_error_name("config_error").is_err());
assert!(validator.validate_error_name("Error").is_err());
assert!(validator.validate_error_name("ConfigError2").is_err());
}
#[test]
fn test_validate_command_names() {
let validator = NamingValidator::new().unwrap();
assert!(validator.validate_command_name("sync").is_ok());
assert!(validator.validate_command_name("validate").is_ok());
assert!(validator.validate_command_name("batch-validate").is_ok());
assert!(validator.validate_command_name("list").is_ok());
assert!(validator.validate_command_name("Sync").is_err());
assert!(validator.validate_command_name("SYNC").is_err());
assert!(validator.validate_command_name("Sync-Validate").is_err());
assert!(validator.validate_command_name("sync_validate").is_err());
}
#[test]
fn test_validate_handler_names() {
let validator = NamingValidator::new().unwrap();
assert!(validator.validate_handler_name("handle_sync").is_ok());
assert!(validator.validate_handler_name("handle_validate").is_ok());
assert!(validator
.validate_handler_name("handle_marketplace_install")
.is_ok());
assert!(validator
.validate_handler_name("marketplace_install_handler")
.is_ok());
assert!(validator.validate_handler_name("sync_handler").is_ok());
assert!(validator.validate_handler_name("Sync").is_err());
assert!(validator.validate_handler_name("sync").is_err());
assert!(validator.validate_handler_name("handleSync").is_err());
assert!(validator.validate_handler_name("_handle_sync").is_err());
}
#[test]
fn test_validate_module_names() {
let validator = NamingValidator::new().unwrap();
assert!(validator.validate_module_name("audit").is_ok());
assert!(validator.validate_module_name("validation").is_ok());
assert!(validator.validate_module_name("naming").is_ok());
assert!(validator.validate_module_name("my_module").is_ok());
assert!(validator.validate_module_name("MyModule").is_err());
assert!(validator.validate_module_name("MY_MODULE").is_err());
assert!(validator.validate_module_name("my-module").is_err());
}
#[test]
fn test_default_instantiation() {
let _validator = NamingValidator::new().unwrap();
}
}