use anyhow::{anyhow, Result};
use std::fs::{create_dir_all, OpenOptions};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
pub trait FilesystemLayer {
fn exists(&self, filename: &str) -> bool;
fn open(&self, filename: &str) -> Result<Vec<u8>>;
fn save(&self, filename: &str, bytes: &[u8]) -> Result<()>;
}
pub struct DirectoryFilesystemLayer {
directory_path_str: String,
}
impl DirectoryFilesystemLayer {
pub fn new(directory_path_str: &str) -> Result<Self> {
let directory_path = Path::new(directory_path_str);
if !(directory_path.exists() && directory_path.is_dir()) {
return Err(anyhow!(
"Path {} is not an existing directory",
directory_path_str
));
}
Ok(DirectoryFilesystemLayer {
directory_path_str: directory_path_str.to_string(),
})
}
fn create_path(&self, filename: &str) -> PathBuf {
let mut full_path = PathBuf::new();
full_path.push(&self.directory_path_str);
full_path.push(filename);
full_path
}
}
impl FilesystemLayer for DirectoryFilesystemLayer {
fn exists(&self, filename: &str) -> bool {
let directory_path = self.create_path(filename);
directory_path.exists()
}
fn open(&self, filename: &str) -> Result<Vec<u8>> {
let path = self.create_path(filename);
let path = path
.to_str()
.ok_or(anyhow!("Error creating full path in directory provider"))?;
let mut file = OpenOptions::new().read(true).open(path)?;
let mut file_contents: Vec<u8> = Vec::new();
file.read_to_end(&mut file_contents)?;
Ok(file_contents)
}
fn save(&self, filename: &str, bytes: &[u8]) -> Result<()> {
let path = self.create_path(filename);
let parent = path.parent()
.ok_or(anyhow!("Error finding parent of path given in save"))?
.to_str()
.ok_or(anyhow!("Error creating full path in directory provider"))?;
let path = path
.to_str()
.ok_or(anyhow!("Error creating full path in directory provider"))?;
create_dir_all(&parent)?;
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&path)?;
file.write_all(bytes)?;
Ok(())
}
}