use crate::edit_control::ModifiableEdit;
use crate::memory::SymbolicContext;
use crate::ops::{default_operator_registry, SomaOperator};
use anyhow::Result;
use std::collections::HashMap;
use std::io::{self, Write};
pub struct VisualReasoningCLI {
operator_registry: HashMap<String, Box<dyn SomaOperator>>,
analysis_history: Vec<VisualAnalysisResult>,
current_session: VisualSession,
}
#[derive(Debug, Clone)]
pub struct VisualSession {
pub session_id: String,
pub file_path: String,
pub analysis_count: usize,
pub patterns_detected: Vec<String>,
pub confidence_scores: Vec<f64>,
}
#[derive(Debug, Clone)]
pub struct VisualAnalysisResult {
pub analysis_type: String,
pub visual_elements: Vec<String>,
pub relationships: Vec<String>,
pub complexity_score: f64,
pub confidence: f64,
pub symbolic_mapping: SymbolicContext,
pub recommendations: Vec<String>,
pub timestamp: u64,
}
impl Default for VisualReasoningCLI {
fn default() -> Self {
Self::new()
}
}
impl VisualReasoningCLI {
pub fn new() -> Self {
Self {
operator_registry: default_operator_registry(),
analysis_history: Vec::new(),
current_session: VisualSession {
session_id: format!("visual_{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs()),
file_path: String::new(),
analysis_count: 0,
patterns_detected: Vec::new(),
confidence_scores: Vec::new(),
},
}
}
pub fn run_visual_analysis(&mut self, edit: &ModifiableEdit) -> Result<()> {
self.current_session.file_path = edit.base_edit.file.clone();
println!("\n🔬 Visual Reasoning Analysis System");
println!("===================================");
println!("File: {}", edit.base_edit.file);
loop {
match self.show_visual_menu()? {
VisualMenuChoice::CodeStructureAnalysis => {
self.analyze_code_structure(edit)?;
}
VisualMenuChoice::PatternRecognition => {
self.run_pattern_recognition(edit)?;
}
VisualMenuChoice::DependencyMapping => {
self.map_dependencies(edit)?;
}
VisualMenuChoice::VisualDiffAnalysis => {
self.analyze_visual_diff(edit)?;
}
VisualMenuChoice::ASTreeVisualization => {
self.visualize_ast_structure(edit)?;
}
VisualMenuChoice::SessionSummary => {
self.show_session_summary()?;
}
VisualMenuChoice::ExportAnalysis => {
self.export_analysis_results()?;
}
VisualMenuChoice::Back => break,
}
}
Ok(())
}
fn show_visual_menu(&self) -> Result<VisualMenuChoice> {
loop {
println!("\n🎯 Visual Reasoning Options:");
println!(" 1. 🏗️ Code Structure Analysis");
println!(" 2. 🔍 Pattern Recognition");
println!(" 3. 🕸️ Dependency Mapping");
println!(" 4. 📊 Visual Diff Analysis");
println!(" 5. 🌳 AST Tree Visualization");
println!(" 6. 📈 Session Summary");
println!(" 7. 💾 Export Analysis Results");
println!(" 8. ⬅️ Back to Main Menu");
print!("\nSelect visual analysis (1-8): ");
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
match input.trim() {
"1" => return Ok(VisualMenuChoice::CodeStructureAnalysis),
"2" => return Ok(VisualMenuChoice::PatternRecognition),
"3" => return Ok(VisualMenuChoice::DependencyMapping),
"4" => return Ok(VisualMenuChoice::VisualDiffAnalysis),
"5" => return Ok(VisualMenuChoice::ASTreeVisualization),
"6" => return Ok(VisualMenuChoice::SessionSummary),
"7" => return Ok(VisualMenuChoice::ExportAnalysis),
"8" => return Ok(VisualMenuChoice::Back),
_ => println!("❌ Invalid choice. Please try again."),
}
}
}
fn analyze_code_structure(&mut self, edit: &ModifiableEdit) -> Result<()> {
println!("\n🏗️ Code Structure Analysis");
println!("==========================");
let mut context = SymbolicContext::new();
context.set("visual_type", "code_structure");
context.set("analysis_mode", "architecture");
context.set("file_path", &edit.base_edit.file);
context.set("code_content", &edit.compute_final_code());
if let Some(visual_op) = self.operator_registry.get("visual_reasoning") {
let result = visual_op.execute(&context)?;
let analysis = VisualAnalysisResult {
analysis_type: "code_structure".to_string(),
visual_elements: vec!["functions".to_string(), "classes".to_string(), "modules".to_string()],
relationships: vec!["calls".to_string(), "inherits".to_string(), "imports".to_string()],
complexity_score: result.resolve_or_default("visual_complexity", "0.5").parse().unwrap_or(0.5),
confidence: result.resolve_or_default("conversion_confidence", "0.8").parse().unwrap_or(0.8),
symbolic_mapping: result,
recommendations: self.generate_structure_recommendations(&edit.base_edit.file),
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
};
self.display_analysis_result(&analysis);
self.analysis_history.push(analysis);
self.current_session.analysis_count += 1;
}
Ok(())
}
fn run_pattern_recognition(&mut self, edit: &ModifiableEdit) -> Result<()> {
println!("\n🔍 Pattern Recognition Analysis");
println!("==============================");
let code = edit.compute_final_code();
let patterns = self.detect_code_patterns(&code);
println!("📋 Detected Patterns:");
for (i, pattern) in patterns.iter().enumerate() {
println!(" {}. {}", i + 1, pattern);
}
let mut context = SymbolicContext::new();
context.set("visual_type", "pattern_recognition");
context.set("patterns_detected", &patterns.join(","));
context.set("pattern_count", &patterns.len().to_string());
if let Some(cognitive_op) = self.operator_registry.get("cognitive_load") {
let cognitive_result = cognitive_op.execute(&context)?;
println!("\n🧠 Cognitive Pattern Analysis:");
println!(" • Pattern complexity: {}",
cognitive_result.resolve_or_default("load_score", "unknown"));
println!(" • Processing load: {}",
cognitive_result.resolve_or_default("load_level", "unknown"));
}
self.current_session.patterns_detected.extend(patterns);
Ok(())
}
fn map_dependencies(&mut self, edit: &ModifiableEdit) -> Result<()> {
println!("\n🕸️ Dependency Mapping");
println!("=====================");
let dependencies = self.extract_dependencies(&edit.compute_final_code());
println!("📦 Dependencies Found:");
for (i, dep) in dependencies.iter().enumerate() {
println!(" {}. {}", i + 1, dep);
}
self.display_dependency_graph(&dependencies);
Ok(())
}
fn analyze_visual_diff(&mut self, edit: &ModifiableEdit) -> Result<()> {
println!("\n📊 Visual Diff Analysis");
println!("=======================");
let line_range = edit.base_edit.line_range;
let original_lines = line_range.1 - line_range.0 + 1;
let modified_lines = edit.base_edit.new_code.lines().count();
let diff_ratio = if original_lines > 0 {
(modified_lines as f64 - original_lines as f64) / original_lines as f64
} else {
0.0
};
println!("📈 Diff Statistics:");
println!(" • Original lines: {}", original_lines);
println!(" • Modified lines: {}", modified_lines);
println!(" • Change ratio: {:.2}%", diff_ratio * 100.0);
self.display_visual_diff_chart(original_lines, modified_lines);
Ok(())
}
fn visualize_ast_structure(&mut self, edit: &ModifiableEdit) -> Result<()> {
println!("\n🌳 AST Tree Visualization");
println!("=========================");
let ast_nodes = self.parse_ast_structure(&edit.compute_final_code());
println!("🌲 AST Structure:");
for (depth, node) in ast_nodes {
let indent = " ".repeat(depth);
println!("{}├─ {}", indent, node);
}
Ok(())
}
fn show_session_summary(&self) -> Result<()> {
println!("\n📈 Visual Analysis Session Summary");
println!("==================================");
println!("Session ID: {}", self.current_session.session_id);
println!("File: {}", self.current_session.file_path);
println!("Analyses performed: {}", self.current_session.analysis_count);
println!("Patterns detected: {}", self.current_session.patterns_detected.len());
if !self.current_session.confidence_scores.is_empty() {
let avg_confidence: f64 = self.current_session.confidence_scores.iter().sum::<f64>()
/ self.current_session.confidence_scores.len() as f64;
println!("Average confidence: {:.2}", avg_confidence);
}
println!("\n📋 Analysis History:");
for (i, analysis) in self.analysis_history.iter().enumerate() {
println!(" {}. {} (confidence: {:.2})",
i + 1, analysis.analysis_type, analysis.confidence);
}
Ok(())
}
fn export_analysis_results(&self) -> Result<()> {
println!("\n💾 Export Analysis Results");
println!("==========================");
let export_data = serde_json::json!({
"session_id": self.current_session.session_id,
"file_path": self.current_session.file_path,
"analysis_count": self.current_session.analysis_count,
"patterns_detected": self.current_session.patterns_detected,
"analysis_history": self.analysis_history.len()
});
println!("📄 Analysis Export Data:");
println!("{}", serde_json::to_string_pretty(&export_data)?);
Ok(())
}
fn detect_code_patterns(&self, code: &str) -> Vec<String> {
let mut patterns = Vec::new();
if code.contains("fn ") { patterns.push("Function definitions".to_string()); }
if code.contains("struct ") { patterns.push("Struct definitions".to_string()); }
if code.contains("impl ") { patterns.push("Implementation blocks".to_string()); }
if code.contains("match ") { patterns.push("Pattern matching".to_string()); }
if code.contains("if ") { patterns.push("Conditional logic".to_string()); }
if code.contains("for ") || code.contains("while ") {
patterns.push("Loop constructs".to_string());
}
if code.contains("use ") { patterns.push("Import statements".to_string()); }
if code.contains("pub ") { patterns.push("Public interfaces".to_string()); }
patterns
}
fn extract_dependencies(&self, code: &str) -> Vec<String> {
let mut deps = Vec::new();
for line in code.lines() {
let trimmed = line.trim();
if trimmed.starts_with("use ") {
if let Some(dep) = trimmed.strip_prefix("use ") {
if let Some(dep_name) = dep.split("::").next() {
deps.push(dep_name.to_string());
}
}
}
}
deps.sort();
deps.dedup();
deps
}
fn display_dependency_graph(&self, dependencies: &[String]) {
println!("\n📊 Dependency Graph (ASCII):");
println!("┌─ {} ─┐", self.current_session.file_path);
for dep in dependencies {
println!("├── {}", dep);
}
println!("└─────────┘");
}
fn display_visual_diff_chart(&self, original: usize, modified: usize) {
println!("\n📊 Visual Diff Chart:");
let max_val = original.max(modified);
let orig_bars = if max_val > 0 { (original * 20) / max_val } else { 0 };
let mod_bars = if max_val > 0 { (modified * 20) / max_val } else { 0 };
println!("Original: [{}] ({} lines)", "█".repeat(orig_bars), original);
println!("Modified: [{}] ({} lines)", "█".repeat(mod_bars), modified);
}
fn parse_ast_structure(&self, code: &str) -> Vec<(usize, String)> {
let mut ast_nodes = Vec::new();
let mut depth = 0;
for line in code.lines() {
let trimmed = line.trim();
if trimmed.starts_with("fn ") {
ast_nodes.push((depth, format!("Function: {}",
trimmed.split(' ').nth(1).unwrap_or("unknown"))));
depth += 1;
} else if trimmed.starts_with("struct ") {
ast_nodes.push((depth, format!("Struct: {}",
trimmed.split(' ').nth(1).unwrap_or("unknown"))));
depth += 1;
} else if trimmed == "}" && depth > 0 {
depth -= 1;
} else if trimmed.starts_with("let ") {
ast_nodes.push((depth + 1, format!("Variable: {}",
trimmed.split(' ').nth(1).unwrap_or("unknown"))));
}
}
ast_nodes
}
fn generate_structure_recommendations(&self, file_path: &str) -> Vec<String> {
let mut recommendations = Vec::new();
let extension = std::path::Path::new(file_path)
.extension()
.and_then(|ext| ext.to_str())
.unwrap_or("");
match extension {
"rs" => {
recommendations.push("Consider using modules to organize code".to_string());
recommendations.push("Implement proper error handling with Result types".to_string());
recommendations.push("Add comprehensive documentation comments".to_string());
}
"py" => {
recommendations.push("Follow PEP 8 style guidelines".to_string());
recommendations.push("Add type hints for better code clarity".to_string());
recommendations.push("Use docstrings for function documentation".to_string());
}
_ => {
recommendations.push("Maintain consistent coding style".to_string());
recommendations.push("Add appropriate comments and documentation".to_string());
}
}
recommendations
}
fn display_analysis_result(&self, result: &VisualAnalysisResult) {
println!("\n📊 Analysis Results:");
println!(" • Type: {}", result.analysis_type);
println!(" • Elements: {}", result.visual_elements.join(", "));
println!(" • Relationships: {}", result.relationships.join(", "));
println!(" • Complexity: {:.2}", result.complexity_score);
println!(" • Confidence: {:.2}", result.confidence);
println!("\n💡 Recommendations:");
for (i, rec) in result.recommendations.iter().enumerate() {
println!(" {}. {}", i + 1, rec);
}
}
}
#[derive(Debug)]
enum VisualMenuChoice {
CodeStructureAnalysis,
PatternRecognition,
DependencyMapping,
VisualDiffAnalysis,
ASTreeVisualization,
SessionSummary,
ExportAnalysis,
Back,
}
pub fn process_visual_reasoning(edit: ModifiableEdit) -> Result<Result<(), String>> {
let mut visual_cli = VisualReasoningCLI::new();
match visual_cli.run_visual_analysis(&edit) {
Ok(()) => Ok(Ok(())),
Err(e) => Ok(Err(format!("Visual reasoning analysis failed: {}", e))),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::agents::gpt4_agent::ProposedEdit;
#[test]
fn test_visual_reasoning_cli_creation() {
let cli = VisualReasoningCLI::new();
assert!(!cli.operator_registry.is_empty());
assert!(cli.analysis_history.is_empty());
assert_eq!(cli.current_session.analysis_count, 0);
}
#[test]
fn test_visual_session_creation() {
let session = VisualSession {
session_id: "test_session".to_string(),
file_path: "test.rs".to_string(),
analysis_count: 0,
patterns_detected: Vec::new(),
confidence_scores: Vec::new(),
};
assert_eq!(session.session_id, "test_session");
assert_eq!(session.file_path, "test.rs");
assert_eq!(session.analysis_count, 0);
}
#[test]
fn test_visual_analysis_result_creation() {
let result = VisualAnalysisResult {
analysis_type: "test_analysis".to_string(),
visual_elements: vec!["element1".to_string()],
relationships: vec!["rel1".to_string()],
complexity_score: 0.8,
confidence: 0.9,
symbolic_mapping: SymbolicContext::new(),
recommendations: vec!["recommendation1".to_string()],
timestamp: 12345,
};
assert_eq!(result.analysis_type, "test_analysis");
assert_eq!(result.visual_elements.len(), 1);
assert_eq!(result.complexity_score, 0.8);
assert_eq!(result.confidence, 0.9);
}
#[test]
#[ignore] fn test_process_visual_reasoning_function() {
let proposed_edit = ProposedEdit {
file: "test.rs".to_string(),
line_range: (1, 2),
new_code: "// test".to_string(),
reason: "Test".to_string(),
confidence: 0.8,
};
let modifiable_edit = ModifiableEdit::from_proposed_edit(proposed_edit);
let _result = process_visual_reasoning(modifiable_edit);
assert!(true, "Function exists and compiles");
}
#[test]
fn test_pattern_detection_helper() {
let cli = VisualReasoningCLI::new();
let rust_code = "fn main() {\n struct MyStruct;\n impl MyStruct {}\n}";
let patterns = cli.detect_code_patterns(rust_code);
assert!(patterns.contains(&"Function definitions".to_string()));
assert!(patterns.contains(&"Struct definitions".to_string()));
assert!(patterns.contains(&"Implementation blocks".to_string()));
}
#[test]
fn test_dependency_extraction_helper() {
let cli = VisualReasoningCLI::new();
let rust_code = "use std::collections::HashMap;\nuse serde::Serialize;\nfn main() {}";
let deps = cli.extract_dependencies(rust_code);
assert!(deps.contains(&"std".to_string()));
assert!(deps.contains(&"serde".to_string()));
assert_eq!(deps.len(), 2);
}
}