wof 1.0.0

The Who's On First rust library and command line.
Documentation
use crate::types::{MultiPolygon, Point, Polygon, Polyline};
use crate::{JsonObject, JsonValue};

/// Trait for JsonValue, it adds some useful features.
pub trait JsonUtils {
  fn as_json_value(&self) -> &JsonValue;
  fn as_mut_json_value(&mut self) -> &mut JsonValue;
  fn assert_is_object(&self) -> Result<(), String> {
    match &self.as_json_value() {
      JsonValue::Object(_) => Ok(()),
      _ => Err(format!(
        "This is not an object but a {}",
        self.type_as_string()
      )),
    }
  }
  fn assert_is_array(&self) -> Result<(), String> {
    match &self.as_json_value() {
      JsonValue::Array(_) => Ok(()),
      _ => Err(format!(
        "This is not an array but a {}",
        self.type_as_string()
      )),
    }
  }
  fn assert_is_number(&self) -> Result<(), String> {
    match &self.as_json_value() {
      JsonValue::Number(_) => Ok(()),
      _ => Err(format!(
        "This is not a number but a {}",
        self.type_as_string()
      )),
    }
  }
  fn assert_is_string(&self) -> Result<(), String> {
    match &self.as_json_value() {
      JsonValue::String(_) => Ok(()),
      JsonValue::Short(_) => Ok(()),
      _ => Err(format!(
        "This is not a String but a {}",
        self.type_as_string()
      )),
    }
  }
  fn as_object(&self) -> Option<&JsonObject> {
    match &self.as_json_value() {
      JsonValue::Object(obj) => Some(obj),
      _ => None,
    }
  }
  fn as_mut_object(&mut self) -> Option<&mut JsonObject> {
    match self.as_mut_json_value() {
      JsonValue::Object(obj) => Some(obj),
      _ => None,
    }
  }
  fn keys(&self) -> Vec<String> {
    match &self.as_json_value() {
      JsonValue::Object(obj) => {
        let mut keys = Vec::new();
        for (k, _) in obj.iter() {
          keys.push(k.to_string());
        }
        keys
      }
      _ => Vec::new(),
    }
  }
  fn as_array(&self) -> Option<&Vec<JsonValue>> {
    match &self.as_json_value() {
      JsonValue::Array(array) => Some(array),
      _ => None,
    }
  }
  fn type_as_string(&self) -> &str {
    match &self.as_json_value() {
      JsonValue::Object(_) => "Object",
      JsonValue::Array(_) => "Array",
      JsonValue::Number(_) => "Number",
      JsonValue::String(_) => "String",
      JsonValue::Short(_) => "Short",
      JsonValue::Boolean(_) => "Boolean",
      JsonValue::Null => "Null",
    }
  }
}

impl JsonUtils for JsonValue {
  fn as_json_value(&self) -> &JsonValue {
    &self
  }
  fn as_mut_json_value(&mut self) -> &mut JsonValue {
    self
  }
}

/// Trait for JsonValue, it adds some useful features when the JsonValue contains GeoJSON coordinates.
pub trait GeoJsonUtils {
  fn as_json_value(&self) -> &JsonValue;
  fn as_mut_json_value(&mut self) -> &mut JsonValue;

  fn as_geom_point(&self) -> Option<Point> {
    if !self.as_json_value().is_array() {
      return None;
    }
    let array = self.as_json_value().as_array().unwrap();
    let mut point = vec![];
    for p in array.iter() {
      if p.is_number() {
        point.push(p.as_f64().unwrap())
      } else {
        return None;
      }
    }
    Some(point)
  }

  fn as_geom_multi_point(&self) -> Option<Vec<Point>> {
    if !self.as_json_value().is_array() {
      return None;
    }
    let array = self.as_json_value().as_array().unwrap();
    let mut multi_point = vec![];
    for point in array.iter() {
      if let Some(point) = point.as_geom_point() {
        multi_point.push(point)
      } else {
        return None;
      }
    }
    Some(multi_point)
  }

  fn as_geom_line(&self) -> Option<Polyline> {
    self.as_geom_multi_point()
  }

  fn as_geom_multi_line(&self) -> Option<Vec<Polyline>> {
    if !self.as_json_value().is_array() {
      return None;
    }
    let array = self.as_json_value().as_array().unwrap();
    let mut multi_line = vec![];
    for line in array.iter() {
      if let Some(line) = line.as_geom_line() {
        multi_line.push(line)
      } else {
        return None;
      }
    }
    Some(multi_line)
  }

  fn as_geom_polygon(&self) -> Option<Polygon> {
    self.as_geom_multi_line()
  }

  fn as_geom_multi_polygon(&self) -> Option<MultiPolygon> {
    if !self.as_json_value().is_array() {
      return None;
    }
    let array = self.as_json_value().as_array().unwrap();
    let mut multi_polygon = vec![];
    for polygon in array.iter() {
      if let Some(polygon) = polygon.as_geom_polygon() {
        multi_polygon.push(polygon)
      } else {
        return None;
      }
    }
    Some(multi_polygon)
  }
}

impl GeoJsonUtils for JsonValue {
  fn as_json_value(&self) -> &JsonValue {
    &self
  }
  fn as_mut_json_value(&mut self) -> &mut JsonValue {
    self
  }
}

#[cfg(test)]
mod test_geojson {
  use super::*;
  use json::array;

  #[test]
  pub fn point() {
    let point = array![30.0, 10.0];
    assert_eq!(point.as_geom_point(), Some(vec![30.0, 10.0]));
    assert_eq!(point.as_geom_line(), None);
    assert_eq!(point.as_geom_polygon(), None);
  }

  #[test]
  pub fn line() {
    let line = array![array![30.0, 10.0], array![10.0, 30.0], array![40.0, 40.0]];
    let expect = Some(vec![vec![30.0, 10.0], vec![10.0, 30.0], vec![40.0, 40.0]]);
    assert_eq!(line.as_geom_line(), expect);
    assert_eq!(line.as_geom_multi_point(), expect);
    assert_eq!(line.as_geom_point(), None);
    assert_eq!(line.as_geom_polygon(), None);
  }

  #[test]
  pub fn polygon() {
    let polygon = array![array![
      array![30.0, 10.0],
      array![40.0, 40.0],
      array![20.0, 40.0],
      array![10.0, 20.0],
      array![30.0, 10.0]
    ]];
    let expect = Some(vec![vec![
      vec![30.0, 10.0],
      vec![40.0, 40.0],
      vec![20.0, 40.0],
      vec![10.0, 20.0],
      vec![30.0, 10.0],
    ]]);
    assert_eq!(polygon.as_geom_polygon(), expect);
    assert_eq!(polygon.as_geom_multi_line(), expect);
    assert_eq!(polygon.as_geom_point(), None);
    assert_eq!(polygon.as_geom_line(), None);
  }

  #[test]
  pub fn polygon_inner() {
    let polygon = array![
      array![
        array![35.0, 10.0],
        array![45.0, 45.0],
        array![15.0, 40.0],
        array![10.0, 20.0],
        array![35.0, 10.0]
      ],
      array![
        array![20.0, 30.0],
        array![35.0, 35.0],
        array![30.0, 20.0],
        array![20.0, 30.0]
      ]
    ];
    let expect = Some(vec![
      vec![
        vec![35.0, 10.0],
        vec![45.0, 45.0],
        vec![15.0, 40.0],
        vec![10.0, 20.0],
        vec![35.0, 10.0],
      ],
      vec![
        vec![20.0, 30.0],
        vec![35.0, 35.0],
        vec![30.0, 20.0],
        vec![20.0, 30.0],
      ],
    ]);
    assert_eq!(polygon.as_geom_polygon(), expect);
    assert_eq!(polygon.as_geom_multi_line(), expect);
    assert_eq!(polygon.as_geom_point(), None);
    assert_eq!(polygon.as_geom_line(), None);
  }

  #[test]
  pub fn multi_polygon() {
    let multi_polygon = array![
      array![array![
        array![40.0, 40.0],
        array![20.0, 45.0],
        array![45.0, 30.0],
        array![40.0, 40.0]
      ]],
      array![
        array![
          array![20.0, 35.0],
          array![10.0, 30.0],
          array![10.0, 10.0],
          array![30.0, 5.0],
          array![45.0, 20.0],
          array![20.0, 35.0]
        ],
        array![
          array![30.0, 20.0],
          array![20.0, 15.0],
          array![20.0, 25.0],
          array![30.0, 20.0]
        ]
      ]
    ];
    let expect = Some(vec![
      vec![vec![
        vec![40.0, 40.0],
        vec![20.0, 45.0],
        vec![45.0, 30.0],
        vec![40.0, 40.0],
      ]],
      vec![
        vec![
          vec![20.0, 35.0],
          vec![10.0, 30.0],
          vec![10.0, 10.0],
          vec![30.0, 5.0],
          vec![45.0, 20.0],
          vec![20.0, 35.0],
        ],
        vec![
          vec![30.0, 20.0],
          vec![20.0, 15.0],
          vec![20.0, 25.0],
          vec![30.0, 20.0],
        ],
      ],
    ]);
    assert_eq!(multi_polygon.as_geom_multi_polygon(), expect);
    assert_eq!(multi_polygon.as_geom_point(), None);
    assert_eq!(multi_polygon.as_geom_line(), None);
    assert_eq!(multi_polygon.as_geom_polygon(), None);
  }
}