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;
29 if idx < lines.len() {
30 if uncomment {
31 lines[idx] = uncomment_line(&lines[idx]);
33 } else {
34 lines[idx] = comment_line(&lines[idx]);
36 }
37 }
38 }
39
40 let new_content = lines.join("\n") + "\n";
41 fs::write(&file_path, new_content)
42 .with_context(|| format!("Failed to write file: {}", file_path.display()))?;
43 }
44
45 Ok(())
46}
47
48pub fn delete_changes(matches: &[Match]) -> Result<()> {
49 let mut files_map: HashMap<PathBuf, Vec<&Match>> = HashMap::new();
51
52 for m in matches {
53 files_map.entry(m.file_path.clone()).or_default().push(m);
54 }
55
56 for (file_path, file_matches) in files_map {
57 let content = fs::read_to_string(&file_path)?;
58 let lines: Vec<String> = content.lines().map(|s| s.to_string()).collect();
59
60 let mut sorted_matches = file_matches;
62 sorted_matches.sort_by(|a, b| b.line_number.cmp(&a.line_number));
63
64 let mut lines_to_delete: HashSet<usize> = HashSet::new();
66 for m in sorted_matches {
67 for line_num in m.line_number..=m.end_line_number {
68 lines_to_delete.insert(line_num - 1);
69 }
70 }
71
72 let new_lines: Vec<String> = lines
74 .into_iter()
75 .enumerate()
76 .filter(|(idx, _)| !lines_to_delete.contains(idx))
77 .map(|(_, line)| line)
78 .collect();
79
80 let new_content = new_lines.join("\n") + "\n";
81 fs::write(&file_path, new_content)
82 .with_context(|| format!("Failed to write file: {}", file_path.display()))?;
83 }
84
85 Ok(())
86}
87
88fn comment_line(line: &str) -> String {
89 let trimmed = line.trim_start();
91 let leading_spaces = line.len() - trimmed.len();
92 format!("{}// {}", " ".repeat(leading_spaces), trimmed)
93}
94
95fn uncomment_line(line: &str) -> String {
96 let re = Regex::new(r"^(\s*)//\s*(.*)$").unwrap();
98 if let Some(caps) = re.captures(line) {
99 format!("{}{}", &caps[1], &caps[2])
100 } else {
101 line.to_string()
102 }
103}