use std::io::{self};
use std::path::Path;
use crate::LocalMetadataFileSystem;
use crate::MetadataFileSystem;
use crate::RuntimeError;
use crate::RuntimeOperation;
use crate::RuntimeResult;
use parking_lot::Mutex;
use tracing::warn;
static LOCK: Mutex<()> = Mutex::new(());
pub fn file_to_string(file_name: impl AsRef<Path>) -> RuntimeResult<String> {
let path = file_name.as_ref();
match std::fs::read_to_string(path) {
Ok(sr) => Ok(sr),
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
warn!("file not exist: {}", path.display());
Ok(String::new())
}
Err(e) => Err(RuntimeError::io(RuntimeOperation::ReadFile, e)),
}
}
pub fn string_to_file(str_content: &str, file_name: impl AsRef<Path>) -> RuntimeResult<()> {
let _lock = LOCK.lock();
let file_path = file_name.as_ref();
let mut bak_file = file_path.as_os_str().to_os_string();
bak_file.push(".bak");
if file_path.exists() {
let previous =
std::fs::read(file_path).map_err(|error| RuntimeError::io(RuntimeOperation::ReadFileBackup, error))?;
LocalMetadataFileSystem
.persist_atomic(Path::new(&bak_file), &previous)
.map_err(metadata_io_error)?;
}
LocalMetadataFileSystem
.persist_atomic(file_path, str_content.as_bytes())
.map_err(metadata_io_error)
}
pub fn metadata_io_error(error: impl std::error::Error + Send + Sync + 'static) -> RuntimeError {
RuntimeError::internal(RuntimeOperation::PersistMetadata, error)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_file_to_string() {
let temp_file = tempfile::NamedTempFile::new().unwrap();
let file_path = temp_file.path().to_str().unwrap();
let content = "Hello, World!";
std::fs::write(file_path, content).unwrap();
let result = file_to_string(file_path);
assert!(result.is_ok());
assert_eq!(result.unwrap(), content);
}
#[test]
fn test_string_to_file() {
let temp_file = tempfile::NamedTempFile::new().unwrap();
let file_path = temp_file.path().to_str().unwrap();
let content = "Hello, World!";
let result = string_to_file(content, file_path);
assert!(result.is_ok());
assert_eq!(std::fs::read_to_string(file_path).unwrap(), content);
}
#[test]
fn replacing_a_file_keeps_the_previous_content_as_a_backup() {
let directory = tempfile::tempdir().unwrap();
let file_path = directory.path().join("config.json");
let backup_path = directory.path().join("config.json.bak");
string_to_file("first", &file_path).unwrap();
assert!(!backup_path.exists(), "a new file has no previous content to keep");
string_to_file("second", &file_path).unwrap();
assert_eq!(std::fs::read_to_string(&file_path).unwrap(), "second");
assert_eq!(std::fs::read_to_string(&backup_path).unwrap(), "first");
}
#[test]
fn test_file_to_string_not_found() {
let result = file_to_string("/nonexistent/path/file.txt");
assert!(result.is_ok());
assert_eq!(result.unwrap(), "");
}
}