1use anyhow::{Context, Result};
2use regex::Regex;
3use std::collections::{HashMap, HashSet};
4use std::fs;
5use std::path::PathBuf;
6
7use crate::types::Match;
8
9pub fn apply_changes(matches: &[Match], uncomment: bool) -> Result<()> {
10 let mut files_map: HashMap<PathBuf, Vec<&Match>> = HashMap::new();
12
13 for m in matches {
14 files_map.entry(m.file_path.clone()).or_default().push(m);
15 }
16
17 for (file_path, file_matches) in files_map {
18 let content = fs::read_to_string(&file_path)?;
19 let mut lines: Vec<String> = content.lines().map(|s| s.to_string()).collect();
20
21 let mut sorted_matches = file_matches;
23 sorted_matches.sort_by(|a, b| b.line_number.cmp(&a.line_number));
24
25 for m in sorted_matches {
26 let idx = m.line_number - 1;
27 if idx < lines.len() {
28 if uncomment {
29 lines[idx] = uncomment_line(&lines[idx]);
31 } else {
32 lines[idx] = comment_line(&lines[idx]);
34 }
35 }
36 }
37
38 let new_content = lines.join("\n") + "\n";
39 fs::write(&file_path, new_content)
40 .with_context(|| format!("Failed to write file: {}", file_path.display()))?;
41 }
42
43 Ok(())
44}
45
46pub fn delete_changes(matches: &[Match]) -> Result<()> {
47 let mut files_map: HashMap<PathBuf, Vec<&Match>> = HashMap::new();
49
50 for m in matches {
51 files_map.entry(m.file_path.clone()).or_default().push(m);
52 }
53
54 for (file_path, file_matches) in files_map {
55 let content = fs::read_to_string(&file_path)?;
56 let lines: Vec<String> = content.lines().map(|s| s.to_string()).collect();
57
58 let mut sorted_matches = file_matches;
60 sorted_matches.sort_by(|a, b| b.line_number.cmp(&a.line_number));
61
62 let mut lines_to_delete: HashSet<usize> = HashSet::new();
64 for m in sorted_matches {
65 lines_to_delete.insert(m.line_number - 1);
66 }
67
68 let new_lines: Vec<String> = lines
70 .into_iter()
71 .enumerate()
72 .filter(|(idx, _)| !lines_to_delete.contains(idx))
73 .map(|(_, line)| line)
74 .collect();
75
76 let new_content = new_lines.join("\n") + "\n";
77 fs::write(&file_path, new_content)
78 .with_context(|| format!("Failed to write file: {}", file_path.display()))?;
79 }
80
81 Ok(())
82}
83
84fn comment_line(line: &str) -> String {
85 let trimmed = line.trim_start();
87 let leading_spaces = line.len() - trimmed.len();
88 format!("{}// {}", " ".repeat(leading_spaces), trimmed)
89}
90
91fn uncomment_line(line: &str) -> String {
92 let re = Regex::new(r"^(\s*)//\s*(.*)$").unwrap();
94 if let Some(caps) = re.captures(line) {
95 format!("{}{}", &caps[1], &caps[2])
96 } else {
97 line.to_string()
98 }
99}