bbox_tile_server/store/
files.rs

1use crate::config::{FileStoreCfg, StoreCompressionCfg};
2use crate::store::{CacheLayout, TileReader, TileStoreError, TileWriter};
3use async_trait::async_trait;
4use bbox_core::{Compression, Format, TileResponse};
5use log::debug;
6use std::fs::{self, File};
7use std::io::{self, BufReader, BufWriter};
8use std::path::PathBuf;
9use tile_grid::Xyz;
10
11#[derive(Clone, Debug)]
12pub struct FileStore {
13    pub(crate) base_dir: PathBuf,
14    compression: StoreCompressionCfg,
15    format: Format,
16}
17
18impl FileStore {
19    pub fn new(base_dir: PathBuf, compression: StoreCompressionCfg, format: Format) -> Self {
20        FileStore {
21            base_dir,
22            compression,
23            format,
24        }
25    }
26    pub fn from_config(
27        cfg: &FileStoreCfg,
28        compression: &Option<StoreCompressionCfg>,
29        tileset_name: &str,
30        format: &Format,
31    ) -> Self {
32        let base_dir = cfg.abs_path().join(PathBuf::from(tileset_name));
33        let compression = compression.clone().unwrap_or(StoreCompressionCfg::None);
34        Self::new(base_dir, compression, *format)
35    }
36    #[allow(dead_code)]
37    pub fn remove_dir_all(&self) -> std::io::Result<()> {
38        fs::remove_dir_all(self.base_dir.as_path())
39    }
40}
41
42#[async_trait]
43impl TileWriter for FileStore {
44    fn compression(&self) -> Compression {
45        match self.compression {
46            StoreCompressionCfg::Gzip => Compression::Gzip,
47            StoreCompressionCfg::None => Compression::None,
48        }
49    }
50    async fn exists(&self, xyz: &Xyz) -> bool {
51        let p = CacheLayout::Zxy.path(&self.base_dir, xyz, &self.format);
52        p.exists()
53    }
54    async fn put_tile(&self, xyz: &Xyz, data: Vec<u8>) -> Result<(), TileStoreError> {
55        let fullpath = CacheLayout::Zxy.path(&self.base_dir, xyz, &self.format);
56        let p = fullpath.as_path();
57        fs::create_dir_all(p.parent().unwrap())
58            .map_err(|e| TileStoreError::FileError(p.parent().unwrap().into(), e))?;
59        debug!("Writing {}", fullpath.display());
60        let mut writer = BufWriter::new(
61            File::create(&fullpath).map_err(|e| TileStoreError::FileError(fullpath.clone(), e))?,
62        );
63        io::copy(&mut data.as_slice(), &mut writer)
64            .map_err(|e| TileStoreError::FileError(fullpath.clone(), e))?;
65        Ok(())
66    }
67}
68
69#[async_trait]
70impl TileReader for FileStore {
71    async fn get_tile(&self, xyz: &Xyz) -> Result<Option<TileResponse>, TileStoreError> {
72        let p = CacheLayout::Zxy.path(&self.base_dir, xyz, &self.format);
73        if let Ok(f) = File::open(p) {
74            let mut response = TileResponse::new();
75            if self.compression == StoreCompressionCfg::Gzip {
76                response.insert_header(("Content-Encoding", "gzip"));
77            }
78            // TODO: Set content_type from `format`
79            Ok(Some(response.with_body(Box::new(BufReader::new(f)))))
80        } else {
81            Ok(None)
82        }
83    }
84}