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:
@@ -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()))
|
||||
}
|
||||
Reference in New Issue
Block a user