camel_component_validator/
resolver.rs1use camel_component_api::CamelError;
6
7pub trait ResourceResolver: Send + Sync {
13 fn resolve(&self, path: &str) -> Result<Vec<u8>, CamelError>;
15}
16
17pub struct FilesystemResolver;
19
20const MAX_SCHEMA_BYTES: u64 = 16 * 1024 * 1024;
24
25impl ResourceResolver for FilesystemResolver {
26 fn resolve(&self, path: &str) -> Result<Vec<u8>, CamelError> {
27 use std::io::Read;
28 let file = std::fs::File::open(path).map_err(|e| {
33 CamelError::EndpointCreationFailed(format!("failed to open schema file '{path}': {e}"))
34 })?;
35 let mut buf = Vec::new();
36 file.take(MAX_SCHEMA_BYTES + 1)
37 .read_to_end(&mut buf)
38 .map_err(|e| {
39 CamelError::EndpointCreationFailed(format!(
40 "failed to read schema file '{path}': {e}"
41 ))
42 })?;
43 if buf.len() as u64 > MAX_SCHEMA_BYTES {
44 return Err(CamelError::EndpointCreationFailed(format!(
45 "schema file '{path}' exceeds {} bytes",
46 MAX_SCHEMA_BYTES
47 )));
48 }
49 Ok(buf)
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn filesystem_resolver_reads_existing_file() {
59 let mut f = tempfile::Builder::new().suffix(".json").tempfile().unwrap();
60 use std::io::Write;
61 f.write_all(b"hello").unwrap();
62 let resolver = FilesystemResolver;
63 let data = resolver.resolve(f.path().to_str().unwrap()).unwrap();
64 assert_eq!(data, b"hello");
65 }
66
67 #[test]
68 fn filesystem_resolver_errors_on_missing_file() {
69 let resolver = FilesystemResolver;
70 let result = resolver.resolve("/nonexistent/file.json");
71 assert!(result.is_err());
72 }
73
74 #[test]
78 fn filesystem_resolver_rejects_file_past_cap_on_read() {
79 let mut f = tempfile::Builder::new().suffix(".json").tempfile().unwrap();
80 f.as_file().set_len(super::MAX_SCHEMA_BYTES + 1).unwrap();
82 let resolver = FilesystemResolver;
83 let err = resolver
84 .resolve(f.path().to_str().unwrap())
85 .expect_err("over-cap file must be rejected");
86 let msg = err.to_string();
87 assert!(msg.contains("exceeds"), "cap rejection: {msg}");
88 }
89}