#![allow(unused)] use std::{ ops::Deref, path::PathBuf, sync::{Arc, Mutex}, }; use tempfile::{tempdir, TempDir}; use tracing::info; #[derive(Clone, Debug)] pub enum FileSystem { Real(RealFileSystemEnv), Temp(TempFileSystemEnv), } impl FileSystem { pub fn new_real(cwd: Option) -> Self { let cwd = cwd.unwrap_or_default(); Self::Real(RealFileSystemEnv::new(cwd)) } pub fn new_temp() -> std::io::Result { TempFileSystemEnv::new().map(Self::Temp) } } impl Deref for FileSystem { type Target = dyn FileSystemEnv; fn deref(&self) -> &Self::Target { match self { Self::Real(env) => env, Self::Temp(env) => env, } } } pub trait FileSystemEnv: Sync + Send + std::fmt::Debug { fn cwd(&self) -> &PathBuf; fn in_cwd(&self, name: &str) -> PathBuf { self.cwd().join(name) } fn write_file(&self, file_name: &str, content: &str) -> std::io::Result { use std::fs::File; use std::io::{LineWriter, Write}; let path = self.in_cwd(file_name); info!("writing to {:?}", path); let file = File::create(path.clone())?; let mut file = LineWriter::new(file); file.write_all(content.as_bytes())?; Ok(path) } fn file_exists(&self, name: &PathBuf) -> bool { use std::fs::File; File::open(name).is_ok() } fn read_file(&self, file_name: &str) -> std::io::Result { use std::fs::File; use std::io::Read; let path = self.in_cwd(file_name); info!("reading from {:?}", path); let mut file = File::open(path)?; let mut content = String::new(); file.read_to_string(&mut content)?; Ok(content) } } #[derive(Clone, Debug, Default)] pub struct RealFileSystemEnv { cwd: PathBuf, } #[derive(Clone, Debug)] pub struct TempFileSystemEnv { cwd: PathBuf, temp_dir: Arc>, } impl FileSystemEnv for TempFileSystemEnv { fn cwd(&self) -> &PathBuf { &self.cwd } } impl FileSystemEnv for RealFileSystemEnv { fn cwd(&self) -> &PathBuf { &self.cwd } } impl RealFileSystemEnv { const fn new(cwd: PathBuf) -> Self { Self { cwd } } } impl TempFileSystemEnv { fn new() -> std::io::Result { let temp_dir = tempdir()?; info!("temp dir: {:?}", temp_dir.path()); let cwd = temp_dir.path().to_path_buf(); let temp_dir = Arc::new(Mutex::new(temp_dir)); Ok(Self { cwd, temp_dir }) } } #[cfg(test)] mod tests { use super::*; use std::path::PathBuf; #[test_log::test] fn test_cwd() { let cwd = PathBuf::from("/tmp"); let env = RealFileSystemEnv::new(cwd.clone()); assert_eq!(env.cwd(), &cwd); } #[test_log::test] fn test_create_on_temp_fs() -> std::io::Result<()> { let env = TempFileSystemEnv::new()?; assert!(env.cwd().exists()); Ok(()) } #[test_log::test] fn test_create_on_real_fs() { let cwd = PathBuf::from("/tmp"); let env = RealFileSystemEnv::new(cwd.clone()); assert_eq!(env.cwd(), &cwd); } #[test_log::test] fn test_write_and_read_file() -> std::io::Result<()> { let env = TempFileSystemEnv::new()?; let file_name = "test.txt"; let content = "Hello, World!"; let path = env.write_file(file_name, content)?; assert_eq!(env.read_file(file_name)?, content); assert!(path.exists()); Ok(()) } }