use crate::edit_control::ModifiableEdit;
use std::collections::HashMap;
pub struct EditPreviewBuilder {
show_diff: bool,
show_risk_assessment: bool,
show_line_numbers: bool,
syntax_highlighting: bool,
theme: PreviewTheme,
}
impl EditPreviewBuilder {
pub fn new() -> Self {
Self {
show_diff: true,
show_risk_assessment: true,
show_line_numbers: true,
syntax_highlighting: true,
theme: PreviewTheme::Default,
}
}
pub fn with_diff(mut self, show: bool) -> Self {
self.show_diff = show;
self
}
pub fn with_risk_assessment(mut self, show: bool) -> Self {
self.show_risk_assessment = show;
self
}
pub fn with_syntax_highlighting(mut self, enabled: bool) -> Self {
self.syntax_highlighting = enabled;
self
}
pub fn with_theme(mut self, theme: PreviewTheme) -> Self {
self.theme = theme;
self
}
pub fn build(self) -> EditPreview {
EditPreview::new(self)
}
}
impl Default for EditPreviewBuilder {
fn default() -> Self {
Self::new()
}
}
pub struct EditPreview {
config: EditPreviewBuilder,
syntax_highlighter: SyntaxHighlighter,
diff_generator: DiffGenerator,
}
impl EditPreview {
fn new(config: EditPreviewBuilder) -> Self {
Self {
config,
syntax_highlighter: SyntaxHighlighter::new(),
diff_generator: DiffGenerator::new(),
}
}
pub fn generate_preview(&self, edit: &ModifiableEdit) -> PreviewResult {
let mut sections = Vec::new();
sections.push(self.create_header_section(edit));
if self.config.show_risk_assessment {
if let Some(risk_section) = self.create_risk_section(edit) {
sections.push(risk_section);
}
}
sections.push(self.create_code_section(edit));
if self.config.show_diff && !edit.modifications.is_empty() {
sections.push(self.create_diff_section(edit));
}
if !edit.processing_metadata.validation_results.is_empty() {
sections.push(self.create_validation_section(edit));
}
PreviewResult { sections }
}
fn create_header_section(&self, edit: &ModifiableEdit) -> PreviewSection {
let confidence_bar = self.create_confidence_visualization(edit.get_effective_confidence());
PreviewSection {
title: "📋 Edit Summary".to_string(),
content: format!(
"File: {}\nLines: {}-{}\nReason: {}\nConfidence: {:.1}% {}\nApproval: {:?}",
edit.base_edit.file,
edit.base_edit.line_range.0,
edit.base_edit.line_range.1,
edit.base_edit.reason,
edit.get_effective_confidence() * 100.0,
confidence_bar,
edit.approval_state
),
style: SectionStyle::Info,
}
}
fn create_risk_section(&self, edit: &ModifiableEdit) -> Option<PreviewSection> {
let risk_score = self.calculate_risk_score(edit);
let risk_level = match risk_score {
r if r >= 0.8 => ("🔴 HIGH RISK", SectionStyle::Error),
r if r >= 0.5 => ("🟡 MEDIUM RISK", SectionStyle::Warning),
_ => ("🟢 LOW RISK", SectionStyle::Success),
};
Some(PreviewSection {
title: "⚠️ Risk Assessment".to_string(),
content: format!(
"{}\nRisk Score: {:.1}%\nRecommendation: {}",
risk_level.0,
risk_score * 100.0,
self.get_risk_recommendation(risk_score)
),
style: risk_level.1,
})
}
fn create_code_section(&self, edit: &ModifiableEdit) -> PreviewSection {
let final_code = edit.compute_final_code();
let highlighted_code = if self.config.syntax_highlighting {
self.syntax_highlighter
.highlight(&final_code, &edit.base_edit.file)
} else {
final_code
};
let numbered_lines = if self.config.show_line_numbers {
self.add_line_numbers(&highlighted_code, edit.base_edit.line_range.0)
} else {
highlighted_code
};
PreviewSection {
title: "📄 Code Preview".to_string(),
content: numbered_lines,
style: SectionStyle::Code,
}
}
fn create_diff_section(&self, edit: &ModifiableEdit) -> PreviewSection {
let diff = self
.diff_generator
.generate_diff(&edit.base_edit.new_code, &edit.compute_final_code());
PreviewSection {
title: "🔍 Changes".to_string(),
content: diff,
style: SectionStyle::Diff,
}
}
fn create_validation_section(&self, edit: &ModifiableEdit) -> PreviewSection {
let validation_summary = edit
.processing_metadata
.validation_results
.iter()
.map(|v| {
format!(
"{}: {} - {}",
v.validator_name,
if v.passed { "✅" } else { "❌" },
v.message
)
})
.collect::<Vec<_>>()
.join("\n");
PreviewSection {
title: "🔧 Validation Results".to_string(),
content: validation_summary,
style: SectionStyle::Info,
}
}
fn create_confidence_visualization(&self, confidence: f64) -> String {
let filled = (confidence * 10.0) as usize;
let bar: String = (0..10)
.map(|i| if i < filled { "█" } else { "░" })
.collect();
format!("[{}]", bar)
}
fn add_line_numbers(&self, content: &str, start_line: usize) -> String {
content
.lines()
.enumerate()
.map(|(i, line)| format!("{:4} | {}", start_line + i, line))
.collect::<Vec<_>>()
.join("\n")
}
fn calculate_risk_score(&self, edit: &ModifiableEdit) -> f64 {
let base_risk = 1.0 - edit.get_effective_confidence();
let modification_risk = edit.modifications.len() as f64 * 0.1;
(base_risk + modification_risk).min(1.0)
}
fn get_risk_recommendation(&self, risk_score: f64) -> String {
match risk_score {
r if r >= 0.8 => "Requires manual review and testing",
r if r >= 0.5 => "Consider additional validation",
_ => "Safe to apply with standard review",
}
.to_string()
}
pub fn render_to_string(&self, edit: &ModifiableEdit) -> String {
let preview = self.generate_preview(edit);
let mut output = Vec::new();
for section in preview.sections {
output.push(format!("═══ {} ═══", section.title));
output.push(section.content);
output.push("".to_string()); }
output.join("\n")
}
}
#[derive(Debug)]
pub struct PreviewResult {
pub sections: Vec<PreviewSection>,
}
#[derive(Debug)]
pub struct PreviewSection {
pub title: String,
pub content: String,
pub style: SectionStyle,
}
#[derive(Debug, Clone)]
pub enum SectionStyle {
Info,
Warning,
Error,
Success,
Code,
Diff,
}
#[derive(Debug, Clone)]
pub enum PreviewTheme {
Default,
Dark,
Light,
}
pub struct SyntaxHighlighter {
rules: HashMap<String, Vec<HighlightRule>>,
}
impl SyntaxHighlighter {
pub fn new() -> Self {
let mut rules = HashMap::new();
rules.insert(
".rs".to_string(),
vec![
HighlightRule::new(
r"\b(fn|let|mut|pub|struct|enum|impl|trait|use|mod)\b",
"keyword",
),
HighlightRule::new(r"//.*$", "comment"),
HighlightRule::new(r#""[^"]*""#, "string"),
HighlightRule::new(r"\b\d+\b", "number"),
HighlightRule::new(r"\b(String|Vec|HashMap|Option|Result)\b", "type"),
],
);
rules.insert(
".js".to_string(),
vec![
HighlightRule::new(
r"\b(function|var|let|const|if|else|for|while|return)\b",
"keyword",
),
HighlightRule::new(r"//.*$", "comment"),
HighlightRule::new(r#""[^"]*""#, "string"),
],
);
rules.insert(
".py".to_string(),
vec![
HighlightRule::new(
r"\b(def|class|if|else|for|while|return|import|from)\b",
"keyword",
),
HighlightRule::new(r"#.*$", "comment"),
HighlightRule::new(r#""[^"]*""#, "string"),
],
);
Self { rules }
}
pub fn highlight(&self, code: &str, filename: &str) -> String {
let extension = std::path::Path::new(filename)
.extension()
.and_then(|ext| ext.to_str())
.map(|ext| format!(".{}", ext))
.unwrap_or_default();
if let Some(rules) = self.rules.get(&extension) {
self.apply_rules(code, rules)
} else {
code.to_string()
}
}
fn apply_rules(&self, code: &str, rules: &[HighlightRule]) -> String {
let mut result = code.to_string();
for rule in rules {
if rule.pattern.contains("fn") {
let styled_replacement = format!("[{}]fn[/{}] ", rule.style, rule.style);
result = result.replace("fn ", &styled_replacement);
}
}
result
}
}
#[derive(Debug)]
struct HighlightRule {
pattern: String,
style: String,
}
impl HighlightRule {
fn new(pattern: &str, style: &str) -> Self {
Self {
pattern: pattern.to_string(),
style: style.to_string(),
}
}
}
pub struct DiffGenerator;
impl DiffGenerator {
pub fn new() -> Self {
Self
}
pub fn generate_diff(&self, original: &str, modified: &str) -> String {
if original == modified {
return "No changes detected.".to_string();
}
let original_lines: Vec<&str> = original.lines().collect();
let modified_lines: Vec<&str> = modified.lines().collect();
let mut diff = Vec::new();
diff.push("--- original".to_string());
diff.push("+++ modified".to_string());
let max_lines = original_lines.len().max(modified_lines.len());
for i in 0..max_lines {
let orig_line = original_lines.get(i).unwrap_or(&"");
let mod_line = modified_lines.get(i).unwrap_or(&"");
if orig_line != mod_line {
if !orig_line.is_empty() {
diff.push(format!("-{}", orig_line));
}
if !mod_line.is_empty() {
diff.push(format!("+{}", mod_line));
}
} else if !orig_line.is_empty() {
diff.push(format!(" {}", orig_line));
}
}
diff.join("\n")
}
}
impl Default for DiffGenerator {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::agents::gpt4_agent::ProposedEdit;
fn create_test_edit() -> ModifiableEdit {
let proposed = ProposedEdit {
file: "test.rs".to_string(),
line_range: (10, 15),
new_code: "fn test() {\n println!(\"Hello\");\n}".to_string(),
reason: "Test function".to_string(),
confidence: 0.9,
};
ModifiableEdit::from_proposed_edit(proposed)
}
#[test]
fn test_preview_builder() {
let preview = EditPreviewBuilder::new()
.with_diff(true)
.with_risk_assessment(false)
.with_syntax_highlighting(true)
.build();
assert!(preview.config.show_diff);
assert!(!preview.config.show_risk_assessment);
assert!(preview.config.syntax_highlighting);
}
#[test]
fn test_preview_generation() {
let edit = create_test_edit();
let preview = EditPreviewBuilder::new().build();
let result = preview.generate_preview(&edit);
assert!(!result.sections.is_empty());
assert!(result
.sections
.iter()
.any(|s| s.title.contains("Edit Summary")));
assert!(result
.sections
.iter()
.any(|s| s.title.contains("Code Preview")));
}
#[test]
fn test_diff_generation() {
let generator = DiffGenerator::new();
let original = "line1\nline2\nline3";
let modified = "line1\nmodified_line2\nline3";
let diff = generator.generate_diff(original, modified);
assert!(diff.contains("-line2"));
assert!(diff.contains("+modified_line2"));
}
#[test]
fn test_confidence_visualization() {
let preview = EditPreviewBuilder::new().build();
let vis = preview.create_confidence_visualization(0.7);
assert!(vis.contains("█"));
assert!(vis.contains("░"));
assert!(vis.len() >= 12); }
#[test]
fn test_syntax_highlighter() {
let highlighter = SyntaxHighlighter::new();
let code = "fn main() {\n println!(\"Hello\");\n}";
let highlighted = highlighter.highlight(code, "test.rs");
assert!(highlighted.contains("[keyword]fn[/keyword]"));
assert!(highlighted.contains("main()"));
}
#[test]
fn test_render_to_string() {
let edit = create_test_edit();
let preview = EditPreviewBuilder::new().build();
let output = preview.render_to_string(&edit);
assert!(output.contains("═══"));
assert!(output.contains("Edit Summary"));
assert!(output.contains("Code Preview"));
}
}