bbox_tile_server/store/
files.rs1use 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 Ok(Some(response.with_body(Box::new(BufReader::new(f)))))
80 } else {
81 Ok(None)
82 }
83 }
84}