Skip to main content

agent_code_lib/tools/
file_write.rs

1//! FileWrite tool: create or overwrite files.
2
3use async_trait::async_trait;
4use serde_json::json;
5use std::path::PathBuf;
6
7use super::{Tool, ToolContext, ToolResult};
8use crate::error::ToolError;
9
10pub struct FileWriteTool;
11
12#[async_trait]
13impl Tool for FileWriteTool {
14    fn name(&self) -> &'static str {
15        "FileWrite"
16    }
17
18    fn description(&self) -> &'static str {
19        "Writes content to a file, creating it if needed or overwriting if it exists."
20    }
21
22    fn input_schema(&self) -> serde_json::Value {
23        json!({
24            "type": "object",
25            "required": ["file_path", "content"],
26            "properties": {
27                "file_path": {
28                    "type": "string",
29                    "description": "Absolute path to the file"
30                },
31                "content": {
32                    "type": "string",
33                    "description": "Content to write"
34                }
35            }
36        })
37    }
38
39    fn is_read_only(&self) -> bool {
40        false
41    }
42
43    fn get_path(&self, input: &serde_json::Value) -> Option<PathBuf> {
44        input
45            .get("file_path")
46            .and_then(|v| v.as_str())
47            .map(PathBuf::from)
48    }
49
50    async fn call(
51        &self,
52        input: serde_json::Value,
53        _ctx: &ToolContext,
54    ) -> Result<ToolResult, ToolError> {
55        let file_path = input
56            .get("file_path")
57            .and_then(|v| v.as_str())
58            .ok_or_else(|| ToolError::InvalidInput("'file_path' is required".into()))?;
59
60        let content = input
61            .get("content")
62            .and_then(|v| v.as_str())
63            .ok_or_else(|| ToolError::InvalidInput("'content' is required".into()))?;
64
65        let path = PathBuf::from(file_path);
66
67        // Create parent directories if needed.
68        if let Some(parent) = path.parent() {
69            tokio::fs::create_dir_all(parent).await.map_err(|e| {
70                ToolError::ExecutionFailed(format!(
71                    "Failed to create directory {}: {e}",
72                    parent.display()
73                ))
74            })?;
75        }
76
77        tokio::fs::write(&path, content)
78            .await
79            .map_err(|e| ToolError::ExecutionFailed(format!("Failed to write {file_path}: {e}")))?;
80
81        let lines = content.lines().count();
82        let bytes = content.len();
83        Ok(ToolResult::success(format!(
84            "Wrote {lines} lines ({bytes} bytes) to {file_path}"
85        )))
86    }
87}