diff options
Diffstat (limited to 'src/storage.rs')
-rw-r--r-- | src/storage.rs | 165 |
1 files changed, 0 insertions, 165 deletions
diff --git a/src/storage.rs b/src/storage.rs deleted file mode 100644 index 9e6b270..0000000 --- a/src/storage.rs +++ /dev/null @@ -1,165 +0,0 @@ -//! Abstractions over different storage backends. -//! -//! The main trait to use here is [`Storage`], which provides the necessary interface to store -//! tiles. Usually you want to have a `dyn Storage`, and then instantiate it with a concrete -//! implementation (either [`Folder`] or [`Sqlite`]), depending on the command line flags or -//! similar. -use color_eyre::{ - eyre::{bail, WrapErr}, - Result, -}; -use rusqlite::{params, Connection}; -use std::{ - fs, - io::ErrorKind, - path::{Path, PathBuf}, -}; - -/// The trait that provides the interface for storing tiles. -pub trait Storage { - /// Prepare the storage. - /// - /// This can be used to e.g. ensure the directory exists, or to create the database. - fn prepare(&mut self) -> Result<()>; - /// Prepare for a given zoom level. - /// - /// This function is called once per zoom, and can be used e.g. to set up the inner folder for - /// the level. This can avoid unnecesary syscalls if this setup would be done in - /// [`Storage::store`] instead. - fn prepare_zoom(&mut self, zoom: u32) -> Result<()>; - /// Store the given data for the tile. - fn store(&mut self, zoom: u32, x: u64, y: u64, data: &[u8]) -> Result<()>; - /// Finish the storing operation. - /// - /// This can flush any buffers, commit database changes, and so on. - fn finish(&mut self) -> Result<()>; -} - -/// Folder-based storage. -/// -/// This stores the tiles according to the [slippy map -/// tilenames](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames). -#[derive(Debug)] -pub struct Folder { - base_dir: PathBuf, -} - -impl Folder { - /// Create a new folder based storage. - /// - /// The given directory is the "root" directory, so a tile would be saved as - /// `base_dir/{zoom}/{x}/{y}.png`. - 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::<PathBuf>(); - 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(()) - } -} - -/// SQLite based storage. -/// -/// This stores tiles in a SQLite database. The database will have a single table: -/// -/// ```sql -/// CREATE TABLE tiles ( -/// zoom INTEGER, -/// x INTEGER, -/// y INTEGER, -/// data BLOB, -/// PRIMARY KEY (zoom, x, y) -/// ); -/// ``` -#[derive(Debug)] -pub struct Sqlite { - connection: Connection, -} - -impl Sqlite { - /// Create a new SQLite backed tile store. - /// - /// The database will be saved at the given location. Note that the database must not yet - /// exist. - pub fn connect<P: AsRef<Path>>(file: P) -> Result<Self> { - let path = file.as_ref(); - if fs::metadata(path).is_ok() { - bail!("Path {path:?} already exists, refusing to open") - } - let connection = Connection::open(path)?; - Ok(Sqlite { connection }) - } -} - -impl Storage for Sqlite { - fn prepare(&mut self) -> Result<()> { - self.connection.execute( - "CREATE TABLE tiles ( - zoom INTEGER, - x INTEGER, - y INTEGER, - data BLOB, - PRIMARY KEY (zoom, x, y) - );", - (), - )?; - self.connection.execute("BEGIN;", ())?; - Ok(()) - } - - fn prepare_zoom(&mut self, _zoom: u32) -> Result<()> { - Ok(()) - } - - fn store(&mut self, zoom: u32, x: u64, y: u64, data: &[u8]) -> Result<()> { - self.connection.execute( - "INSERT INTO tiles (zoom, x, y, data) VALUES (?, ?, ?, ?)", - params![zoom, x, y, data], - )?; - Ok(()) - } - - fn finish(&mut self) -> Result<()> { - self.connection.execute("COMMIT;", ())?; - Ok(()) - } -} |