gestura_core_tools/
file_async.rs1use crate::error::AppError;
7use crate::error::Result;
8use crate::file::FileTools;
9use serde::Serialize;
10use std::path::Path;
11
12#[derive(Debug, Serialize)]
13struct ListDirOutput {
14 path: String,
15 show_hidden: bool,
16 truncated: bool,
17 entries: Vec<crate::file::FileEntry>,
18}
19
20#[derive(Debug, Serialize)]
21struct TreeDirOutput {
22 path: String,
23 max_depth: Option<usize>,
24 show_hidden: bool,
25 tree: crate::file::TreeNode,
26}
27
28#[derive(Debug, Serialize)]
29struct WriteFileOutput {
30 path: String,
31 bytes_written: usize,
32 created: bool,
33 changed: bool,
34}
35
36#[derive(Debug, Serialize)]
37struct EditFileOutput {
38 path: String,
39 replacements: usize,
40 changed: bool,
41}
42
43#[derive(Debug, Serialize)]
44struct SearchOutput {
45 pattern: String,
46 path: String,
47 recursive: bool,
48 truncated: bool,
49 matches: Vec<crate::file::SearchMatch>,
50}
51
52pub async fn read_file(path: &str) -> Result<String> {
54 read_file_range(path, None, None).await
55}
56
57pub async fn read_file_range(
59 path: &str,
60 start_line: Option<usize>,
61 end_line: Option<usize>,
62) -> Result<String> {
63 let path = path.to_string();
64 tokio::task::spawn_blocking(move || {
65 let tools = FileTools::new();
66 tools
67 .read(Path::new(&path), start_line, end_line)
68 .map(|r| r.content)
69 })
70 .await
71 .map_err(|e| AppError::Io(std::io::Error::other(format!("Task join error: {}", e))))?
72}
73
74pub async fn write_file(path: &str, content: &str) -> Result<String> {
76 let path = path.to_string();
77 let content = content.to_string();
78 tokio::task::spawn_blocking(move || {
79 let tools = FileTools::new();
80 let res = tools.write(Path::new(&path), &content)?;
81
82 let out = WriteFileOutput {
83 path,
84 bytes_written: res.bytes_written,
85 created: res.created,
86 changed: res.changed,
87 };
88 serde_json::to_string_pretty(&out).map_err(|e| {
89 AppError::Io(std::io::Error::other(format!(
90 "Failed to serialize write output: {e}"
91 )))
92 })
93 })
94 .await
95 .map_err(|e| AppError::Io(std::io::Error::other(format!("Task join error: {}", e))))?
96}
97
98pub async fn edit_file(path: &str, old_str: &str, new_str: &str) -> Result<String> {
102 let path = path.to_string();
103 let old_str = old_str.to_string();
104 let new_str = new_str.to_string();
105 tokio::task::spawn_blocking(move || {
106 let tools = FileTools::new();
107 let res = tools.edit(Path::new(&path), &old_str, &new_str)?;
108
109 let out = EditFileOutput {
110 path,
111 replacements: res.replacements,
112 changed: res.changed,
113 };
114 serde_json::to_string_pretty(&out).map_err(|e| {
115 AppError::Io(std::io::Error::other(format!(
116 "Failed to serialize edit output: {e}"
117 )))
118 })
119 })
120 .await
121 .map_err(|e| AppError::Io(std::io::Error::other(format!("Task join error: {}", e))))?
122}
123
124pub async fn list_dir(path: &str, show_hidden: bool, max_entries: Option<usize>) -> Result<String> {
128 let path = path.to_string();
129 tokio::task::spawn_blocking(move || {
130 let tools = FileTools::new();
131 let mut entries = tools.list(Path::new(&path), show_hidden)?;
132 let max = max_entries.unwrap_or(200);
133 let truncated = entries.len() > max;
134 if truncated {
135 entries.truncate(max);
136 }
137
138 let out = ListDirOutput {
139 path,
140 show_hidden,
141 truncated,
142 entries,
143 };
144 serde_json::to_string_pretty(&out).map_err(|e| {
145 AppError::Io(std::io::Error::other(format!(
146 "Failed to serialize list output: {e}"
147 )))
148 })
149 })
150 .await
151 .map_err(|e| AppError::Io(std::io::Error::other(format!("Task join error: {}", e))))?
152}
153
154pub async fn tree_dir(path: &str, max_depth: Option<usize>, show_hidden: bool) -> Result<String> {
158 let path = path.to_string();
159 let show_hidden_flag = show_hidden;
160 tokio::task::spawn_blocking(move || {
161 let tools = FileTools::new();
162 let tree = tools.tree(Path::new(&path), max_depth, show_hidden_flag)?;
163 let out = TreeDirOutput {
164 path,
165 max_depth,
166 show_hidden: show_hidden_flag,
167 tree,
168 };
169 serde_json::to_string_pretty(&out).map_err(|e| {
170 AppError::Io(std::io::Error::other(format!(
171 "Failed to serialize tree output: {e}"
172 )))
173 })
174 })
175 .await
176 .map_err(|e| AppError::Io(std::io::Error::other(format!("Task join error: {}", e))))?
177}
178
179pub async fn search_files(
183 pattern: &str,
184 path: &str,
185 recursive: bool,
186 max_matches: Option<usize>,
187) -> Result<String> {
188 let pattern = pattern.to_string();
189 let path = path.to_string();
190 tokio::task::spawn_blocking(move || {
191 let tools = FileTools::new();
192 let mut matches = tools.search(&pattern, Path::new(&path), recursive)?;
193 let max = max_matches.unwrap_or(200);
194 let truncated = matches.len() > max;
195 if truncated {
196 matches.truncate(max);
197 }
198
199 let out = SearchOutput {
200 pattern,
201 path,
202 recursive,
203 truncated,
204 matches,
205 };
206 serde_json::to_string_pretty(&out).map_err(|e| {
207 AppError::Io(std::io::Error::other(format!(
208 "Failed to serialize search output: {e}"
209 )))
210 })
211 })
212 .await
213 .map_err(|e| AppError::Io(std::io::Error::other(format!("Task join error: {}", e))))?
214}