use crate::{Result, WriterInfo};
use flate2::{write::GzEncoder, Compression};
use std::{
fs::{File, OpenOptions},
io::{BufWriter, ErrorKind, Write},
path::{Path, PathBuf},
};
pub trait NamingConvention {
fn temp_file(&self) -> Result<(PathBuf, File)>;
fn main_file(&self) -> PathBuf;
fn backup_file(&self) -> PathBuf;
fn is_compressed(&self) -> bool;
fn new_temp(&self) -> Result<WriterInfo> {
let (name, file) = self.temp_file()?;
let writer = if self.is_compressed() {
Box::new(GzEncoder::new(file, Compression::default())) as Box<dyn Write>
} else {
Box::new(BufWriter::new(file)) as Box<dyn Write>
};
Ok(WriterInfo { name, writer })
}
}
#[derive(Debug, Clone)]
pub struct SimpleNaming {
path: PathBuf,
base: String,
ext: String,
compressed: bool,
}
impl SimpleNaming {
pub fn new<P: AsRef<Path>>(path: P, base: &str, ext: &str, compressed: bool) -> SimpleNaming {
SimpleNaming {
path: path.as_ref().to_path_buf(),
base: base.to_string(),
ext: ext.to_string(),
compressed,
}
}
pub fn make_name(&self, ext: &str, compressed: bool) -> PathBuf {
let name = format!(
"{}.{}{}",
self.base,
ext,
if compressed { ".gz" } else { "" }
);
self.path.join(name)
}
}
impl NamingConvention for SimpleNaming {
fn main_file(&self) -> PathBuf {
self.make_name(&self.ext, self.compressed)
}
fn backup_file(&self) -> PathBuf {
self.make_name("bak", self.compressed)
}
fn temp_file(&self) -> Result<(PathBuf, File)> {
let mut n = 0;
loop {
let name = self.make_name(&n.to_string(), false);
match OpenOptions::new().write(true).create_new(true).open(&name) {
Ok(fd) => return Ok((name, fd)),
Err(ref e) if e.kind() == ErrorKind::AlreadyExists => (),
Err(e) => return Err(e.into()),
}
n += 1;
}
}
fn is_compressed(&self) -> bool {
self.compressed
}
}