use crate::{OverrideEntry, OverrideError, OverrideSystem, ResolutionStrategy, Result};
use colored::*;
use std::path::Path;
pub struct OverrideInspector {
system: OverrideSystem,
}
impl OverrideInspector {
pub fn new(workspace_path: &Path) -> Result<Self> {
let system = OverrideSystem::new(workspace_path)?;
Ok(Self { system })
}
pub fn list_all_detailed(&self) -> Result<()> {
let overrides = self.system.list_all()?;
if overrides.is_empty() {
println!("{}", "No overrides found.".yellow());
return Ok(());
}
println!("{}", format!("Found {} overrides:", overrides.len()).bold());
println!();
for (idx, entry) in overrides.iter().enumerate() {
self.print_override_entry(idx + 1, entry);
}
Ok(())
}
pub fn list_by_file(&self, file_path: &Path) -> Result<()> {
let overrides = self.system.list_by_file(file_path)?;
if overrides.is_empty() {
println!(
"{}",
format!("No overrides found for file: {}", file_path.display()).yellow()
);
return Ok(());
}
println!(
"{}",
format!(
"Found {} overrides for {}:",
overrides.len(),
file_path.display()
)
.bold()
);
println!();
for (idx, entry) in overrides.iter().enumerate() {
self.print_override_entry(idx + 1, entry);
}
Ok(())
}
pub fn inspect_override(&self, key: &str) -> Result<()> {
let overrides = self.system.list_all()?;
let entry = overrides
.iter()
.find(|e| e.key.primary == key || e.key.fallbacks.contains(&key.to_string()))
.ok_or_else(|| OverrideError::OverrideNotFound(key.to_string()))?;
println!("{}", "Override Details:".bold().blue());
println!();
println!("{}", "Keys:".bold());
println!(" Primary: {}", entry.key.primary.green());
if !entry.key.fallbacks.is_empty() {
println!(" Fallbacks:");
for fallback in &entry.key.fallbacks {
println!(" - {}", fallback.cyan());
}
}
println!();
println!("{}", "Metadata:".bold());
println!(
" File: {}",
entry.metadata.file_path.display().to_string().blue()
);
if let Some(func_name) = &entry.metadata.function_name {
println!(" Function: {}", func_name.yellow());
}
if let Some(line) = entry.metadata.original_line {
println!(" Original Line: {}", line.to_string().magenta());
}
println!(
" Created: {}",
entry.metadata.created_at.format("%Y-%m-%d %H:%M:%S UTC")
);
println!(
" Modified: {}",
entry.metadata.modified_at.format("%Y-%m-%d %H:%M:%S UTC")
);
if let Some(notes) = &entry.metadata.notes {
println!(" Notes: {notes}");
}
println!();
println!("{}", "Override Configuration:".bold());
println!(" Key: {}", entry.override_config.key.green());
if !entry.override_config.env.is_empty() {
println!(" Environment Variables:");
for (key, value) in &entry.override_config.env {
println!(" {} = {}", key.cyan(), value);
}
}
if !entry.override_config.cargo_options.is_empty() {
println!(" Cargo Options:");
for opt in &entry.override_config.cargo_options {
println!(" {opt}");
}
}
if !entry.override_config.rustc_options.is_empty() {
println!(" Rustc Options:");
for opt in &entry.override_config.rustc_options {
println!(" {opt}");
}
}
if !entry.override_config.args.is_empty() {
println!(" Additional Arguments:");
for arg in &entry.override_config.args {
println!(" {arg}");
}
}
Ok(())
}
pub fn debug_resolution(
&mut self,
file_path: &Path,
line: usize,
column: Option<usize>,
) -> Result<()> {
let context = self.system.get_function_context(file_path, line, column)?;
println!("{}", "Resolution Debug Information:".bold().blue());
println!();
println!("{}", "Context:".bold());
println!(" File: {}", context.file_path.display().to_string().blue());
println!(" Line: {}", line.to_string().magenta());
if let Some(col) = column {
println!(" Column: {}", col.to_string().magenta());
}
if let Some(func_name) = &context.function_name {
println!(" Detected Function: {}", func_name.yellow());
} else {
println!(" Detected Function: {}", "None".red());
}
println!();
let candidates = self.system.debug_resolution(&context)?;
if candidates.is_empty() {
println!("{}", "No resolution candidates found.".yellow());
return Ok(());
}
println!(
"{}",
format!("Found {} resolution candidates:", candidates.len()).bold()
);
println!();
for (idx, (entry, strategy, score)) in candidates.iter().enumerate() {
println!("{}. {} (score: {:.2})", idx + 1, entry.key.primary, score);
println!(" Strategy: {}", self.format_strategy(strategy));
println!(
" Function: {}",
entry.metadata.function_name.as_deref().unwrap_or("N/A")
);
if let Some(line) = entry.metadata.original_line {
println!(" Original Line: {line}");
}
println!();
}
if let Some((override_config, strategy)) = self.system.resolve_with_strategy(&context)? {
println!("{}", "Final Resolution:".bold().green());
println!(" Override Key: {}", override_config.key);
println!(" Strategy: {}", self.format_strategy(&strategy));
} else {
println!("{}", "No override resolved.".red());
}
Ok(())
}
pub fn export_overrides(&self, output_path: Option<&Path>) -> Result<()> {
let export_data = self.system.export()?;
if let Some(path) = output_path {
std::fs::write(path, &export_data)?;
println!(
"{}",
format!("Exported overrides to: {}", path.display()).green()
);
} else {
println!("{}", "Exported Override Data:".bold());
println!("{export_data}");
}
Ok(())
}
pub fn show_statistics(&self) -> Result<()> {
let overrides = self.system.list_all()?;
println!("{}", "Override Statistics:".bold().blue());
println!();
println!("Total Overrides: {}", overrides.len());
let mut file_counts = std::collections::HashMap::new();
for entry in &overrides {
*file_counts
.entry(entry.metadata.file_path.display().to_string())
.or_insert(0) += 1;
}
println!();
println!("{}", "Overrides by File:".bold());
for (file, count) in file_counts.iter() {
println!(" {}: {}", file.blue(), count);
}
let function_count = overrides
.iter()
.filter(|e| e.metadata.function_name.is_some())
.count();
let file_level_count = overrides.len() - function_count;
println!();
println!("{}", "Override Types:".bold());
println!(" Function-level: {function_count}");
println!(" File-level: {file_level_count}");
let mut env_vars = std::collections::HashSet::new();
for entry in &overrides {
for key in entry.override_config.env.keys() {
env_vars.insert(key.clone());
}
}
if !env_vars.is_empty() {
println!();
println!("{}", "Environment Variables Used:".bold());
for var in env_vars {
println!(" - {}", var.cyan());
}
}
Ok(())
}
fn print_override_entry(&self, idx: usize, entry: &OverrideEntry) {
println!("{}. {}", idx, entry.key.primary.green());
println!(
" File: {}",
entry.metadata.file_path.display().to_string().blue()
);
if let Some(func_name) = &entry.metadata.function_name {
println!(" Function: {}", func_name.yellow());
}
if let Some(line) = entry.metadata.original_line {
println!(" Line: {}", line.to_string().magenta());
}
println!(" Override: {}", entry.override_config.key);
println!();
}
fn format_strategy(&self, strategy: &ResolutionStrategy) -> colored::ColoredString {
match strategy {
ResolutionStrategy::Exact => "Exact Key Match".green(),
ResolutionStrategy::Fallback => "Fallback Key".cyan(),
ResolutionStrategy::FuzzyFunction => "Fuzzy Function Match".yellow(),
ResolutionStrategy::LineProximity => "Line Proximity".magenta(),
ResolutionStrategy::HashFallback => "Hash Fallback".red(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{CommandOverride, FunctionContext};
use tempfile::TempDir;
#[test]
fn test_inspector_list_all() {
let temp_dir = TempDir::new().unwrap();
let mut system = OverrideSystem::new(temp_dir.path()).unwrap();
let context1 = FunctionContext {
file_path: "src/main.rs".into(),
function_name: Some("main".to_string()),
line_number: 10,
context: None,
};
let key1 = system.generate_key(&context1).unwrap();
let mut override1 = CommandOverride::new("test1".to_string());
override1.env.insert("DEBUG".to_string(), "1".to_string());
system.save_override(key1, override1, &context1).unwrap();
let inspector = OverrideInspector::new(temp_dir.path()).unwrap();
inspector.list_all_detailed().unwrap();
}
#[test]
fn test_inspector_debug_resolution() {
let temp_dir = TempDir::new().unwrap();
let mut system = OverrideSystem::new(temp_dir.path()).unwrap();
let test_file = temp_dir.path().join("test.rs");
std::fs::write(
&test_file,
r#"
fn process_data(input: &str) -> String {
input.to_uppercase()
}
fn main() {
let result = process_data("hello");
println!("{}", result);
}
"#,
)
.unwrap();
let context = FunctionContext {
file_path: test_file.clone(),
function_name: Some("process_data".to_string()),
line_number: 2,
context: None,
};
let key = system.generate_key(&context).unwrap();
let override_config = CommandOverride::new("debug-test".to_string());
system
.save_override(key, override_config, &context)
.unwrap();
let mut inspector = OverrideInspector::new(temp_dir.path()).unwrap();
inspector.debug_resolution(&test_file, 2, None).unwrap();
}
}