72 lines
2.1 KiB
Rust
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 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(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()))
|
|
}
|