Procházet zdrojové kódy

Move tile sources configuration to separate file

Johannes Hofmann před 7 roky
rodič
revize
8eb2c1c3c2
3 změnil soubory, kde provedl 92 přidání a 26 odebrání
  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 Zobrazit soubor

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

+ 6
- 0
src/args.rs Zobrazit soubor

@@ -13,6 +13,12 @@ pub fn parse<'a>() -> clap::ArgMatches<'a> {
13 13
             .value_name("FILE")
14 14
             .help("Set a custom config file")
15 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 22
         .arg(Arg::with_name("fps")
17 23
             .long("fps")
18 24
             .value_name("FPS")

+ 82
- 24
src/config.rs Zobrazit soubor

@@ -6,7 +6,8 @@ use tile_source::TileSource;
6 6
 use toml::Value;
7 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 13
 #[derive(Debug)]
@@ -19,6 +20,8 @@ pub struct Config {
19 20
 }
20 21
 
21 22
 impl Config {
23
+    //TODO use builder pattern to create config
24
+
22 25
     pub fn from_arg_matches<'a>(matches: &clap::ArgMatches<'a>) -> Result<Config, String> {
23 26
         let mut config = if let Some(config_path) = matches.value_of_os("config") {
24 27
             Config::from_toml_file(config_path)?
@@ -26,6 +29,12 @@ impl Config {
26 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 38
         config.merge_arg_matches(matches);
30 39
 
31 40
         Ok(config)
@@ -45,7 +54,7 @@ impl 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 58
         if let Ok(xdg_dirs) = xdg::BaseDirectories::with_prefix("deltamap") {
50 59
             if let Some(config_path) = xdg_dirs.find_config_file("config.toml") {
51 60
                 info!("load config from path {:?}", config_path);
@@ -69,6 +78,30 @@ impl Config {
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 105
     /// Returns a tile cache directory path at a standard XDG cache location. The returned path may
73 106
     /// not exist.
74 107
     fn default_tile_cache_dir() -> Result<PathBuf, String> {
@@ -81,7 +114,7 @@ impl Config {
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 118
         match toml_str.parse::<Value>() {
86 119
             Ok(Value::Table(ref table)) => {
87 120
                 let tile_cache_dir = {
@@ -121,14 +154,44 @@ impl Config {
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 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 195
                     let min_zoom = source.get("min_zoom")
133 196
                         .unwrap_or_else(|| &Value::Integer(0))
134 197
                         .as_integer()
@@ -169,15 +232,18 @@ impl Config {
169 232
                         .as_str()
170 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 238
                     if name.contains('/') || name.contains('\\') {
173 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 243
                     path.push(name);
178 244
 
179
-                    sources_vec.push((
180
-                        name.clone(),
245
+                    self.sources.push((
246
+                        name.to_string(),
181 247
                         TileSource::new(
182 248
                             id as u32,
183 249
                             url_template.to_string(),
@@ -188,29 +254,20 @@ impl Config {
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 259
             Ok(_) => Err("TOML file has invalid structure. Expected a Table as the top-level element.".to_string()),
203 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 265
         let mut file = File::open(path).map_err(|e| format!("{}", e))?;
209 266
 
210 267
         let mut content = String::new();
211 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 273
     pub fn tile_sources(&self) -> &[(String, TileSource)] {
@@ -236,6 +293,7 @@ mod tests {
236 293
 
237 294
     #[test]
238 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
 }