use std::path::Path;
use crate::config::Backend;
use crate::error::{Error, Result};
pub trait IoBackend: Send + Sync {
fn read(&self, path: &Path) -> Result<Vec<u8>>;
fn name(&self) -> &'static str;
}
#[derive(Debug, Default, Clone, Copy)]
pub struct RayonBackend;
impl IoBackend for RayonBackend {
fn read(&self, path: &Path) -> Result<Vec<u8>> {
std::fs::read(path).map_err(|e| Error::io(path, e))
}
fn name(&self) -> &'static str {
"rayon"
}
}
pub fn select(backend: Backend) -> Box<dyn IoBackend> {
if backend == Backend::IoUringRemoved {
tracing::warn!(
"config.toml sets backend = \"io-uring\", which was removed in 0.5.0: it selected a \
stub that did buffered reads anyway. Using the portable backend; set \
backend = \"auto\" to silence this."
);
}
match backend {
Backend::Auto | Backend::Rayon | Backend::IoUringRemoved => Box::new(RayonBackend),
}
}
pub fn default_backend() -> Box<dyn IoBackend> {
select(Backend::default())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn config_backend_is_honored_and_reported() {
assert_eq!(select(Backend::Rayon).name(), "rayon");
assert_eq!(select(Backend::Auto).name(), "rayon");
}
#[test]
fn pre_0_5_io_uring_config_still_loads() {
let cfg: crate::config::Config =
toml::from_str("backend = \"io-uring\"").expect("legacy value must still parse");
assert_eq!(cfg.backend, Backend::IoUringRemoved);
assert_eq!(select(cfg.backend).name(), "rayon");
}
#[test]
fn backend_reads_file_bytes_verbatim() {
let dir = std::env::temp_dir().join(format!("greplm-io-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let f = dir.join("sample.txt");
let body: &[u8] = b"line one\nline two\n\xff\xfe binary-ish\n";
std::fs::write(&f, body).unwrap();
for b in [Backend::Auto, Backend::Rayon] {
assert_eq!(select(b).read(&f).unwrap(), body, "backend {b:?}");
}
assert!(select(Backend::Rayon).read(&dir.join("missing")).is_err());
let _ = std::fs::remove_dir_all(&dir);
}
}