//! This module implements a cached file system allowing for results to be stored in-memory rather //! rather being queried from the file system again. use std::fs; use std::io::{Error, Result}; use std::path::{Path, PathBuf}; use moka::sync::Cache; use once_cell::sync::Lazy; pub fn read(path: impl AsRef) -> Result> { static READ_CACHE: Lazy>> = Lazy::new(|| Cache::new(10_000)); let path = path.as_ref().canonicalize()?; match READ_CACHE.get(path.as_path()) { Some(content) => Ok(content), None => { let content = fs::read(path.as_path())?; READ_CACHE.insert(path, content.clone()); Ok(content) } } } pub fn read_to_string(path: impl AsRef) -> Result { let content = read(path)?; String::from_utf8(content).map_err(|_| { Error::new( std::io::ErrorKind::InvalidData, "The contents of the file are not valid UTF8", ) }) } pub fn read_dir(path: impl AsRef) -> Result>>> { static READ_DIR_CACHE: Lazy>> = Lazy::new(|| Cache::new(10_000)); let path = path.as_ref().canonicalize()?; match READ_DIR_CACHE.get(path.as_path()) { Some(entries) => Ok(Box::new(entries.into_iter().map(Ok)) as Box<_>), None => { let entries = fs::read_dir(path.as_path())? .flat_map(|maybe_entry| maybe_entry.map(|entry| entry.path())) .collect(); READ_DIR_CACHE.insert(path.clone(), entries); Ok(read_dir(path).unwrap()) } } } pub trait PathExt { fn cached_canonicalize(&self) -> Result; } impl PathExt for T where T: AsRef, { fn cached_canonicalize(&self) -> Result { static CANONICALIZATION_CACHE: Lazy> = Lazy::new(|| Cache::new(10_000)); let path = self.as_ref().to_path_buf(); match CANONICALIZATION_CACHE.get(&path) { Some(canonicalized) => Ok(canonicalized), None => { let canonicalized = path.canonicalize()?; CANONICALIZATION_CACHE.insert(path, canonicalized.clone()); Ok(canonicalized) } } } }