claude_rust_tools/infrastructure/
file_write_tool.rs1use claude_rust_errors::{AppError, AppResult};
2use claude_rust_types::{PermissionLevel, Tool};
3use serde_json::{Value, json};
4
5pub struct FileWriteTool;
6
7#[async_trait::async_trait]
8impl Tool for FileWriteTool {
9 fn name(&self) -> &str {
10 "file_write"
11 }
12
13 fn description(&self) -> &str {
14 "Write content to a file, creating parent directories as needed."
15 }
16
17 fn input_schema(&self) -> Value {
18 json!({
19 "type": "object",
20 "properties": {
21 "file_path": {
22 "type": "string",
23 "description": "Absolute path to the file to write"
24 },
25 "content": {
26 "type": "string",
27 "description": "The content to write to the file"
28 }
29 },
30 "required": ["file_path", "content"]
31 })
32 }
33
34 fn permission_level(&self) -> PermissionLevel {
35 PermissionLevel::Dangerous
36 }
37
38 async fn execute(&self, input: Value) -> AppResult<String> {
39 let path = input
40 .get("file_path")
41 .and_then(|v| v.as_str())
42 .ok_or_else(|| AppError::Tool("missing 'file_path' field".into()))?;
43
44 let content = input
45 .get("content")
46 .and_then(|v| v.as_str())
47 .ok_or_else(|| AppError::Tool("missing 'content' field".into()))?;
48
49 tracing::info!(path, "writing file");
50
51 let file_path = std::path::Path::new(path);
52 if let Some(parent) = file_path.parent() {
53 tokio::fs::create_dir_all(parent)
54 .await
55 .map_err(|e| AppError::Tool(format!("cannot create directories for '{path}': {e}")))?;
56 }
57
58 tokio::fs::write(path, content)
59 .await
60 .map_err(|e| AppError::Tool(format!("cannot write '{path}': {e}")))?;
61
62 let line_count = content.lines().count();
63 Ok(format!("Wrote {line_count} lines to {path}"))
64 }
65}