use std::collections::BTreeMap;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use base64::engine::general_purpose::STANDARD;
use base64::Engine;
use sha2::{Digest, Sha256};
use super::input::extension;
pub(crate) struct ImageBlocks {
dir: PathBuf,
open: BTreeMap<u32, (String, String)>,
}
impl ImageBlocks {
pub(crate) fn new(dir: &Path) -> Self {
Self {
dir: dir.to_owned(),
open: BTreeMap::new(),
}
}
pub(crate) fn start(&mut self, index: u32, media_type: &str) {
self.open
.insert(index, (media_type.to_owned(), String::new()));
}
pub(crate) fn delta(&mut self, index: u32, frag: &str) {
if let Some((_, b64)) = self.open.get_mut(&index) {
b64.push_str(frag);
}
}
pub(crate) fn stop(&mut self, index: u32) -> io::Result<Option<PathBuf>> {
match self.open.remove(&index) {
Some((media_type, b64)) if !b64.is_empty() => {
write_image(&self.dir, &media_type, &b64).map(Some)
}
_ => Ok(None),
}
}
pub(crate) fn drop_all(&mut self) {
self.open.clear();
}
}
pub(crate) fn file_name(media_type: &str, bytes: &[u8]) -> String {
let digest = Sha256::digest(bytes);
let hex: String = digest.iter().take(6).map(|b| format!("{b:02x}")).collect();
format!("bz-{hex}.{}", extension(media_type))
}
pub(crate) fn write_image(dir: &Path, media_type: &str, b64: &str) -> io::Result<PathBuf> {
let bytes = STANDARD
.decode(b64)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let path = dir.join(file_name(media_type, &bytes));
fs::write(&path, bytes)?;
Ok(path)
}