tycode_core/file/
manager.rs1use crate::file::access::FileAccessManager;
2use crate::tools::r#trait::FileModification;
3use anyhow::{Context, Result};
4
5#[derive(Debug, Clone)]
7pub struct FileModificationStats {
8 pub lines_added: u32,
9 pub lines_removed: u32,
10}
11
12pub struct FileModificationManager {
14 file_access: FileAccessManager,
15}
16
17impl FileModificationManager {
18 pub fn new(file_access: FileAccessManager) -> Self {
19 Self { file_access }
20 }
21
22 pub async fn apply_modification(
23 &self,
24 modification: FileModification,
25 ) -> Result<FileModificationStats> {
26 let stats = match modification.operation {
27 crate::tools::r#trait::FileOperation::Create => {
28 let content = modification
29 .new_content
30 .ok_or_else(|| anyhow::anyhow!("Create operation requires new_content"))?;
31
32 self.file_access
33 .write_file(
34 modification
35 .path
36 .to_str()
37 .ok_or_else(|| anyhow::anyhow!("Invalid file path"))?,
38 &content,
39 )
40 .await
41 .with_context(|| {
42 format!("Failed to create file: {}", modification.path.display())
43 })?;
44
45 let lines_added = content.lines().count() as u32;
46 let lines_removed = 0;
47
48 tracing::info!(
49 "Created file: {} ({} lines)",
50 modification.path.display(),
51 lines_added
52 );
53
54 FileModificationStats {
55 lines_added,
56 lines_removed,
57 }
58 }
59 crate::tools::r#trait::FileOperation::Update => {
60 let content = modification
61 .new_content
62 .ok_or_else(|| anyhow::anyhow!("Update operation requires new_content"))?;
63
64 let original_content = modification.original_content.as_deref().unwrap_or("");
65 let lines_added = count_lines_added(original_content, &content);
66 let lines_removed = count_lines_removed(original_content, &content);
67
68 self.file_access
69 .write_file(
70 modification
71 .path
72 .to_str()
73 .ok_or_else(|| anyhow::anyhow!("Invalid file path"))?,
74 &content,
75 )
76 .await
77 .with_context(|| {
78 format!("Failed to update file: {}", modification.path.display())
79 })?;
80
81 tracing::info!(
82 "Updated file: {} (+{} lines, -{} lines)",
83 modification.path.display(),
84 lines_added,
85 lines_removed
86 );
87
88 FileModificationStats {
89 lines_added,
90 lines_removed,
91 }
92 }
93 crate::tools::r#trait::FileOperation::Delete => {
94 let original_content = self
96 .file_access
97 .read_file(
98 modification
99 .path
100 .to_str()
101 .ok_or_else(|| anyhow::anyhow!("Invalid file path"))?,
102 )
103 .await
104 .with_context(|| {
105 format!(
106 "Failed to read file before deletion: {}",
107 modification.path.display()
108 )
109 })?;
110
111 let lines_removed = original_content.lines().count() as u32;
112
113 self.file_access
114 .delete_file(
115 modification
116 .path
117 .to_str()
118 .ok_or_else(|| anyhow::anyhow!("Invalid file path"))?,
119 )
120 .await
121 .with_context(|| {
122 format!("Failed to delete file: {}", modification.path.display())
123 })?;
124
125 tracing::info!(
126 "Deleted file: {} ({} lines)",
127 modification.path.display(),
128 lines_removed
129 );
130
131 FileModificationStats {
132 lines_added: 0,
133 lines_removed,
134 }
135 }
136 };
137
138 Ok(stats)
139 }
140}
141
142fn count_lines_added(original: &str, new: &str) -> u32 {
144 let original_lines: std::collections::HashSet<&str> = original.lines().collect();
145 let new_lines: std::collections::HashSet<&str> = new.lines().collect();
146
147 new_lines.difference(&original_lines).count() as u32
149}
150
151fn count_lines_removed(original: &str, new: &str) -> u32 {
153 let original_lines: std::collections::HashSet<&str> = original.lines().collect();
154 let new_lines: std::collections::HashSet<&str> = new.lines().collect();
155
156 original_lines.difference(&new_lines).count() as u32
158}