aboutsummaryrefslogtreecommitdiff
path: root/src/storage.rs
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2025-06-26 22:10:31 +0200
committerDaniel Schadt <kingdread@gmx.de>2025-06-26 22:10:31 +0200
commit99150875308e0cac89f4de2996cfd1954305dcfe (patch)
treef19224064543aed367522b05778a992d7385c712 /src/storage.rs
parent6adcd94a6747fe7ec6f1ad1073453636847a0bff (diff)
downloadhittekaart-py.tar.gz
hittekaart-py.tar.bz2
hittekaart-py.zip
split crate into core and clipy
Diffstat (limited to 'src/storage.rs')
-rw-r--r--src/storage.rs165
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(())
- }
-}