Files
boxmaker/crates/toolkit/src/files.rs
T
kyleandClaude Opus 5.5 2b3ae080b4 proto becomes bxcore, the one shared crate (M5b task 00)
Done the way the owner asked and the spike mapped: the crate and the
manifests renamed, then what the build reported fixed. Layers: 138
"proto unresolved" errors across 169 files; cargo fmt reordering
imports; six fixture paths built as strings in brokerd's and bxctl's
tests, which the compiler cannot see, and comments naming
crates/proto/; the crate-dependency check and its self-test. One more
the spike did not reach: a `\nproto =` key inside a printf format in
the self-test, which a word-boundary replacement skips because `n` and
`p` are both word characters.

make gate: gate: ok, 944 tests; no new entry in /tmp.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 01:48:38 -07:00

72 lines
2.1 KiB
Rust

//! 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 bxcore::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(u64::try_from(MAX_READ).map_or(u64::MAX, |n| n.saturating_add(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()))
}