(
&self,
args: serde_json::Value,
ctx: ToolContext,
)
| 82 | } |
| 83 | |
| 84 | async fn execute( |
| 85 | &self, |
| 86 | args: serde_json::Value, |
| 87 | ctx: ToolContext, |
| 88 | ) -> Result<ToolResult, ToolError> { |
| 89 | let input: MultiEditInput = |
| 90 | serde_json::from_value(args).map_err(|e| ToolError::InvalidArguments(e.to_string()))?; |
| 91 | |
| 92 | let base_path = PathBuf::from(&ctx.directory); |
| 93 | let mut results: Vec<String> = Vec::new(); |
| 94 | let mut total_edits = 0; |
| 95 | let mut total_files = 0; |
| 96 | |
| 97 | for file_edit in input.edits { |
| 98 | let file_path = base_path.join(&file_edit.file_path); |
| 99 | |
| 100 | if !file_path.exists() { |
| 101 | return Err(ToolError::FileNotFound(format!( |
| 102 | "File not found: {}", |
| 103 | file_edit.file_path |
| 104 | ))); |
| 105 | } |
| 106 | |
| 107 | let content = tokio::fs::read_to_string(&file_path) |
| 108 | .await |
| 109 | .map_err(|e| ToolError::ExecutionError(format!("Failed to read file: {}", e)))?; |
| 110 | |
| 111 | let mut new_content = content; |
| 112 | let mut file_edits = 0; |
| 113 | |
| 114 | for edit in file_edit.edits { |
| 115 | let count = if edit.replace_all { |
| 116 | let count = new_content.matches(&edit.old_string).count(); |
| 117 | new_content = new_content.replace(&edit.old_string, &edit.new_string); |
| 118 | count |
| 119 | } else { |
| 120 | if !new_content.contains(&edit.old_string) { |
| 121 | return Err(ToolError::ExecutionError(format!( |
| 122 | "Could not find '{}' in file {}", |
| 123 | edit.old_string, file_edit.file_path |
| 124 | ))); |
| 125 | } |
| 126 | if let Some(pos) = new_content.find(&edit.old_string) { |
| 127 | let before = &new_content[..pos]; |
| 128 | let after = &new_content[pos + edit.old_string.len()..]; |
| 129 | new_content = format!("{}{}{}", before, edit.new_string, after); |
| 130 | 1 |
| 131 | } else { |
| 132 | 0 |
| 133 | } |
| 134 | }; |
| 135 | file_edits += count; |
| 136 | } |
| 137 | |
| 138 | if file_edits > 0 { |
| 139 | tokio::fs::write(&file_path, &new_content) |
| 140 | .await |
| 141 | .map_err(|e| { |
nothing calls this directly
no test coverage detected