From 1207bbcff3ace7af19eeabb1a73f2a9554ca4306 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sun, 7 Apr 2024 11:52:47 +0100 Subject: [PATCH] feat: Add Filesystem to wrap fs operations --- Cargo.toml | 6 +++ src/filesystem.rs | 128 ++++++++++++++++++++++++++++++++++++++++++++++ src/init.rs | 22 ++++---- src/main.rs | 8 +-- src/server.rs | 26 ++++------ 5 files changed, 158 insertions(+), 32 deletions(-) create mode 100644 src/filesystem.rs diff --git a/Cargo.toml b/Cargo.toml index c17a7fd..e4e2162 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,15 @@ console-subscriber = "0.2" tracing = "0.1" tracing-subscriber = "0.3" +# fs +tempfile = "3.10" + [dev-dependencies] # Conventional commits githook cc-cli = "0.1.5" +# Testing +test-log = "0.2" + [package.metadata.bin] cc-cli = { version = "0.1" } diff --git a/src/filesystem.rs b/src/filesystem.rs new file mode 100644 index 0000000..095078e --- /dev/null +++ b/src/filesystem.rs @@ -0,0 +1,128 @@ +#![allow(unused)] + +use std::{ + ops::Deref, + path::PathBuf, + sync::{Arc, Mutex}, +}; + +use tempfile::{tempdir, TempDir}; +use tracing::{event, Level}; + +#[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); + event!(Level::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() + } +} + +#[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()?; + event!(Level::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); + } +} diff --git a/src/init.rs b/src/init.rs index 0727ed6..62b8d32 100644 --- a/src/init.rs +++ b/src/init.rs @@ -1,26 +1,22 @@ -use std::io::Write; +use std::path::PathBuf; -pub(crate) fn run() { +use crate::filesystem::FileSystem; + +pub(crate) fn run(fs: FileSystem) { let file_name = ".git-next.toml"; - let path = std::path::Path::new(file_name); - if path.exists() { + let path = PathBuf::from(file_name); + if fs.file_exists(&path) { eprintln!( "The configuration file already exists at {} - not overwritting it.", file_name ); } else { - match std::fs::File::create(file_name) { - Ok(mut file) => { + match fs.write_file(file_name, include_str!("../default.toml")) { + Ok(_) => { println!("Created a default configuration file at {}", file_name); - match file.write_all(include_bytes!("../default.toml")) { - Ok(_) => println!("Wrote to the configuration file successfully."), - Err(e) => { - eprintln!("Failed to write to the configuration file: {}", e) - } - } } Err(e) => { - eprintln!("Failed to create a default configuration file: {}", e); + eprintln!("Failed to write to the configuration file: {}", e) } } } diff --git a/src/main.rs b/src/main.rs index c18b6de..5b88953 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod filesystem; mod init; mod server; @@ -21,18 +22,19 @@ enum Server { Start, } fn main() { + let fs = filesystem::FileSystem::new_real(None); let commands = Commands::parse(); match commands.command { Command::Init => { - init::run(); + init::run(fs); } Command::Server(server) => match server { Server::Init => { - server::init(); + server::init(fs); } Server::Start => { - server::start(); + server::start(fs); } }, } diff --git a/src/server.rs b/src/server.rs index 738e067..c151340 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,34 +1,28 @@ -use std::io::Write; +use std::path::PathBuf; use tracing::info; -pub(crate) fn init() { +use crate::filesystem::FileSystem; + +pub(crate) fn init(fs: FileSystem) { let file_name = "git-next-server.toml"; - let path = std::path::Path::new(file_name); - if path.exists() { + let path = PathBuf::from(file_name); + if fs.file_exists(&path) { eprintln!( "The configuration file already exists at {} - not overwritting it.", file_name ); } else { - match std::fs::File::create(file_name) { - Ok(mut file) => { - println!("Created a default configuration file at {}", file_name); - match file.write_all(include_bytes!("../server-default.toml")) { - Ok(_) => println!("Wrote to the configuration file successfully."), - Err(e) => { - eprintln!("Failed to write to the configuration file: {}", e) - } - } - } + match fs.write_file(file_name, include_str!("../server-default.toml")) { + Ok(_) => println!("Created a default configuration file at {}", file_name), Err(e) => { - eprintln!("Failed to create a default configuration file: {}", e); + eprintln!("Failed to write to the configuration file: {}", e) } } } } -pub(crate) fn start() { +pub(crate) fn start(_fs: FileSystem) { let Ok(_) = init_logging() else { eprintln!("Failed to initialize logging."); return;