Skip to content

Instantly share code, notes, and snippets.

@willir
Last active April 21, 2016 05:53
Show Gist options
  • Save willir/b3a36c35d2193f4f92b57fadaccd2c4c to your computer and use it in GitHub Desktop.
Save willir/b3a36c35d2193f4f92b57fadaccd2c4c to your computer and use it in GitHub Desktop.
Rust: Towns File Parser
[package]
name = "town_parser"
version = "0.1.0"
authors = ["Anton Rapetov <[email protected]>"]
[dependencies]
memmap = ">= 0.3.0"
extern crate memmap;
use std::collections::{HashSet, HashMap};
use std::cmp::{Eq, PartialEq};
use std::{env, str, num, error, fmt};
use std::hash::{Hash, Hasher};
use std::str::FromStr;
use memmap::{Mmap, Protection};
// -----------------------------------------------------------------------
#[derive(Debug)]
enum BaseCityErrorCause {
IntParse(num::ParseIntError),
FloatParse(num::ParseFloatError),
}
#[derive(Debug)]
struct BaseCityError {
descr: Option<String>,
cause: Option<BaseCityErrorCause>
}
impl BaseCityError {
fn new(s: String) -> BaseCityError {
BaseCityError{ descr: Some(s), cause: None }
}
}
impl fmt::Display for BaseCityError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.cause {
Some(ref cause) => match *cause {
BaseCityErrorCause::IntParse(ref err) => write!(f, "Error parsing integer {}", err),
BaseCityErrorCause::FloatParse(ref err) => write!(f, "Error parsing integer {}", err),
},
None => write!(f, "{}", self.descr.as_ref().unwrap()),
}
}
}
impl error::Error for BaseCityError {
fn description(&self) -> &str {
match self.cause {
Some(ref cause) => match *cause {
BaseCityErrorCause::IntParse(ref err) => err.description(),
BaseCityErrorCause::FloatParse(ref err) => err.description(),
},
None => &self.descr.as_ref().unwrap(),
}
}
fn cause(&self) -> Option<&error::Error> {
match self.cause {
Some(ref cause) => match *cause {
BaseCityErrorCause::IntParse(ref err) => Some(err),
BaseCityErrorCause::FloatParse(ref err) => Some(err),
},
None => None,
}
}
}
impl From<num::ParseIntError> for BaseCityError {
fn from(err: num::ParseIntError) -> BaseCityError {
BaseCityError { descr: None, cause: Some(BaseCityErrorCause::IntParse(err)) }
}
}
impl From<num::ParseFloatError> for BaseCityError {
fn from(err: num::ParseFloatError) -> BaseCityError {
BaseCityError { descr: None, cause: Some(BaseCityErrorCause::FloatParse(err)) }
}
}
// -----------------------------------------------------------------------
#[derive(Debug, Clone)]
struct BaseCity {
id: i32,
name: String,
lat: f64,
lon: f64,
country_code: String,
}
impl FromStr for BaseCity {
type Err = BaseCityError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let splitted: Vec<&str> = s.split('\t').collect();
if splitted.len() != 5 {
return Err(BaseCityError::new(format!("Wrong number of columns expect: {}, got: {}", 5, splitted.len())));
}
let id = try!(splitted[0].parse::<i32>());
let name = splitted[1].trim().to_string();
let lat = try!(splitted[2].parse::<f64>());
let lon = try!(splitted[3].parse::<f64>());
let country_code = splitted[4].trim().to_string();
if name.is_empty() {
Err(BaseCityError::new("Name is empty:".to_string()))
} else if country_code.is_empty() {
Err(BaseCityError::new("Country Code is empty:".to_string()))
} else {
Ok(BaseCity { id: id, name: name, lat: lat, lon: lon, country_code: country_code })
}
}
}
impl Eq for BaseCity {}
impl PartialEq for BaseCity {
fn eq(&self, other: &BaseCity) -> bool {
self.id == other.id
}
}
impl Hash for BaseCity {
fn hash<H>(&self, state: &mut H) where H: Hasher {
self.id.hash(state);
}
}
fn main() {
let path = env::args().nth(1).expect("supply a single path as the program argument");
let mmap = Mmap::open_path(path, Protection::Read).unwrap();
let file_raw_data = unsafe { mmap.as_slice() };
let file_str = str::from_utf8(file_raw_data).expect("File contains invalid UTF8");
let mut res: HashMap<String, HashSet<BaseCity>> = HashMap::new();
for (i, line) in file_str.lines().enumerate() {
if i == 0 { continue; } // First Line is header
let city = match line.parse::<BaseCity>() {
Ok(expr) => expr,
Err(err) => {
println!("WARNING: incorrect line: {}, reason: {}", line, err);
continue;
},
};
res.entry(city.country_code.clone()).or_insert_with(|| HashSet::new()).insert(city);
}
let ru_cities = &res["RU"];
println!("Hello, world! {}", res.len());
println!("Number of city in Russia: {}", ru_cities.len());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment