use std::fmt::Debug;
use std::io::{BufReader, Write};
use std::path::PathBuf;
use std::{ffi::OsString, fs::File};
use log::error;
use logging_timer::{executing, finish, stime, stimer, Level};
use serde::{de::DeserializeOwned, Serialize};
pub fn serialize_to_bin_file<T: Serialize>(
structure: &T,
path: PathBuf,
) -> Result<(), ReadWriteError> {
let tmr = stimer!(Level::Debug; "Serialization");
let encoded: Vec<u8> = bincode::serialize(&structure)?;
executing!(tmr, "Done encoding");
let mut file = File::create(path)?;
file.write_all(&encoded)?;
finish!(tmr, "Done writing file");
Ok(())
}
#[stime("debug")]
pub fn deserialize_from_bin_file<T: DeserializeOwned>(path: PathBuf) -> Result<T, ReadWriteError> {
let file = File::open(path)?;
let buf_reader = BufReader::new(file);
let decoded: T = bincode::deserialize_from(buf_reader)?;
Ok(decoded)
}
#[stime("debug")]
pub fn serialize_to_json_file<T: Serialize>(
structure: &T,
path: PathBuf,
) -> Result<(), ReadWriteError> {
let mut file = File::create(path)?;
let encoded = serde_json::to_writer_pretty(file, structure);
Ok(())
}
#[stime("debug")]
pub fn deserialize_from_json_file<T: DeserializeOwned>(path: PathBuf) -> Result<T, ReadWriteError> {
let file = File::open(path)?;
let buf_reader = BufReader::new(file);
let decoded: T = serde_json::from_reader(buf_reader)?;
Ok(decoded)
}
pub fn parse_serialization_path(
mut path: PathBuf,
extension: &str,
default_file_name_prefix: &str,
) -> Result<PathBuf, ReadWriteError> {
if let Some(ext) = path.extension() {
if ext != extension {
return Err(ReadWriteError::UnsupportedFileExtension {
expected: extension.to_owned(),
actual: ext.to_os_string(),
});
}
if let Some(parent) = path.parent() {
if !parent.is_dir() {
std::fs::create_dir_all(parent)?;
}
}
Ok(path)
} else {
if !path.is_dir() {
std::fs::create_dir_all(path.clone())?;
}
let mut file_name: String = default_file_name_prefix.to_owned();
let now = chrono::offset::Local::now();
file_name.push_str(&now.timestamp().to_string());
file_name.push('.');
file_name.push_str(extension);
path.push(file_name);
Ok(path)
}
}
pub fn check_deserialization_path(
path: &PathBuf,
expected_ext: &str,
) -> Result<(), ReadWriteError> {
if path.is_dir() {
return Err(ReadWriteError::NotAFile(path.clone().into_os_string()));
}
match path.extension() {
Some(ext) => {
if ext == expected_ext {
Ok(())
} else {
Err(ReadWriteError::UnsupportedFileExtension {
expected: expected_ext.to_owned(),
actual: ext.to_os_string(),
})
}
}
None => Err(ReadWriteError::NoFileExtension(
path.clone().into_os_string(),
)),
}
}
#[derive(thiserror::Error, Debug)]
pub enum ReadWriteError {
#[error("Problem serializing/deserializing with bincode")]
BincodeSerdeError(#[from] bincode::Error),
#[error("Problem serializing/deserializing with serde_json")]
JsonSerdeError(#[from] serde_json::Error),
#[error("Problem writing to file")]
FileWriteError(#[from] std::io::Error),
#[error("Unknown file extension {actual:?}, expected {expected}")]
UnsupportedFileExtension { expected: String, actual: OsString },
#[error("Expected a file but only a directory was given: {0:?}")]
NotAFile(OsString),
#[error("No file extension found in path {0:?}")]
NoFileExtension(OsString),
}
#[cfg(test)]
mod tests {
mod parse_serialization_path {
use super::super::*;
#[test]
fn parse_serialization_path_for_existing_directory_gives_correct_file_name() {
let path = PathBuf::from("./");
let expected_extension = "test";
let default_file_name_prefix = "test_prefix";
let path = parse_serialization_path(path, expected_extension, default_file_name_prefix)
.unwrap();
let ext = path.extension().unwrap().to_str().unwrap();
assert_eq!(ext, expected_extension);
let file_name_without_extension = path.file_stem().unwrap().to_str().unwrap();
assert!(file_name_without_extension.contains(default_file_name_prefix));
}
#[test]
fn parse_serialization_path_for_existing_file() {
let this_file = std::file!();
let path = PathBuf::from(this_file);
let expected_extension = "rs";
let default_file_name_prefix = "test_prefix";
parse_serialization_path(path, expected_extension, default_file_name_prefix).unwrap();
}
#[test]
#[should_panic]
fn parse_serialization_path_for_existing_file_wrong_extension() {
let this_file = std::file!();
let path = PathBuf::from(this_file);
let expected_extension = "bad_ext";
let default_file_name_prefix = "test_prefix";
parse_serialization_path(path, expected_extension, default_file_name_prefix).unwrap();
}
}
}