bbox_map_server/
config.rs1use crate::wms_fcgi_backend::{MockFcgiBackend, QgisFcgiBackend, UmnFcgiBackend};
2use bbox_core::cli::CommonCommands;
3use bbox_core::config::{from_config_opt_or_exit, ConfigError};
4use bbox_core::service::ServiceConfig;
5use clap::{ArgMatches, FromArgMatches};
6use log::warn;
7use serde::Deserialize;
8use std::env;
9use std::ffi::OsStr;
10use std::fs::File;
11use std::path::Path;
12
13#[derive(Deserialize, Debug)]
14#[serde(default, deny_unknown_fields)]
15pub struct MapServiceCfg {
16 num_fcgi_processes: Option<usize>,
17 pub fcgi_client_pool_size: usize,
18 pub wait_timeout: Option<u64>,
19 pub create_timeout: Option<u64>,
20 pub recycle_timeout: Option<u64>,
21 pub qgis_backend: Option<QgisBackendCfg>,
22 pub umn_backend: Option<UmnBackendCfg>,
23 pub mock_backend: Option<MockBackendCfg>,
24 pub search_projects: bool,
25 pub default_project: Option<String>,
26}
27
28#[derive(Deserialize, Clone, Debug)]
29#[serde(deny_unknown_fields)]
30pub struct QgisBackendCfg {
31 pub exe_location: Option<String>,
32 pub project_basedir: String,
33 pub qgs: Option<QgisBackendSuffixCfg>,
34 pub qgz: Option<QgisBackendSuffixCfg>,
35}
36
37#[derive(Deserialize, Clone, Debug)]
38#[serde(deny_unknown_fields)]
39pub struct QgisBackendSuffixCfg {
40 pub path: String,
41}
42
43impl QgisBackendCfg {
44 pub fn new(basedir: &str) -> Self {
45 QgisBackendCfg {
46 exe_location: None,
47 project_basedir: basedir.to_string(),
48 qgs: Some(QgisBackendSuffixCfg {
49 path: "/qgis".to_string(),
50 }),
51 qgz: Some(QgisBackendSuffixCfg {
52 path: "/qgz".to_string(),
53 }),
54 }
55 }
56}
57
58#[derive(Deserialize, Clone, Debug)]
59#[serde(deny_unknown_fields)]
60pub struct UmnBackendCfg {
61 pub exe_location: Option<String>,
62 pub project_basedir: String,
63 pub path: String,
64}
65
66impl UmnBackendCfg {
67 pub fn new(basedir: &str) -> Self {
68 UmnBackendCfg {
69 exe_location: None,
70 project_basedir: basedir.to_string(),
71 path: "/wms/map".to_string(),
72 }
73 }
74}
75
76#[derive(Deserialize, Clone, Debug)]
77#[serde(deny_unknown_fields)]
78#[allow(dead_code)] pub struct MockBackendCfg {
80 pub path: String,
81}
82
83impl Default for MapServiceCfg {
84 fn default() -> Self {
85 let mut cfg = MapServiceCfg {
86 num_fcgi_processes: None,
87 fcgi_client_pool_size: 1,
88 wait_timeout: Some(90000),
89 create_timeout: Some(500),
90 recycle_timeout: Some(500),
91 qgis_backend: None,
92 umn_backend: None,
93 mock_backend: None,
94 search_projects: cfg!(feature = "inventory"),
96 default_project: None,
97 };
98 if let Ok(cwd) = env::current_dir().map(|p| p.into_os_string()) {
99 cfg.qgis_backend = Some(QgisBackendCfg::new(&cwd.to_string_lossy()));
100 cfg.umn_backend = Some(UmnBackendCfg::new(&cwd.to_string_lossy()));
101 }
102 cfg
103 }
104}
105
106impl ServiceConfig for MapServiceCfg {
107 fn initialize(cli: &ArgMatches) -> Result<Self, ConfigError> {
108 let has_qgis_config =
110 from_config_opt_or_exit::<QgisBackendCfg>("mapserver.qgis_backend").is_some();
111 let has_umn_config =
112 from_config_opt_or_exit::<UmnBackendCfg>("mapserver.umn_backend").is_some();
113 let mut cfg: MapServiceCfg = from_config_opt_or_exit("mapserver").unwrap_or_default();
114
115 if let Ok(CommonCommands::Serve(args)) = CommonCommands::from_arg_matches(cli) {
117 if let Some(file_or_url) = args.file_or_url {
118 match Path::new(&file_or_url).extension().and_then(OsStr::to_str) {
120 Some("qgs") | Some("qgz") => {
121 if let Some(backend) = cfg.qgis_backend.as_mut() {
122 if !has_qgis_config
123 && set_backend_basedir(&mut backend.project_basedir, &file_or_url)
124 {
125 cfg.default_project = Some(file_or_url);
126 }
127 }
128 }
129 Some("map") => {
130 if let Some(backend) = cfg.umn_backend.as_mut() {
131 if !has_umn_config
132 && set_backend_basedir(&mut backend.project_basedir, &file_or_url)
133 {
134 cfg.default_project = Some(file_or_url);
135 }
136 }
137 }
138 _ => { }
139 }
140 }
141 }
142 Ok(cfg)
143 }
144}
145
146impl MapServiceCfg {
147 pub fn num_fcgi_processes(&self) -> usize {
148 self.num_fcgi_processes.unwrap_or(num_cpus::get())
149 }
150}
151
152fn set_backend_basedir(project_basedir: &mut String, file_or_url: &str) -> bool {
153 if File::open(file_or_url).is_ok() {
154 if let Some(dir) = Path::new(file_or_url).parent() {
155 *project_basedir = dir.to_string_lossy().to_string();
156 }
157 true
158 } else {
159 warn!("Can't read file `{file_or_url}` - ignoring");
160 false
161 }
162}
163
164impl QgisBackendCfg {
165 pub fn backend(&self) -> QgisFcgiBackend {
166 QgisFcgiBackend::new(self.clone())
167 }
168}
169
170impl UmnBackendCfg {
171 pub fn backend(&self) -> UmnFcgiBackend {
172 UmnFcgiBackend::new(self.clone())
173 }
174}
175
176impl MockBackendCfg {
177 pub fn backend(&self) -> MockFcgiBackend {
178 MockFcgiBackend::new(self.clone())
179 }
180}