Skip to main content

yapi_core/tools/
write.rs

1//! `write`: create or overwrite a file, creating parent directories.
2
3use futures_util::future::BoxFuture;
4use serde_json::{Value, json};
5use tokio_util::sync::CancellationToken;
6use yapi_agent::{Tool, UpdateSink};
7use yapi_types::event::ToolResult;
8use yapi_types::message::ToolDeclaration;
9
10use super::mutation::with_file_lock;
11use super::{ToolEnv, check_abort, declaration, node_error, string_arg, text_result};
12
13/// The `write` tool.
14pub struct Write {
15    env: ToolEnv,
16    declaration: ToolDeclaration,
17}
18
19impl Write {
20    /// A `write` tool for `env`.
21    pub fn new(env: ToolEnv) -> Write {
22        Write {
23            env,
24            declaration: declaration(
25                "write",
26                "Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.".into(),
27                json!({"type":"object","required":["path","content"],"properties":{
28                    "path":{"type":"string","description":"Path to the file to write (relative or absolute)"},
29                    "content":{"type":"string","description":"Content to write to the file"}}}),
30            ),
31        }
32    }
33}
34
35impl Tool for Write {
36    fn declaration(&self) -> &ToolDeclaration {
37        &self.declaration
38    }
39
40    fn execute(
41        &self,
42        _call_id: String,
43        args: Value,
44        cancel: CancellationToken,
45        _updates: UpdateSink,
46    ) -> BoxFuture<'_, Result<ToolResult, String>> {
47        Box::pin(async move {
48            let path = string_arg(&args, "path")?.to_owned();
49            let content = string_arg(&args, "content")?.to_owned();
50            let absolute = super::path::resolve_to_cwd(&path, &self.env.cwd);
51            with_file_lock(&absolute, async {
52                check_abort(&cancel)?;
53                if let Some(dir) = absolute.parent() {
54                    tokio::fs::create_dir_all(dir)
55                        .await
56                        .map_err(|err| node_error(&err, "mkdir", dir))?;
57                }
58                check_abort(&cancel)?;
59                tokio::fs::write(&absolute, content)
60                    .await
61                    .map_err(|err| node_error(&err, "open", &absolute))?;
62                check_abort(&cancel)?;
63                Ok(text_result(format!("Successfully wrote to {path}"), None))
64            })
65            .await
66        })
67    }
68}