use std::{
io,
os::unix::fs::PermissionsExt,
path::{Path, PathBuf},
};
pub trait Files {
fn read(&self, path: &Path) -> io::Result<String>;
fn list(&self, directory: &Path) -> io::Result<Vec<PathBuf>>;
}
pub struct Filesystem;
impl Files for Filesystem {
fn read(&self, path: &Path) -> io::Result<String> {
let meta = std::fs::metadata(path)?;
let perm = meta.permissions();
if perm.mode() as libc::mode_t & libc::S_IWOTH != 0 {
warn!(
"Unrestricted config file permissions on {}: Others can write.",
path.display()
);
}
std::fs::read_to_string(path)
}
fn list(&self, directory: &Path) -> io::Result<Vec<PathBuf>> {
let meta = std::fs::metadata(directory)?;
let perm = meta.permissions();
if perm.mode() as libc::mode_t & libc::S_IWOTH != 0 {
warn!(
"Unrestricted config directory permissions on {}: Others can write.",
directory.display()
);
}
std::fs::read_dir(directory)?
.map(|entry| entry.map(|entry| entry.path()))
.collect()
}
}
#[cfg(test)]
pub use memory::Memory;
use tracing::warn;
#[cfg(test)]
mod memory {
use std::collections::{HashMap, HashSet};
use super::*;
#[derive(Debug, Default)]
pub struct Memory {
documents: HashMap<PathBuf, String>,
directories: HashSet<PathBuf>,
}
impl Memory {
pub fn new() -> Self {
Self::default()
}
pub fn document(mut self, path: impl Into<PathBuf>, contents: &str) -> Self {
let path = path.into();
if let Some(parent) = path.parent() {
self.directories.insert(parent.to_path_buf());
}
self.documents.insert(path, contents.to_owned());
self
}
pub fn directory(mut self, path: impl Into<PathBuf>) -> Self {
self.directories.insert(path.into());
self
}
}
fn not_found(what: &str) -> io::Error {
io::Error::new(io::ErrorKind::NotFound, format!("no such {what}"))
}
impl Files for Memory {
fn read(&self, path: &Path) -> io::Result<String> {
self.documents
.get(path)
.cloned()
.ok_or_else(|| not_found("document"))
}
fn list(&self, directory: &Path) -> io::Result<Vec<PathBuf>> {
if !self.directories.contains(directory) {
return Err(not_found("directory"));
}
Ok(self
.documents
.keys()
.filter(|path| path.parent() == Some(directory))
.cloned()
.collect())
}
}
}