deltamap 0.1.0

A simple map viewer
use coord::MapCoord;
use projection::Projection;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::str::FromStr;
use toml::Value;
use toml::value::Table;
use toml;


#[derive(Debug)]
pub struct Session {
    pub view_center: MapCoord,
    pub zoom: f64,
    pub tile_source: Option<String>,
    pub projection: Projection,
}

impl Session {
    pub fn from_toml_file<P: AsRef<Path>>(path: P) -> Result<Session, String> {
        let mut file = File::open(&path).map_err(|e| format!("{}", e))?;

        let mut content = String::new();
        file.read_to_string(&mut content).map_err(|e| format!("{}", e))?;

        Session::from_toml_str(&content)
    }

    pub fn from_toml_str(toml_str: &str) -> Result<Session, String> {
        match toml_str.parse::<Value>() {
            Ok(Value::Table(ref table)) => {

                let view = match table.get("view") {
                    Some(&Value::Table(ref table)) => table,
                    Some(_) => return Err("view has to be a table.".to_string()),
                    None => return Err("view table is missing.".to_string()),
                };

                let x = match view.get("x") {
                    Some(&Value::Float(x)) => x,
                    Some(&Value::Integer(x)) => x as f64,
                    Some(_) => return Err("x has to be a number.".to_string()),
                    None => return Err("x position is missing.".to_string()),
                };

                let y = match view.get("y") {
                    Some(&Value::Float(y)) => y,
                    Some(&Value::Integer(y)) => y as f64,
                    Some(_) => return Err("y has to be a number.".to_string()),
                    None => return Err("y position is missing.".to_string()),
                };

                let zoom = match view.get("zoom") {
                    Some(&Value::Float(z)) => z,
                    Some(&Value::Integer(z)) => z as f64,
                    Some(_) => return Err("zoom has to be a number.".to_string()),
                    None => return Err("zoom value is missing.".to_string()),
                };

                let tile_source = match view.get("tile_source") {
                    Some(&Value::String(ref s)) => Some(s.clone()),
                    Some(_) => return Err("tile_source has to be a string.".to_string()),
                    None => None,
                };

                let projection = match view.get("projection") {
                    Some(&Value::String(ref s)) => {
                        Projection::from_str(s).unwrap_or_else(|_| Projection::Mercator)
                    },
                    Some(_) => return Err("projection has to be a string.".to_string()),
                    None => Projection::Mercator,
                };

                Ok(
                    Session {
                        view_center: MapCoord::new(x, y),
                        zoom,
                        tile_source,
                        projection,
                    }
                )
            },
            Ok(_) => Err("TOML file has invalid structure. Expected a Table as the top-level element.".to_string()),
            Err(e) => Err(format!("{}", e)),
        }
    }

    pub fn to_toml_string(&self) -> String {
        let mut view = Table::new();
        view.insert("x".to_string(), Value::Float(self.view_center.x));
        view.insert("y".to_string(), Value::Float(self.view_center.y));
        view.insert("zoom".to_string(), Value::Float(self.zoom));
        if let Some(ref tile_source) = self.tile_source {
            view.insert("tile_source".to_string(), Value::String(tile_source.clone()));
        }
        view.insert("projection".to_string(), Value::String(self.projection.to_str().to_string()));

        let mut root = Table::new();
        root.insert("view".to_string(), Value::Table(view));

        toml::ser::to_string_pretty(&Value::Table(root)).unwrap()
    }
}