use serde::{Deserialize, Serialize};
use crate::err::{DeserializeErr};
use std::str::FromStr;
use rust_decimal::Decimal;
use chrono::{DateTime, Utc, TimeZone};
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct GeoName {
pub geonameid: i32,
pub name: String,
pub asciiname: Option<String>,
pub alternatenames: Vec<String>,
pub latitude: Decimal,
pub longitude: Decimal,
pub feature_class: Option<String>,
pub feature_code: Option<String>,
pub country_code: Option<String>,
pub cc2: Option<String>,
pub admin1_code : Option<String>,
pub admin2_code : Option<String>,
pub admin3_code : Option<String>,
pub admin4_code : Option<String>,
pub population: Option<i64>,
pub elevation: Option<i32>,
pub dem: String,
pub timezone: Option<String>,
pub modification: DateTime<Utc>,
}
impl GeoName {
pub fn deserialize_from_string(str: &str) -> Result<Self, DeserializeErr> {
let vec = str.split(" ").collect::<Vec<&str>>();
let elevation = match vec[15].to_owned().is_empty() {
true => None,
false => Some(vec[15].parse::<i32>()?)
};
Ok(Self {
geonameid: vec[0].parse::<i32>()?,
name: vec[1].to_owned(),
asciiname: Some(vec[2].to_owned()).filter(|x| !x.is_empty()),
alternatenames: vec[3].split(",").collect::<Vec<&str>>().into_iter().map(|x| x.to_owned()).collect::<Vec<String>>(),
latitude: Decimal::from_str(&vec[4])?,
longitude: Decimal::from_str(&vec[5])?,
feature_class: Some(vec[6].to_owned()).filter(|x| !x.is_empty()),
feature_code: Some(vec[7].to_owned()).filter(|x| !x.is_empty()),
country_code: Some(vec[8].to_owned()).filter(|x| !x.is_empty()),
cc2: Some(vec[9].to_owned()).filter(|x| !x.is_empty()),
admin1_code: Some(vec[10].to_owned()).filter(|x| !x.is_empty()),
admin2_code: Some(vec[11].to_owned()).filter(|x| !x.is_empty()),
admin3_code: Some(vec[12].to_owned()).filter(|x| !x.is_empty()),
admin4_code: Some(vec[13].to_owned()).filter(|x| !x.is_empty()),
population: Some(vec[14].parse::<i64>()?).filter(|x| x != &0),
elevation,
dem: vec[16].to_owned(),
timezone: Some(vec[17].to_owned()).filter(|x| !x.is_empty()),
modification: Utc.datetime_from_str(&(vec[18].to_string() + " 00:00:00 +00:00"), "%Y-%m-%d %H:%M:%S %z")?
})
}
}
#[cfg(test)]
mod tests {
use crate::model::GeoName;
#[test]
fn deserialize_string_to_geo_name() {
use std::fs::File;
use std::io::{BufRead, BufReader};
for line in BufReader::new(File::open("./test-data/geonames_gb.txt").unwrap()).lines() {
let _geo_name = GeoName::deserialize_from_string(&line.unwrap()).unwrap();
}
}
}