execution_engine/
config.rs1use serde::{Deserialize, Serialize};
7use std::path::PathBuf;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11pub enum OversizedOutputStrategy {
12 TruncateWithWarning,
14 FailExecution,
16 StreamToFile,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ExecutionConfig {
23 pub default_timeout_ms: u64,
25
26 pub max_timeout_ms: u64,
28
29 pub stream_output: bool,
31
32 pub log_dir: Option<PathBuf>,
34
35 pub max_concurrent_executions: usize,
37
38 pub max_in_memory_executions: usize,
40
41 pub execution_retention_secs: u64,
43
44 pub enable_auto_cleanup: bool,
46
47 pub max_output_size_bytes: usize,
49
50 pub oversized_output_strategy: OversizedOutputStrategy,
52}
53
54impl Default for ExecutionConfig {
55 fn default() -> Self {
56 Self {
57 default_timeout_ms: 300_000, max_timeout_ms: 3_600_000, stream_output: true,
60 log_dir: None,
61 max_concurrent_executions: 100,
62 max_in_memory_executions: 1_000,
63 execution_retention_secs: 3_600, enable_auto_cleanup: true,
65 max_output_size_bytes: 10_485_760, oversized_output_strategy: OversizedOutputStrategy::TruncateWithWarning,
67 }
68 }
69}
70
71impl ExecutionConfig {
72 pub fn validate(&self) -> Result<(), String> {
87 if self.default_timeout_ms == 0 {
88 return Err("default_timeout_ms must be greater than 0".to_string());
89 }
90
91 if self.max_timeout_ms == 0 {
92 return Err("max_timeout_ms must be greater than 0".to_string());
93 }
94
95 if self.default_timeout_ms > self.max_timeout_ms {
96 return Err("default_timeout_ms cannot exceed max_timeout_ms".to_string());
97 }
98
99 if self.max_concurrent_executions == 0 {
100 return Err("max_concurrent_executions must be greater than 0".to_string());
101 }
102
103 if self.max_in_memory_executions == 0 {
104 return Err("max_in_memory_executions must be greater than 0".to_string());
105 }
106
107 if self.execution_retention_secs == 0 {
108 return Err("execution_retention_secs must be greater than 0".to_string());
109 }
110
111 if self.max_output_size_bytes == 0 {
112 return Err("max_output_size_bytes must be greater than 0".to_string());
113 }
114
115 Ok(())
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_default_config() {
125 let config = ExecutionConfig::default();
126 assert_eq!(config.default_timeout_ms, 300_000);
127 assert_eq!(config.max_timeout_ms, 3_600_000);
128 assert!(config.stream_output);
129 assert!(config.log_dir.is_none());
130 assert_eq!(config.max_concurrent_executions, 100);
131 assert_eq!(config.max_in_memory_executions, 1_000);
132 assert_eq!(config.execution_retention_secs, 3_600);
133 assert!(config.enable_auto_cleanup);
134 assert_eq!(config.max_output_size_bytes, 10_485_760);
135 assert_eq!(
136 config.oversized_output_strategy,
137 OversizedOutputStrategy::TruncateWithWarning
138 );
139 }
140
141 #[test]
142 fn test_validate_success() {
143 let config = ExecutionConfig::default();
144 assert!(config.validate().is_ok());
145 }
146
147 #[test]
148 fn test_validate_default_timeout_zero() {
149 let mut config = ExecutionConfig::default();
150 config.default_timeout_ms = 0;
151 assert!(config.validate().is_err());
152 assert_eq!(
153 config.validate().unwrap_err(),
154 "default_timeout_ms must be greater than 0"
155 );
156 }
157
158 #[test]
159 fn test_validate_max_timeout_zero() {
160 let mut config = ExecutionConfig::default();
161 config.max_timeout_ms = 0;
162 assert!(config.validate().is_err());
163 assert_eq!(
164 config.validate().unwrap_err(),
165 "max_timeout_ms must be greater than 0"
166 );
167 }
168
169 #[test]
170 fn test_validate_default_exceeds_max() {
171 let mut config = ExecutionConfig::default();
172 config.default_timeout_ms = 1_000_000;
173 config.max_timeout_ms = 500_000;
174 assert!(config.validate().is_err());
175 assert_eq!(
176 config.validate().unwrap_err(),
177 "default_timeout_ms cannot exceed max_timeout_ms"
178 );
179 }
180
181 #[test]
182 fn test_validate_max_concurrent_zero() {
183 let mut config = ExecutionConfig::default();
184 config.max_concurrent_executions = 0;
185 assert!(config.validate().is_err());
186 assert_eq!(
187 config.validate().unwrap_err(),
188 "max_concurrent_executions must be greater than 0"
189 );
190 }
191
192 #[test]
193 fn test_validate_max_in_memory_zero() {
194 let mut config = ExecutionConfig::default();
195 config.max_in_memory_executions = 0;
196 assert!(config.validate().is_err());
197 assert_eq!(
198 config.validate().unwrap_err(),
199 "max_in_memory_executions must be greater than 0"
200 );
201 }
202
203 #[test]
204 fn test_validate_retention_zero() {
205 let mut config = ExecutionConfig::default();
206 config.execution_retention_secs = 0;
207 assert!(config.validate().is_err());
208 assert_eq!(
209 config.validate().unwrap_err(),
210 "execution_retention_secs must be greater than 0"
211 );
212 }
213
214 #[test]
215 fn test_validate_max_output_size_zero() {
216 let mut config = ExecutionConfig::default();
217 config.max_output_size_bytes = 0;
218 assert!(config.validate().is_err());
219 assert_eq!(
220 config.validate().unwrap_err(),
221 "max_output_size_bytes must be greater than 0"
222 );
223 }
224
225 #[test]
226 fn test_oversized_output_strategy_equality() {
227 assert_eq!(
228 OversizedOutputStrategy::TruncateWithWarning,
229 OversizedOutputStrategy::TruncateWithWarning
230 );
231 assert_ne!(
232 OversizedOutputStrategy::TruncateWithWarning,
233 OversizedOutputStrategy::FailExecution
234 );
235 assert_ne!(
236 OversizedOutputStrategy::FailExecution,
237 OversizedOutputStrategy::StreamToFile
238 );
239 }
240
241 #[test]
242 fn test_config_clone() {
243 let config = ExecutionConfig::default();
244 let cloned = config.clone();
245 assert_eq!(config.default_timeout_ms, cloned.default_timeout_ms);
246 assert_eq!(config.max_timeout_ms, cloned.max_timeout_ms);
247 }
248}