feat: Add Filesystem to wrap fs operations

This commit is contained in:
Paul Campbell 2024-04-07 11:52:47 +01:00
parent d341647d7d
commit 1207bbcff3
5 changed files with 158 additions and 32 deletions

View file

@ -14,9 +14,15 @@ console-subscriber = "0.2"
tracing = "0.1" tracing = "0.1"
tracing-subscriber = "0.3" tracing-subscriber = "0.3"
# fs
tempfile = "3.10"
[dev-dependencies] [dev-dependencies]
# Conventional commits githook # Conventional commits githook
cc-cli = "0.1.5" cc-cli = "0.1.5"
# Testing
test-log = "0.2"
[package.metadata.bin] [package.metadata.bin]
cc-cli = { version = "0.1" } cc-cli = { version = "0.1" }

128
src/filesystem.rs Normal file
View file

@ -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<PathBuf>) -> Self {
let cwd = cwd.unwrap_or_default();
Self::Real(RealFileSystemEnv::new(cwd))
}
pub fn new_temp() -> std::io::Result<Self> {
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<PathBuf> {
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<Mutex<TempDir>>,
}
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<Self> {
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);
}
}

View file

@ -1,27 +1,23 @@
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 file_name = ".git-next.toml";
let path = std::path::Path::new(file_name); let path = PathBuf::from(file_name);
if path.exists() { if fs.file_exists(&path) {
eprintln!( eprintln!(
"The configuration file already exists at {} - not overwritting it.", "The configuration file already exists at {} - not overwritting it.",
file_name file_name
); );
} else { } else {
match std::fs::File::create(file_name) { match fs.write_file(file_name, include_str!("../default.toml")) {
Ok(mut file) => { Ok(_) => {
println!("Created a default configuration file at {}", file_name); 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) => { Err(e) => {
eprintln!("Failed to write to the configuration file: {}", e) eprintln!("Failed to write to the configuration file: {}", e)
} }
} }
} }
Err(e) => {
eprintln!("Failed to create a default configuration file: {}", e);
}
}
}
} }

View file

@ -1,3 +1,4 @@
mod filesystem;
mod init; mod init;
mod server; mod server;
@ -21,18 +22,19 @@ enum Server {
Start, Start,
} }
fn main() { fn main() {
let fs = filesystem::FileSystem::new_real(None);
let commands = Commands::parse(); let commands = Commands::parse();
match commands.command { match commands.command {
Command::Init => { Command::Init => {
init::run(); init::run(fs);
} }
Command::Server(server) => match server { Command::Server(server) => match server {
Server::Init => { Server::Init => {
server::init(); server::init(fs);
} }
Server::Start => { Server::Start => {
server::start(); server::start(fs);
} }
}, },
} }

View file

@ -1,34 +1,28 @@
use std::io::Write; use std::path::PathBuf;
use tracing::info; 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 file_name = "git-next-server.toml";
let path = std::path::Path::new(file_name); let path = PathBuf::from(file_name);
if path.exists() { if fs.file_exists(&path) {
eprintln!( eprintln!(
"The configuration file already exists at {} - not overwritting it.", "The configuration file already exists at {} - not overwritting it.",
file_name file_name
); );
} else { } else {
match std::fs::File::create(file_name) { match fs.write_file(file_name, include_str!("../server-default.toml")) {
Ok(mut file) => { Ok(_) => println!("Created a default configuration file at {}", file_name),
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) => { Err(e) => {
eprintln!("Failed to write to the configuration file: {}", e) eprintln!("Failed to write to the configuration file: {}", e)
} }
} }
} }
Err(e) => {
eprintln!("Failed to create a default configuration file: {}", e);
}
}
}
} }
pub(crate) fn start() { pub(crate) fn start(_fs: FileSystem) {
let Ok(_) = init_logging() else { let Ok(_) = init_logging() else {
eprintln!("Failed to initialize logging."); eprintln!("Failed to initialize logging.");
return; return;