#![deny(missing_docs)]
use std::ffi::OsStr;
use std::io::{BufReader, Write};
use std::path::PathBuf;
use super::{InputHandle, InputOrigin, IoProvider, OpenResult};
use crate::digest::DigestData;
use crate::errors::{ErrorKind, Result};
use crate::status::StatusBackend;
pub struct FormatCache {
bundle_digest: DigestData,
formats_base: PathBuf,
}
impl FormatCache {
pub fn new(bundle_digest: DigestData, formats_base: PathBuf) -> FormatCache {
FormatCache {
bundle_digest,
formats_base,
}
}
fn path_for_format(&mut self, name: &OsStr) -> Result<PathBuf> {
let stem = match name.to_str().and_then(|s| s.splitn(2, '.').next()) {
Some(s) => s,
None => {
return Err(ErrorKind::Msg(format!(
"incomprehensible format file name \"{}\"",
name.to_string_lossy()
))
.into());
}
};
let mut p = self.formats_base.clone();
p.push(format!(
"{}-{}-{}.fmt",
self.bundle_digest.to_string(),
stem,
crate::FORMAT_SERIAL
));
Ok(p)
}
}
impl IoProvider for FormatCache {
fn input_open_format(
&mut self,
name: &OsStr,
_status: &mut dyn StatusBackend,
) -> OpenResult<InputHandle> {
let path = match self.path_for_format(name) {
Ok(p) => p,
Err(e) => return OpenResult::Err(e),
};
let f = match super::try_open_file(&path) {
OpenResult::Ok(f) => f,
OpenResult::NotAvailable => return OpenResult::NotAvailable,
OpenResult::Err(e) => return OpenResult::Err(e),
};
OpenResult::Ok(InputHandle::new_read_only(
name,
BufReader::new(f),
InputOrigin::Other,
))
}
fn write_format(
&mut self,
name: &str,
data: &[u8],
_status: &mut dyn StatusBackend,
) -> Result<()> {
let final_path = self.path_for_format(OsStr::new(name))?;
let mut temp_dest = tempfile::Builder::new()
.prefix("format_")
.rand_bytes(6)
.tempfile_in(&self.formats_base)?;
temp_dest.write_all(data)?;
temp_dest.persist(&final_path)?;
Ok(())
}
}