dampen_core/codegen/
config.rs1use std::path::PathBuf;
7
8#[derive(Debug, Clone)]
10pub struct CodegenConfig {
11 pub output_dir: PathBuf,
13
14 pub format_output: bool,
16
17 pub validate_syntax: bool,
19
20 pub model_type: String,
22
23 pub message_type: String,
25
26 pub persistence: Option<PersistenceConfig>,
28}
29
30#[derive(Debug, Clone)]
32pub struct PersistenceConfig {
33 pub app_name: String,
35}
36
37impl CodegenConfig {
38 pub fn new(output_dir: PathBuf) -> Self {
46 Self {
47 output_dir,
48 format_output: true,
49 validate_syntax: true,
50 model_type: "Model".to_string(),
51 message_type: "Message".to_string(),
52 persistence: None,
53 }
54 }
55
56 pub fn with_persistence(mut self, app_name: impl Into<String>) -> Self {
58 self.persistence = Some(PersistenceConfig {
59 app_name: app_name.into(),
60 });
61 self
62 }
63
64 pub fn with_model_type(mut self, model_type: impl Into<String>) -> Self {
66 self.model_type = model_type.into();
67 self
68 }
69
70 pub fn with_message_type(mut self, message_type: impl Into<String>) -> Self {
72 self.message_type = message_type.into();
73 self
74 }
75
76 pub fn with_formatting(mut self, format_output: bool) -> Self {
78 self.format_output = format_output;
79 self
80 }
81
82 pub fn with_validation(mut self, validate_syntax: bool) -> Self {
84 self.validate_syntax = validate_syntax;
85 self
86 }
87
88 pub fn validate(&self) -> Result<(), String> {
93 if self.output_dir.as_os_str().is_empty() {
95 return Err("Output directory cannot be empty".to_string());
96 }
97
98 if !is_valid_identifier(&self.model_type) {
100 return Err(format!(
101 "Model type '{}' is not a valid Rust identifier",
102 self.model_type
103 ));
104 }
105
106 if !is_valid_identifier(&self.message_type) {
108 return Err(format!(
109 "Message type '{}' is not a valid Rust identifier",
110 self.message_type
111 ));
112 }
113
114 Ok(())
115 }
116}
117
118impl Default for CodegenConfig {
119 fn default() -> Self {
120 Self::new(PathBuf::from("target/generated"))
121 }
122}
123
124impl PersistenceConfig {
125 pub fn new(app_name: impl Into<String>) -> Self {
127 Self {
128 app_name: app_name.into(),
129 }
130 }
131}
132
133fn is_valid_identifier(s: &str) -> bool {
139 if s.is_empty() {
140 return false;
141 }
142
143 let mut chars = s.chars();
144
145 if let Some(first) = chars.next() {
147 if !first.is_uppercase() {
148 return false;
149 }
150 } else {
151 return false;
152 }
153
154 chars.all(|c| c.is_alphanumeric() || c == '_')
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn test_valid_identifiers() {
164 assert!(is_valid_identifier("Model"));
165 assert!(is_valid_identifier("MyModel"));
166 assert!(is_valid_identifier("Model123"));
167 assert!(is_valid_identifier("My_Model"));
168 assert!(is_valid_identifier("M"));
169 }
170
171 #[test]
172 fn test_invalid_identifiers() {
173 assert!(!is_valid_identifier(""));
174 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")); }
180
181 #[test]
182 fn test_config_validation() {
183 let config = CodegenConfig::default();
184 assert!(config.validate().is_ok());
185
186 let config = CodegenConfig::new(PathBuf::from("")).with_model_type("Model");
187 assert!(config.validate().is_err());
188
189 let config = CodegenConfig::new(PathBuf::from("target")).with_model_type("invalid");
190 assert!(config.validate().is_err());
191
192 let config = CodegenConfig::new(PathBuf::from("target")).with_message_type("invalid");
193 assert!(config.validate().is_err());
194 }
195}