use color_eyre::{eyre::{bail, WrapErr}, Result}; use std::{ fs, io::ErrorKind, path::{Path, PathBuf}, }; pub trait Storage { fn prepare(&mut self) -> Result<()>; fn prepare_zoom(&mut self, zoom: u32) -> Result<()>; fn store(&mut self, zoom: u32, x: u64, y: u64, data: &[u8]) -> Result<()>; fn finish(&mut self) -> Result<()>; } #[derive(Debug)] pub struct Folder { base_dir: PathBuf, } impl Folder { pub fn new(base_dir: PathBuf) -> Self { Folder { base_dir } } } impl Storage for Folder { fn prepare(&mut self) -> Result<()> { let path = &self.base_dir; let metadata = fs::metadata(path); match metadata { Err(e) if e.kind() == ErrorKind::NotFound => { let parent = path.parent().unwrap_or_else(|| Path::new("/")); fs::create_dir(path) .context(format!("Could not create output directory at {parent:?}"))? } Err(e) => Err(e).context("Error while checking output directory")?, Ok(m) if m.is_dir() => (), Ok(_) => bail!("Output directory is not a directory"), } Ok(()) } fn prepare_zoom(&mut self, zoom: u32) -> Result<()> { let target = [&self.base_dir, &zoom.to_string().into()] .iter() .collect::(); fs::create_dir(target)?; Ok(()) } fn store(&mut self, zoom: u32, x: u64, y: u64, data: &[u8]) -> Result<()> { let folder = self.base_dir.join(zoom.to_string()).join(x.to_string()); let metadata = folder.metadata(); match metadata { Err(_) => fs::create_dir(&folder)?, Ok(m) if !m.is_dir() => bail!("Output path is not a directory"), _ => {} } let file = folder.join(format!("{y}.png")); fs::write(file, data)?; Ok(()) } fn finish(&mut self) -> Result<()> { Ok(()) } }