Browse Source

Move tile sources configuration to separate file

Johannes Hofmann 7 years ago
parent
commit
8eb2c1c3c2
3 changed files with 92 additions and 26 deletions
  1. 4
    2
      default_tile_sources.toml
  2. 6
    0
      src/args.rs
  3. 82
    24
      src/config.rs

default_config.toml → default_tile_sources.toml View File

1
-[tile_sources.OSM]
1
+[[tile_sources]]
2
+name = "OSM"
2
 max_zoom = 19
3
 max_zoom = 19
3
 url_template = "http://a.tile.openstreetmap.org/{z}/{x}/{y}.png"
4
 url_template = "http://a.tile.openstreetmap.org/{z}/{x}/{y}.png"
4
 extension = "png"
5
 extension = "png"
5
 
6
 
6
-[tile_sources.esri]
7
+[[tile_sources]]
8
+name = "esri"
7
 max_zoom = 19
9
 max_zoom = 19
8
 url_template = "https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
10
 url_template = "https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
9
 extension = "jpg"
11
 extension = "jpg"

+ 6
- 0
src/args.rs View File

13
             .value_name("FILE")
13
             .value_name("FILE")
14
             .help("Set a custom config file")
14
             .help("Set a custom config file")
15
             .takes_value(true))
15
             .takes_value(true))
16
+        .arg(Arg::with_name("tile-sources")
17
+            .short("t")
18
+            .long("tile-sources")
19
+            .value_name("FILE")
20
+            .help("Set a custom tile sources file")
21
+            .takes_value(true))
16
         .arg(Arg::with_name("fps")
22
         .arg(Arg::with_name("fps")
17
             .long("fps")
23
             .long("fps")
18
             .value_name("FPS")
24
             .value_name("FPS")

+ 82
- 24
src/config.rs View File

6
 use toml::Value;
6
 use toml::Value;
7
 use xdg;
7
 use xdg;
8
 
8
 
9
-static DEFAULT_CONFIG: &'static str = include_str!("../default_config.toml");
9
+static DEFAULT_CONFIG: &'static str = "";
10
+static DEFAULT_TILE_SOURCES: &'static str = include_str!("../default_tile_sources.toml");
10
 
11
 
11
 
12
 
12
 #[derive(Debug)]
13
 #[derive(Debug)]
19
 }
20
 }
20
 
21
 
21
 impl Config {
22
 impl Config {
23
+    //TODO use builder pattern to create config
24
+
22
     pub fn from_arg_matches<'a>(matches: &clap::ArgMatches<'a>) -> Result<Config, String> {
25
     pub fn from_arg_matches<'a>(matches: &clap::ArgMatches<'a>) -> Result<Config, String> {
23
         let mut config = if let Some(config_path) = matches.value_of_os("config") {
26
         let mut config = if let Some(config_path) = matches.value_of_os("config") {
24
             Config::from_toml_file(config_path)?
27
             Config::from_toml_file(config_path)?
26
             Config::find_or_create()?
29
             Config::find_or_create()?
27
         };
30
         };
28
 
31
 
32
+        if let Some(tile_sources_path) = matches.value_of_os("tile-sources") {
33
+            config.add_tile_sources_from_file(tile_sources_path)?;
34
+        } else {
35
+            config.add_tile_sources_from_default_or_create()?;
36
+        };
37
+
29
         config.merge_arg_matches(matches);
38
         config.merge_arg_matches(matches);
30
 
39
 
31
         Ok(config)
40
         Ok(config)
45
         }
54
         }
46
     }
55
     }
47
 
56
 
48
-    pub fn find_or_create() -> Result<Config, String> {
57
+    fn find_or_create() -> Result<Config, String> {
49
         if let Ok(xdg_dirs) = xdg::BaseDirectories::with_prefix("deltamap") {
58
         if let Ok(xdg_dirs) = xdg::BaseDirectories::with_prefix("deltamap") {
50
             if let Some(config_path) = xdg_dirs.find_config_file("config.toml") {
59
             if let Some(config_path) = xdg_dirs.find_config_file("config.toml") {
51
                 info!("load config from path {:?}", config_path);
60
                 info!("load config from path {:?}", config_path);
69
         }
78
         }
70
     }
79
     }
71
 
80
 
81
+    fn add_tile_sources_from_default_or_create(&mut self) -> Result<(), String> {
82
+        if let Ok(xdg_dirs) = xdg::BaseDirectories::with_prefix("deltamap") {
83
+            if let Some(sources_path) = xdg_dirs.find_config_file("tile_sources.toml") {
84
+                info!("load tile sources from path {:?}", sources_path);
85
+
86
+                self.add_tile_sources_from_file(sources_path)
87
+            } else {
88
+                // try to write a default tile sources file
89
+                if let Ok(path) = xdg_dirs.place_config_file("tile_sources.toml") {
90
+                    if let Ok(mut file) = File::create(&path) {
91
+                        if file.write_all(DEFAULT_TILE_SOURCES.as_bytes()).is_ok() {
92
+                            info!("write default tile sources to {:?}", &path);
93
+                        }
94
+                    }
95
+                }
96
+
97
+                self.add_tile_sources_from_str(DEFAULT_TILE_SOURCES)
98
+            }
99
+        } else {
100
+            info!("load default config");
101
+            self.add_tile_sources_from_str(DEFAULT_TILE_SOURCES)
102
+        }
103
+    }
104
+
72
     /// Returns a tile cache directory path at a standard XDG cache location. The returned path may
105
     /// Returns a tile cache directory path at a standard XDG cache location. The returned path may
73
     /// not exist.
106
     /// not exist.
74
     fn default_tile_cache_dir() -> Result<PathBuf, String> {
107
     fn default_tile_cache_dir() -> Result<PathBuf, String> {
81
         }
114
         }
82
     }
115
     }
83
 
116
 
84
-    pub fn from_toml_str(toml_str: &str) -> Result<Config, String> {
117
+    fn from_toml_str(toml_str: &str) -> Result<Config, String> {
85
         match toml_str.parse::<Value>() {
118
         match toml_str.parse::<Value>() {
86
             Ok(Value::Table(ref table)) => {
119
             Ok(Value::Table(ref table)) => {
87
                 let tile_cache_dir = {
120
                 let tile_cache_dir = {
121
                     }
154
                     }
122
                 };
155
                 };
123
 
156
 
124
-                let sources_table = table.get("tile_sources")
157
+                Ok(
158
+                    Config {
159
+                        tile_cache_dir: tile_cache_dir,
160
+                        sources: vec![],
161
+                        fps: fps,
162
+                        use_network: use_network,
163
+                        async: async,
164
+                    }
165
+                )
166
+            },
167
+            Ok(_) => Err("TOML file has invalid structure. Expected a Table as the top-level element.".to_string()),
168
+            Err(e) => Err(format!("{}", e)),
169
+        }
170
+    }
171
+
172
+    fn from_toml_file<P: AsRef<Path>>(path: P) -> Result<Config, String> {
173
+        let mut file = File::open(path).map_err(|e| format!("{}", e))?;
174
+
175
+        let mut content = String::new();
176
+        file.read_to_string(&mut content).map_err(|e| format!("{}", e))?;
177
+
178
+        Config::from_toml_str(&content)
179
+    }
180
+
181
+    fn add_tile_sources_from_str(&mut self, toml_str: &str) -> Result<(), String> {
182
+        match toml_str.parse::<Value>() {
183
+            Ok(Value::Table(ref table)) => {
184
+                let sources_array = table.get("tile_sources")
125
                     .ok_or_else(|| "missing \"tile_sources\" table".to_string())?
185
                     .ok_or_else(|| "missing \"tile_sources\" table".to_string())?
126
-                    .as_table()
127
-                    .ok_or_else(|| "\"tile_sources\" has to be a table".to_string())?;
186
+                    .as_array()
187
+                    .ok_or_else(|| "\"tile_sources\" has to be an array.".to_string())?;
128
 
188
 
129
-                let mut sources_vec: Vec<(String, TileSource)> = Vec::with_capacity(sources_table.len());
189
+                for (id, source) in sources_array.iter().enumerate() {
190
+                    let name = source.get("name")
191
+                        .ok_or_else(|| "tile_source is missing \"name\" entry.".to_string())?
192
+                        .as_str()
193
+                        .ok_or_else(|| "\"name\" has to be a string".to_string())?;
130
 
194
 
131
-                for (id, (name, source)) in sources_table.iter().enumerate() {
132
                     let min_zoom = source.get("min_zoom")
195
                     let min_zoom = source.get("min_zoom")
133
                         .unwrap_or_else(|| &Value::Integer(0))
196
                         .unwrap_or_else(|| &Value::Integer(0))
134
                         .as_integer()
197
                         .as_integer()
169
                         .as_str()
232
                         .as_str()
170
                         .ok_or_else(|| "extension has to be a string".to_string())?;
233
                         .ok_or_else(|| "extension has to be a string".to_string())?;
171
 
234
 
235
+                    //TODO reduce allowed strings to a reasonable subset of valid UTF-8 strings
236
+                    // that can also be used as a directory name or introduce a dir_name key with
237
+                    // more restrictions.
172
                     if name.contains('/') || name.contains('\\') {
238
                     if name.contains('/') || name.contains('\\') {
173
                         return Err(format!("source name ({:?}) must not contain slashes (\"/\" or \"\\\")", name));
239
                         return Err(format!("source name ({:?}) must not contain slashes (\"/\" or \"\\\")", name));
174
                     }
240
                     }
175
 
241
 
176
-                    let mut path = PathBuf::from(&tile_cache_dir);
242
+                    let mut path = PathBuf::from(&self.tile_cache_dir);
177
                     path.push(name);
243
                     path.push(name);
178
 
244
 
179
-                    sources_vec.push((
180
-                        name.clone(),
245
+                    self.sources.push((
246
+                        name.to_string(),
181
                         TileSource::new(
247
                         TileSource::new(
182
                             id as u32,
248
                             id as u32,
183
                             url_template.to_string(),
249
                             url_template.to_string(),
188
                         ),
254
                         ),
189
                     ));
255
                     ));
190
                 }
256
                 }
191
-
192
-                Ok(
193
-                    Config {
194
-                        tile_cache_dir: tile_cache_dir,
195
-                        sources: sources_vec,
196
-                        fps: fps,
197
-                        use_network: use_network,
198
-                        async: async,
199
-                    }
200
-                )
257
+                Ok(())
201
             },
258
             },
202
             Ok(_) => Err("TOML file has invalid structure. Expected a Table as the top-level element.".to_string()),
259
             Ok(_) => Err("TOML file has invalid structure. Expected a Table as the top-level element.".to_string()),
203
             Err(e) => Err(format!("{}", e)),
260
             Err(e) => Err(format!("{}", e)),
204
         }
261
         }
205
     }
262
     }
206
 
263
 
207
-    pub fn from_toml_file<P: AsRef<Path>>(path: P) -> Result<Config, String> {
264
+    fn add_tile_sources_from_file<P: AsRef<Path>>(&mut self, path: P) -> Result<(), String> {
208
         let mut file = File::open(path).map_err(|e| format!("{}", e))?;
265
         let mut file = File::open(path).map_err(|e| format!("{}", e))?;
209
 
266
 
210
         let mut content = String::new();
267
         let mut content = String::new();
211
         file.read_to_string(&mut content).map_err(|e| format!("{}", e))?;
268
         file.read_to_string(&mut content).map_err(|e| format!("{}", e))?;
212
 
269
 
213
-        Config::from_toml_str(&content)
270
+        self.add_tile_sources_from_str(&content)
214
     }
271
     }
215
 
272
 
216
     pub fn tile_sources(&self) -> &[(String, TileSource)] {
273
     pub fn tile_sources(&self) -> &[(String, TileSource)] {
236
 
293
 
237
     #[test]
294
     #[test]
238
     fn default_config() {
295
     fn default_config() {
239
-        assert!(Config::from_toml_str(DEFAULT_CONFIG).is_ok())
296
+        let mut config = Config::from_toml_str(DEFAULT_CONFIG).unwrap();
297
+        config.add_tile_sources_from_str(DEFAULT_TILE_SOURCES).unwrap();
240
     }
298
     }
241
 }
299
 }