Skip to main content

claude_rust_tools/infrastructure/
file_write_tool.rs

1use 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    fn is_destructive(&self, _input: &Value) -> bool { true }
39
40    fn get_path(&self, input: &Value) -> Option<String> {
41        input.get("file_path").and_then(|v| v.as_str()).map(|s| s.to_string())
42    }
43
44    async fn execute(&self, input: Value) -> AppResult<String> {
45        let path = input
46            .get("file_path")
47            .and_then(|v| v.as_str())
48            .ok_or_else(|| AppError::Tool("missing 'file_path' field".into()))?;
49
50        let content = input
51            .get("content")
52            .and_then(|v| v.as_str())
53            .ok_or_else(|| AppError::Tool("missing 'content' field".into()))?;
54
55        tracing::info!(path, "writing file");
56
57        let file_path = std::path::Path::new(path);
58        if let Some(parent) = file_path.parent() {
59            tokio::fs::create_dir_all(parent)
60                .await
61                .map_err(|e| AppError::Tool(format!("cannot create directories for '{path}': {e}")))?;
62        }
63
64        tokio::fs::write(path, content)
65            .await
66            .map_err(|e| AppError::Tool(format!("cannot write '{path}': {e}")))?;
67
68        let line_count = content.lines().count();
69        Ok(format!("Wrote {line_count} lines to {path}"))
70    }
71}