use crate::{Fail, Result};
use std::collections::HashMap;
use std::fs::{File, OpenOptions, remove_file, rename};
use std::io::prelude::*;
#[derive(Debug)]
pub struct StorageFile {
file: File,
raw: String,
cache: HashMap<String, String>,
}
impl StorageFile {
pub fn new(file_name: impl AsRef<str>) -> Result<Self> {
let mut file = open_file(file_name)?;
let raw = read_file(&mut file)?;
let raw = String::from_utf8(raw)?;
let cache = parse(&raw);
Ok(Self { file, raw, cache })
}
pub fn raw(&self) -> &str {
&self.raw
}
pub fn raw_write(&mut self, raw: String) -> Result<()> {
self.raw = raw;
self.cache = parse(&self.raw);
write_file(&mut self.file, &self.raw)
}
pub fn cache(&self) -> &HashMap<String, String> {
&self.cache
}
pub fn cache_mut(&mut self) -> &mut HashMap<String, String> {
&mut self.cache
}
pub fn write(&mut self) -> Result<()> {
self.raw = serialize(self.cache());
write_file(&mut self.file, &self.raw)
}
}
pub fn open_file(file_name: impl AsRef<str>) -> Result<File> {
OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(file_name.as_ref())
.or_else(Fail::from)
}
pub fn delete_file(file_name: impl AsRef<str>) -> Result<()> {
remove_file(file_name.as_ref()).or_else(Fail::from)
}
pub fn move_file(file_name: impl AsRef<str>, new_file_name: impl AsRef<str>) -> Result<()> {
rename(file_name.as_ref(), new_file_name.as_ref()).or_else(Fail::from)
}
pub fn read_file(file: &mut File) -> Result<Vec<u8>> {
file.rewind()?;
let mut buf = Vec::with_capacity(match file.metadata() {
Ok(metadata) => metadata.len() as usize,
Err(_) => 8192,
});
file.read_to_end(&mut buf)?;
Ok(buf)
}
pub fn write_file(file: &mut File, data: impl AsRef<[u8]>) -> Result<()> {
file.set_len(0)?;
file.rewind()?;
file.write_all(data.as_ref())?;
file.flush().or_else(Fail::from)
}
pub fn parse(buf: &str) -> HashMap<String, String> {
let mut conf = HashMap::new();
buf.split('\n')
.map(|l| l.splitn(2, '=').map(|c| c.trim()).collect())
.for_each(|kv: Vec<&str>| {
if kv.len() == 2 {
conf.insert(kv[0].to_lowercase(), kv[1].to_string());
}
});
conf
}
pub fn serialize(data: &HashMap<String, String>) -> String {
let mut buf = String::with_capacity(data.len() * 10);
for (k, v) in data {
buf.push_str(k);
buf.push('=');
buf.push_str(v);
buf.push('\n');
}
buf
}