1use crate::{
2 CommandOverride, OverrideEntry, OverrideMetadata, ValidationStatus,
3 detector::FunctionDetector,
4 error::{OverrideError, Result},
5 key::{FunctionContext, OverrideKey},
6 preview::OverridePreview,
7 resolver::{OverrideResolver, ResolutionStrategy},
8 storage::OverrideStorage,
9};
10use std::path::{Path, PathBuf};
11
12pub struct PreviewParams<'a> {
14 pub override_config: &'a CommandOverride,
15 pub context: &'a FunctionContext,
16 pub base_command: &'a str,
17 pub base_env: &'a indexmap::IndexMap<String, String>,
18 pub base_cargo_options: &'a [String],
19 pub base_rustc_options: &'a [String],
20 pub base_args: &'a [String],
21}
22
23pub struct OverrideSystem {
25 workspace_path: PathBuf,
26 pub(crate) storage: OverrideStorage,
27 detector: FunctionDetector,
28}
29
30impl OverrideSystem {
31 #[cfg(test)]
33 pub fn new_for_test(workspace_path: &Path) -> Result<Self> {
34 let storage = OverrideStorage::new_for_test(workspace_path)?;
35 let detector = FunctionDetector::new()?;
36
37 Ok(Self {
38 workspace_path: workspace_path.to_path_buf(),
39 storage,
40 detector,
41 })
42 }
43
44 pub fn new(workspace_path: &Path) -> Result<Self> {
46 let storage = OverrideStorage::new(workspace_path)?;
47 let detector = FunctionDetector::new()?;
48
49 Ok(Self {
50 workspace_path: workspace_path.to_path_buf(),
51 storage,
52 detector,
53 })
54 }
55
56 pub fn generate_key(&mut self, context: &FunctionContext) -> Result<OverrideKey> {
58 let enhanced_context = if context.function_name.is_none() {
60 if let Ok(source) = std::fs::read_to_string(&context.file_path) {
61 if let Some(func_info) = self
62 .detector
63 .find_function_at_line(&source, context.line_number)?
64 {
65 FunctionContext {
66 function_name: Some(func_info.name),
67 ..context.clone()
68 }
69 } else {
70 context.clone()
71 }
72 } else {
73 context.clone()
74 }
75 } else {
76 context.clone()
77 };
78
79 OverrideKey::new(&enhanced_context)
80 }
81
82 pub fn save_override(
84 &self,
85 key: OverrideKey,
86 override_config: CommandOverride,
87 context: &FunctionContext,
88 ) -> Result<()> {
89 let entry = OverrideEntry {
90 key,
91 override_config,
92 metadata: OverrideMetadata {
93 created_at: chrono::Utc::now(),
94 modified_at: chrono::Utc::now(),
95 file_path: context.file_path.clone(),
96 function_name: context.function_name.clone(),
97 original_line: Some(context.line_number),
98 notes: None,
99 validation_status: crate::ValidationStatus::Pending,
100 last_execution_time: None,
101 last_execution_success: None,
102 failure_count: 0,
103 },
104 };
105
106 self.storage.save(&entry)
107 }
108
109 pub fn save_override_with_validation(
111 &self,
112 key: OverrideKey,
113 override_config: CommandOverride,
114 context: &FunctionContext,
115 command: &str,
116 ) -> Result<()> {
117 let entry = OverrideEntry {
119 key,
120 override_config: override_config.clone(),
121 metadata: OverrideMetadata {
122 created_at: chrono::Utc::now(),
123 modified_at: chrono::Utc::now(),
124 file_path: context.file_path.clone(),
125 function_name: context.function_name.clone(),
126 original_line: Some(context.line_number),
127 notes: None,
128 validation_status: crate::ValidationStatus::Pending,
129 last_execution_time: None,
130 last_execution_success: None,
131 failure_count: 0,
132 },
133 };
134
135 self.storage.save_with_validation(&entry, |entry| {
137 use crate::parser::OverrideParser;
138
139 let parser = OverrideParser::new(command);
141
142 let mut parsed = crate::parser::ParsedOverride::new();
144
145 for (k, v) in &entry.override_config.env {
147 parsed.env.insert(k.clone(), v.clone());
148 }
149
150 for opt in &entry.override_config.cargo_options {
152 let parts: Vec<&str> = opt.splitn(2, ' ').collect();
153 if parts.len() == 2 {
154 parsed.options.insert(
155 parts[0].to_string(),
156 crate::parser::OptionValue::Single(parts[1].to_string()),
157 );
158 } else {
159 parsed
160 .options
161 .insert(opt.clone(), crate::parser::OptionValue::Flag(true));
162 }
163 }
164
165 parsed.extra_args = entry.override_config.args.clone();
167
168 parser.validate(&parsed)
170 })
171 }
172
173 pub fn resolve_override(
175 &mut self,
176 context: &FunctionContext,
177 ) -> Result<Option<CommandOverride>> {
178 let mut resolver = OverrideResolver::new(OverrideStorage::new(&self.workspace_path)?)?;
179 match resolver.resolve(context)? {
180 Some((entry, _strategy)) => Ok(Some(entry.override_config)),
181 None => Ok(None),
182 }
183 }
184
185 #[cfg(test)]
187 pub fn resolve_override_with_storage(
188 &mut self,
189 context: &FunctionContext,
190 ) -> Result<Option<CommandOverride>> {
191 let entries = self.storage.list_all()?;
193 for entry in entries {
194 if entry.metadata.file_path == context.file_path {
195 if let Some(ref function_name) = context.function_name {
196 if entry.metadata.function_name.as_ref() == Some(function_name) {
197 return Ok(Some(entry.override_config));
198 }
199 } else if entry.metadata.original_line == Some(context.line_number) {
200 return Ok(Some(entry.override_config));
201 }
202 }
203 }
204 Ok(None)
205 }
206
207 pub fn resolve_with_strategy(
209 &mut self,
210 context: &FunctionContext,
211 ) -> Result<Option<(CommandOverride, ResolutionStrategy)>> {
212 let mut resolver = OverrideResolver::new(OverrideStorage::new(&self.workspace_path)?)?;
213 match resolver.resolve(context)? {
214 Some((entry, strategy)) => Ok(Some((entry.override_config, strategy))),
215 None => Ok(None),
216 }
217 }
218
219 pub fn preview_override(&self, params: PreviewParams) -> Result<OverridePreview> {
221 let key = OverrideKey::new(params.context)?;
222
223 let entry = OverrideEntry {
224 key,
225 override_config: params.override_config.clone(),
226 metadata: OverrideMetadata {
227 created_at: chrono::Utc::now(),
228 modified_at: chrono::Utc::now(),
229 file_path: params.context.file_path.clone(),
230 function_name: params.context.function_name.clone(),
231 original_line: Some(params.context.line_number),
232 notes: None,
233 validation_status: ValidationStatus::Pending,
234 failure_count: 0,
235 last_execution_success: None,
236 last_execution_time: None,
237 },
238 };
239
240 OverridePreview::new(
241 entry,
242 params.base_command,
243 params.base_env,
244 params.base_cargo_options,
245 params.base_rustc_options,
246 params.base_args,
247 )
248 }
249
250 pub fn list_all(&self) -> Result<Vec<OverrideEntry>> {
252 self.storage.list_all()
253 }
254
255 pub fn list_by_file(&self, file_path: &Path) -> Result<Vec<OverrideEntry>> {
257 self.storage.get_by_file(file_path)
258 }
259
260 pub fn delete_override(&self, key: &str) -> Result<bool> {
262 self.storage.delete(key)
263 }
264
265 pub fn clear_all(&self) -> Result<()> {
267 self.storage.clear()
268 }
269
270 pub fn clear_by_file(&self, file_path: &Path) -> Result<usize> {
272 self.storage.clear_by_file(file_path)
273 }
274
275 pub fn export(&self) -> Result<String> {
277 self.storage.export()
278 }
279
280 pub fn import(&self, data: &str) -> Result<usize> {
282 self.storage.import(data)
283 }
284
285 pub fn debug_resolution(
287 &mut self,
288 context: &FunctionContext,
289 ) -> Result<Vec<(OverrideEntry, ResolutionStrategy, f32)>> {
290 let mut resolver = OverrideResolver::new(OverrideStorage::new(&self.workspace_path)?)?;
291 resolver.get_resolution_candidates(context)
292 }
293
294 pub fn get_function_context(
296 &mut self,
297 file_path: &Path,
298 line: usize,
299 column: Option<usize>,
300 ) -> Result<FunctionContext> {
301 let source = std::fs::read_to_string(file_path)?;
302
303 let function_name = self
304 .detector
305 .find_function_at_line(&source, line)?
306 .map(|f| f.name);
307
308 Ok(FunctionContext {
309 file_path: file_path.to_path_buf(),
310 function_name,
311 line_number: line,
312 context: column.map(|c| format!("column:{c}")),
313 })
314 }
315
316 pub fn update_execution_status(&self, key: &str, success: bool) -> Result<()> {
318 self.storage.update_execution_status(key, success)
319 }
320
321 pub fn rollback_to_last_backup(&self) -> Result<()> {
323 self.storage.rollback_to_last_backup()
324 }
325
326 pub fn get_problematic_overrides(&self, failure_threshold: u32) -> Result<Vec<OverrideEntry>> {
328 let all_overrides = self.list_all()?;
329 Ok(all_overrides
330 .into_iter()
331 .filter(|entry| entry.metadata.failure_count >= failure_threshold)
332 .collect())
333 }
334
335 pub fn should_auto_rollback(&self, key: &str, threshold: u32) -> Result<bool> {
337 if let Some(entry) = self.storage.get_by_primary_key(key)? {
338 Ok(entry.metadata.failure_count >= threshold)
339 } else {
340 Ok(false)
341 }
342 }
343}
344
345pub fn create_override_system() -> Result<OverrideSystem> {
347 let cwd = std::env::current_dir().map_err(|e| {
348 OverrideError::StorageError(format!("Failed to get current directory: {e}"))
349 })?;
350 OverrideSystem::new(&cwd)
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356 use tempfile::TempDir;
357
358 #[test]
359 fn test_override_system_workflow() {
360 let temp_dir = TempDir::new().unwrap();
361 let mut system = OverrideSystem::new_for_test(temp_dir.path()).unwrap();
362
363 let context = FunctionContext {
365 file_path: PathBuf::from("src/main.rs"),
366 function_name: Some("handle_request".to_string()),
367 line_number: 42,
368 context: None,
369 };
370
371 let key = system.generate_key(&context).unwrap();
373 assert_eq!(key.primary, "src/main.rs:handle_request");
374
375 let mut override_config = CommandOverride::new("test".to_string());
377 override_config
378 .env
379 .insert("RUST_LOG".to_string(), "debug".to_string());
380
381 system
382 .save_override(key.clone(), override_config.clone(), &context)
383 .unwrap();
384
385 let resolved = system.resolve_override_with_storage(&context).unwrap();
387 assert!(resolved.is_some());
388
389 let resolved_config = resolved.unwrap();
390 assert_eq!(resolved_config.key, "test");
391 assert_eq!(
392 resolved_config.env.get("RUST_LOG"),
393 Some(&"debug".to_string())
394 );
395
396 let all_overrides = system.list_all().unwrap();
398 assert_eq!(all_overrides.len(), 1);
399
400 assert!(system.delete_override(&key.primary).unwrap());
402
403 let resolved_after = system.resolve_override_with_storage(&context).unwrap();
405 assert!(resolved_after.is_none());
406 }
407
408 #[test]
409 fn test_function_detection_integration() {
410 let temp_dir = TempDir::new().unwrap();
411 let mut system = OverrideSystem::new_for_test(temp_dir.path()).unwrap();
412
413 let test_file = temp_dir.path().join("test.rs");
415 std::fs::write(
416 &test_file,
417 r#"
418fn process_data(input: &str) -> String {
419 input.to_uppercase()
420}
421
422fn main() {
423 let result = process_data("hello");
424 println!("{}", result);
425}
426"#,
427 )
428 .unwrap();
429
430 let context = system.get_function_context(&test_file, 2, None).unwrap();
432 assert_eq!(context.function_name.as_deref(), Some("process_data"));
433
434 let context = system.get_function_context(&test_file, 6, None).unwrap();
436 assert_eq!(context.function_name.as_deref(), Some("main"));
437 }
438}