use std::{io, io::Result};
use vfs::{MemoryFS, OverlayFS, PhysicalFS, SeekAndRead, SeekAndWrite, filesystem::FileSystem};
use super::{StorageReadProvider, StorageWriteProvider};
pub struct VirtualStorageProvider<FileSystemType: FileSystem> {
filesystem: FileSystemType,
}
impl<FileSystemType: FileSystem> VirtualStorageProvider<FileSystemType> {
fn new(filesystem: FileSystemType) -> VirtualStorageProvider<FileSystemType> {
VirtualStorageProvider { filesystem }
}
pub fn exists(&self, item_identifier: &str) -> bool {
self.filesystem.metadata(item_identifier).is_ok()
}
pub fn filesystem(&self) -> &FileSystemType {
&self.filesystem
}
pub fn take(self) -> FileSystemType {
self.filesystem
}
}
impl<FileSystemType: FileSystem> StorageReadProvider for VirtualStorageProvider<FileSystemType> {
type Reader = Box<dyn SeekAndRead + Send>;
fn open_reader(&self, item_identifier: &str) -> Result<Self::Reader> {
self.filesystem
.open_file(item_identifier)
.map_err(io::Error::other)
}
fn get_length(&self, item_identifier: &str) -> Result<u64> {
self.filesystem
.metadata(item_identifier)
.map_err(io::Error::other)
.map(|metadata| metadata.len)
}
fn exists(&self, item_identifier: &str) -> bool {
self.filesystem.metadata(item_identifier).is_ok()
}
}
impl<FileSystemType: FileSystem> StorageWriteProvider for VirtualStorageProvider<FileSystemType> {
type Writer = Box<dyn SeekAndWrite + Send>;
fn open_writer(&self, item_identifier: &str) -> Result<Self::Writer> {
self.filesystem
.append_file(item_identifier)
.map_err(io::Error::other)
}
fn create_for_write(&self, item_identifier: &str) -> Result<Self::Writer> {
if self
.filesystem
.exists(item_identifier)
.map_err(io::Error::other)?
{
self.filesystem
.remove_file(item_identifier)
.map_err(io::Error::other)?;
}
self.filesystem
.create_file(item_identifier)
.map_err(io::Error::other)
}
fn delete(&self, item_identifier: &str) -> Result<()> {
self.filesystem
.remove_file(item_identifier)
.map_err(io::Error::other)
}
}
impl VirtualStorageProvider<OverlayFS> {
pub fn new_overlay<P: AsRef<std::path::Path>>(path: P) -> Self {
#[allow(clippy::disallowed_methods)]
let base_filesystem = PhysicalFS::new(path);
let memory_filesystem = MemoryFS::new();
let overlay_filesystem =
OverlayFS::new(&[memory_filesystem.into(), base_filesystem.into()]);
VirtualStorageProvider::new(overlay_filesystem)
}
}
impl VirtualStorageProvider<MemoryFS> {
pub fn new_memory() -> Self {
let memory_filesystem = MemoryFS::new();
VirtualStorageProvider::new(memory_filesystem)
}
}
impl VirtualStorageProvider<PhysicalFS> {
pub fn new_physical<P: AsRef<std::path::Path>>(path: P) -> Self {
#[allow(clippy::disallowed_methods)]
let physical_filesystem = PhysicalFS::new(path);
VirtualStorageProvider::new(physical_filesystem)
}
}
#[cfg(test)]
mod tests {
use std::io::{Read, Seek, SeekFrom, Write};
use super::*;
#[test]
fn test_file_reader() {
let file_name = "/test_file_reader.txt";
let storage_provider = VirtualStorageProvider::new_memory();
{
let mut file = storage_provider
.filesystem()
.create_file(file_name)
.expect("Could not create file");
write!(file, "Hello, world!").unwrap();
}
let mut reader = storage_provider.open_reader(file_name).unwrap();
let mut buffer = [0; 5];
reader.seek(SeekFrom::Start(0)).unwrap();
reader.read(&mut buffer).unwrap();
assert_eq!(&buffer, b"Hello");
reader.seek(SeekFrom::Start(5)).unwrap();
reader.read(&mut buffer).unwrap();
assert_eq!(&buffer, b", wor");
storage_provider.take().remove_file(file_name).unwrap();
}
#[test]
fn test_file_storage_exists() {
let storage_provider = VirtualStorageProvider::new_memory();
let file_name = "/test_file_storage_exists.txt";
assert!(
!storage_provider.exists(file_name),
"File should not exist yet"
);
storage_provider
.filesystem()
.create_file(file_name)
.expect("Could not create file")
.write_all(b"This is the text")
.expect("Write did not succeed");
assert!(
storage_provider.exists(file_name),
"New file does not exist"
);
storage_provider
.filesystem()
.remove_file(file_name)
.unwrap();
assert!(
!storage_provider.exists(file_name),
"New file was not deleted"
);
}
#[test]
fn test_file_storage_get_length() {
let file_name = "/test_file_storage_get_length.txt";
let storage_provider = VirtualStorageProvider::new_memory();
storage_provider
.filesystem()
.create_file(file_name)
.expect("Could not create new file")
.write_all(b"Hello, world!")
.expect("Write did not succeed");
assert_eq!(storage_provider.get_length(file_name).unwrap(), 13);
storage_provider.take().remove_file(file_name).unwrap();
}
}