use camel_component_api::CamelError;
pub trait ResourceResolver: Send + Sync {
fn resolve(&self, path: &str) -> Result<Vec<u8>, CamelError>;
}
pub struct FilesystemResolver;
const MAX_SCHEMA_BYTES: u64 = 16 * 1024 * 1024;
impl ResourceResolver for FilesystemResolver {
fn resolve(&self, path: &str) -> Result<Vec<u8>, CamelError> {
use std::io::Read;
let file = std::fs::File::open(path).map_err(|e| {
CamelError::EndpointCreationFailed(format!("failed to open schema file '{path}': {e}"))
})?;
let mut buf = Vec::new();
file.take(MAX_SCHEMA_BYTES + 1)
.read_to_end(&mut buf)
.map_err(|e| {
CamelError::EndpointCreationFailed(format!(
"failed to read schema file '{path}': {e}"
))
})?;
if buf.len() as u64 > MAX_SCHEMA_BYTES {
return Err(CamelError::EndpointCreationFailed(format!(
"schema file '{path}' exceeds {} bytes",
MAX_SCHEMA_BYTES
)));
}
Ok(buf)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn filesystem_resolver_reads_existing_file() {
let mut f = tempfile::Builder::new().suffix(".json").tempfile().unwrap();
use std::io::Write;
f.write_all(b"hello").unwrap();
let resolver = FilesystemResolver;
let data = resolver.resolve(f.path().to_str().unwrap()).unwrap();
assert_eq!(data, b"hello");
}
#[test]
fn filesystem_resolver_errors_on_missing_file() {
let resolver = FilesystemResolver;
let result = resolver.resolve("/nonexistent/file.json");
assert!(result.is_err());
}
#[test]
fn filesystem_resolver_rejects_file_past_cap_on_read() {
let mut f = tempfile::Builder::new().suffix(".json").tempfile().unwrap();
f.as_file().set_len(super::MAX_SCHEMA_BYTES + 1).unwrap();
let resolver = FilesystemResolver;
let err = resolver
.resolve(f.path().to_str().unwrap())
.expect_err("over-cap file must be rejected");
let msg = err.to_string();
assert!(msg.contains("exceeds"), "cap rejection: {msg}");
}
}