toolkit: the tool program, read_file and write_file

Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
2026-09-23 00:00:03 -07:00
parent 644fda14da
commit 0d444dd5bf
10 changed files with 446 additions and 6 deletions
+68
View File
@@ -0,0 +1,68 @@
//! The first two tools, `read_file` and `write_file`. `shell` and `http_fetch` come later; until
//! then `run` never reaches them, so they stay unknown tools.
use std::fs;
use std::io::Read;
use std::path::Path;
use proto::tools::{ReadFileArgs, WriteFileArgs};
use crate::Outcome;
pub const MAX_READ: usize = 1024 * 1024;
fn tool_err(tool: &str, path: &str, why: &str) -> Outcome {
Outcome::tool_error(format!("{tool}: {path}: {why}"))
}
pub fn read_file(args: &ReadFileArgs) -> Outcome {
let path = args.path.as_str();
let is_dir = match fs::metadata(path) {
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
return tool_err("read_file", path, "no such file");
}
Err(e) => return tool_err("read_file", path, &e.to_string()),
Ok(meta) => meta.is_dir(),
};
if is_dir {
return tool_err("read_file", path, "is a directory");
}
let file = match fs::File::open(path) {
Err(e) => return tool_err("read_file", path, &e.to_string()),
Ok(f) => f,
};
let mut buf = Vec::new();
let n = match file.take(MAX_READ as u64 + 1).read_to_end(&mut buf) {
Err(e) => return tool_err("read_file", path, &e.to_string()),
Ok(n) => n,
};
if n > MAX_READ {
return tool_err("read_file", path, &format!("larger than {MAX_READ} bytes"));
}
match String::from_utf8(buf) {
Ok(text) => Outcome::done(text),
Err(_) => tool_err("read_file", path, "not UTF-8 text"),
}
}
pub fn write_file(args: &WriteFileArgs) -> Outcome {
let path = args.path.as_str();
let content = args.content.as_str();
match Path::new(path).parent() {
Some(p) if p.is_dir() => {}
_ => return tool_err("write_file", path, "the directory does not exist"),
}
if Path::new(path).is_dir() {
return tool_err("write_file", path, "is a directory");
}
if let Err(e) = fs::write(path, content) {
return tool_err("write_file", path, &e.to_string());
}
Outcome::done(format!("wrote {} bytes to {path}", content.len()))
}