use std::path::PathBuf;
#[derive(Debug, Clone)]
pub struct CodegenConfig {
pub output_dir: PathBuf,
pub format_output: bool,
pub validate_syntax: bool,
pub model_type: String,
pub message_type: String,
pub persistence: Option<PersistenceConfig>,
}
#[derive(Debug, Clone)]
pub struct PersistenceConfig {
pub app_name: String,
}
impl CodegenConfig {
pub fn new(output_dir: PathBuf) -> Self {
Self {
output_dir,
format_output: true,
validate_syntax: true,
model_type: "Model".to_string(),
message_type: "Message".to_string(),
persistence: None,
}
}
pub fn with_persistence(mut self, app_name: impl Into<String>) -> Self {
self.persistence = Some(PersistenceConfig {
app_name: app_name.into(),
});
self
}
pub fn with_model_type(mut self, model_type: impl Into<String>) -> Self {
self.model_type = model_type.into();
self
}
pub fn with_message_type(mut self, message_type: impl Into<String>) -> Self {
self.message_type = message_type.into();
self
}
pub fn with_formatting(mut self, format_output: bool) -> Self {
self.format_output = format_output;
self
}
pub fn with_validation(mut self, validate_syntax: bool) -> Self {
self.validate_syntax = validate_syntax;
self
}
pub fn validate(&self) -> Result<(), String> {
if self.output_dir.as_os_str().is_empty() {
return Err("Output directory cannot be empty".to_string());
}
if !is_valid_identifier(&self.model_type) {
return Err(format!(
"Model type '{}' is not a valid Rust identifier",
self.model_type
));
}
if !is_valid_identifier(&self.message_type) {
return Err(format!(
"Message type '{}' is not a valid Rust identifier",
self.message_type
));
}
Ok(())
}
}
impl Default for CodegenConfig {
fn default() -> Self {
Self::new(PathBuf::from("target/generated"))
}
}
impl PersistenceConfig {
pub fn new(app_name: impl Into<String>) -> Self {
Self {
app_name: app_name.into(),
}
}
}
fn is_valid_identifier(s: &str) -> bool {
if s.is_empty() {
return false;
}
let mut chars = s.chars();
if let Some(first) = chars.next() {
if !first.is_uppercase() {
return false;
}
} else {
return false;
}
chars.all(|c| c.is_alphanumeric() || c == '_')
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_identifiers() {
assert!(is_valid_identifier("Model"));
assert!(is_valid_identifier("MyModel"));
assert!(is_valid_identifier("Model123"));
assert!(is_valid_identifier("My_Model"));
assert!(is_valid_identifier("M"));
}
#[test]
fn test_invalid_identifiers() {
assert!(!is_valid_identifier(""));
assert!(!is_valid_identifier("model")); assert!(!is_valid_identifier("123Model")); assert!(!is_valid_identifier("My-Model")); assert!(!is_valid_identifier("My Model")); assert!(!is_valid_identifier("_Model")); }
#[test]
fn test_config_validation() {
let config = CodegenConfig::default();
assert!(config.validate().is_ok());
let config = CodegenConfig::new(PathBuf::from("")).with_model_type("Model");
assert!(config.validate().is_err());
let config = CodegenConfig::new(PathBuf::from("target")).with_model_type("invalid");
assert!(config.validate().is_err());
let config = CodegenConfig::new(PathBuf::from("target")).with_message_type("invalid");
assert!(config.validate().is_err());
}
}