1use serde::Deserialize;
4use std::fs::File;
5use std::io::Read;
6use tile_grid::{Extent, Grid, Origin, Unit};
7use toml::Value;
8
9#[derive(Deserialize, Clone, Debug)]
12pub struct ExtentCfg {
13 pub minx: f64,
14 pub miny: f64,
15 pub maxx: f64,
16 pub maxy: f64,
17}
18
19impl From<&ExtentCfg> for Extent {
20 fn from(cfg: &ExtentCfg) -> Extent {
21 Extent {
22 minx: cfg.minx,
23 miny: cfg.miny,
24 maxx: cfg.maxx,
25 maxy: cfg.maxy,
26 }
27 }
28}
29
30pub trait FromGridCfg {
31 fn from_config(grid_cfg: &GridCfg) -> Result<Self, String>
32 where
33 Self: Sized;
34}
35
36impl FromGridCfg for Grid {
37 fn from_config(grid_cfg: &GridCfg) -> Result<Self, String> {
38 if let Some(ref gridname) = grid_cfg.predefined {
39 match gridname.as_str() {
40 "wgs84" => Ok(Grid::wgs84()),
41 "web_mercator" => Ok(Grid::web_mercator()),
42 _ => Err(format!("Unkown grid '{gridname}'")),
43 }
44 } else if let Some(ref usergrid) = grid_cfg.user {
45 let units = match &usergrid.units.to_lowercase() as &str {
46 "m" => Ok(Unit::Meters),
47 "dd" => Ok(Unit::Degrees),
48 "ft" => Ok(Unit::Feet),
49 _ => Err(format!("Unexpected enum value '{}'", usergrid.units)),
50 };
51 let origin = match &usergrid.origin as &str {
52 "TopLeft" => Ok(Origin::TopLeft),
53 "BottomLeft" => Ok(Origin::BottomLeft),
54 _ => Err(format!("Unexpected enum value '{}'", usergrid.origin)),
55 };
56 let grid = Grid::new(
57 usergrid.width,
58 usergrid.height,
59 Extent::from(&usergrid.extent),
60 usergrid.srid,
61 units?,
62 usergrid.resolutions.clone(),
63 origin?,
64 );
65 Ok(grid)
66 } else {
67 Err("Invalid grid definition".to_string())
68 }
69 }
70}
71
72#[derive(Deserialize, Clone, Debug)]
75pub struct ApplicationCfg {
76 pub service: ServiceCfg,
77 pub datasource: Vec<DatasourceCfg>,
78 pub grid: GridCfg,
79 #[serde(rename = "tileset")]
80 pub tilesets: Vec<TilesetCfg>,
81 pub cache: Option<CacheCfg>,
82 pub webserver: WebserverCfg,
83}
84
85#[derive(Deserialize, Clone, Debug)]
86pub struct ServiceCfg {
87 pub mvt: ServiceMvtCfg,
88}
89
90#[derive(Deserialize, Clone, Debug)]
91pub struct ServiceMvtCfg {
92 pub viewer: bool,
93}
94
95#[derive(Deserialize, Clone, Debug)]
96pub struct DatasourceCfg {
97 pub name: Option<String>,
98 pub default: Option<bool>,
99 pub dbconn: Option<String>,
101 pub pool: Option<u16>,
102 pub connection_timeout: Option<u64>,
103 pub path: Option<String>,
105}
106
107#[derive(Deserialize, Clone, Debug)]
108pub struct GridCfg {
109 pub predefined: Option<String>,
110 pub user: Option<UserGridCfg>,
111}
112
113#[derive(Deserialize, Clone, Debug)]
114pub struct UserGridCfg {
115 pub width: u16,
117 pub height: u16,
118 pub extent: ExtentCfg,
124 pub srid: i32,
126 pub units: String,
128 #[serde(default)]
135 pub resolutions: Vec<f64>,
136 pub origin: String,
138}
139
140#[derive(Deserialize, Clone, Debug)]
141pub struct TilesetCfg {
142 pub name: String,
143 pub extent: Option<ExtentCfg>,
144 pub minzoom: Option<u8>,
145 pub maxzoom: Option<u8>,
146 pub center: Option<(f64, f64)>,
147 pub start_zoom: Option<u8>,
148 pub attribution: Option<String>,
149 #[serde(rename = "layer")]
150 pub layers: Vec<LayerCfg>,
151 pub style: Option<serde_json::Value>,
153 pub cache_limits: Option<TilesetCacheCfg>,
154}
155
156#[derive(Deserialize, Clone, Debug)]
157pub struct LayerQueryCfg {
158 #[serde(default)]
159 pub minzoom: u8,
160 pub maxzoom: Option<u8>,
161 pub simplify: Option<bool>,
163 pub tolerance: Option<String>,
165 pub sql: Option<String>,
166}
167
168#[derive(Deserialize, Clone, Debug)]
169pub struct LayerCfg {
170 pub name: String,
171 pub datasource: Option<String>,
172 pub geometry_field: Option<String>,
173 pub geometry_type: Option<String>,
174 pub srid: Option<i32>,
176 #[serde(default)]
178 pub no_transform: bool,
179 pub fid_field: Option<String>,
180 pub table_name: Option<String>,
182 pub query_limit: Option<u32>,
183 #[serde(default)]
185 pub query: Vec<LayerQueryCfg>,
186 pub minzoom: Option<u8>,
187 pub maxzoom: Option<u8>,
188 #[serde(default = "default_tile_size")]
190 pub tile_size: u32,
191 #[serde(default)]
193 pub simplify: bool,
194 #[serde(default = "default_tolerance")]
196 pub tolerance: String,
197 pub buffer_size: Option<u32>,
199 #[serde(default)]
201 pub make_valid: bool,
202 #[serde(default)]
204 pub shift_longitude: bool,
205 pub style: Option<serde_json::Value>,
207}
208
209pub fn default_tile_size() -> u32 {
210 4096
211}
212
213pub const DEFAULT_TOLERANCE: &str = "!pixel_width!/2";
214
215pub fn default_tolerance() -> String {
216 DEFAULT_TOLERANCE.to_string()
217}
218
219#[derive(Deserialize, Clone, Debug)]
220pub struct TilesetCacheCfg {
221 #[serde(default)]
222 pub minzoom: u8,
223 pub maxzoom: Option<u8>,
224 #[serde(default)]
225 pub no_cache: bool,
226}
227
228#[derive(Deserialize, Clone, Debug)]
229pub struct CacheCfg {
230 pub file: Option<CacheFileCfg>,
231 pub s3: Option<S3CacheFileCfg>,
232}
233
234#[derive(Deserialize, Clone, Debug)]
235pub struct CacheFileCfg {
236 pub base: String,
237 pub baseurl: Option<String>,
238}
239
240#[derive(Deserialize, Clone, Debug)]
241pub struct S3CacheFileCfg {
242 pub endpoint: String,
243 pub bucket: String,
244 pub access_key: String,
245 pub secret_key: String,
246 pub region: String,
247 pub baseurl: Option<String>,
248 pub key_prefix: Option<String>,
249 pub gzip_header_enabled: Option<bool>,
250}
251
252#[derive(Deserialize, Clone, Debug)]
253pub struct WebserverCfg {
254 pub bind: Option<String>,
255 pub port: Option<u16>,
256 pub threads: Option<u8>,
257 pub cache_control_max_age: Option<u32>,
260 #[serde(rename = "static", default)]
261 pub static_: Vec<WebserverStaticCfg>,
262}
263
264#[derive(Deserialize, Clone, Debug)]
265pub struct WebserverStaticCfg {
266 pub path: String,
267 pub dir: String,
268}
269
270pub fn read_config<'a, T: Deserialize<'a>>(path: &str) -> Result<T, String> {
272 let mut file = match File::open(path) {
273 Ok(file) => file,
274 Err(_) => {
275 return Err("Could not find config file!".to_string());
276 }
277 };
278 let mut config_toml = String::new();
279 if let Err(err) = file.read_to_string(&mut config_toml) {
280 return Err(format!("Error while reading config: [{}]", err));
281 };
282
283 parse_config(config_toml, path)
284}
285
286pub fn parse_config<'a, T: Deserialize<'a>>(config_toml: String, path: &str) -> Result<T, String> {
288 let toml = config_toml; toml.parse::<Value>()
312 .and_then(|cfg| cfg.try_into::<T>())
313 .map_err(|err| format!("{} - {}", path, err))
314}