use serde::Deserialize;
use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;
use std::{collections, env};
#[derive(Debug, Deserialize, Clone)]
struct TempRecord {
#[serde(rename = "Zipcode")]
zip_code: String,
#[serde(rename = "ZipCodeType")]
zip_code_type: String,
#[serde(rename = "City")]
city: String,
#[serde(rename = "State")]
state: String,
#[serde(skip)]
location_type: String,
#[serde(rename = "Lat")]
latitude: Option<f64>,
#[serde(rename = "Long")]
longitude: Option<f64>,
#[serde(rename = "Location")]
location: String,
#[serde(rename = "Decommisioned")]
is_decommissioned: String,
#[serde(rename = "TaxReturnsFiled")]
tax_returns_filed: Option<u64>,
#[serde(rename = "EstimatedPopulation")]
estimated_population: Option<u64>,
#[serde(rename = "TotalWages")]
total_wages: Option<u64>,
}
impl TempRecord {
fn to_syntax(&self) -> String {
let mut output = String::from("Record {");
output.push_str(&format!("zip_code: \"{}\",", self.zip_code));
output.push_str(&format!("zip_code_type: {},", self.encoded_type()));
output.push_str(&format!("city: \"{}\",", self.city));
output.push_str(&format!("state: \"{}\",", self.state));
output.push_str(&format!("coordinates: {},", self.coordinates()));
output.push_str(&format!("is_decommissioned: {},", self.is_decommissioned));
output.push_str(&format!("tax_returns_filed: {:?},", self.tax_returns_filed));
output.push_str(&format!(
"estimated_population: {:?},",
self.estimated_population
));
output.push_str(&format!("total_wages: {:?},", self.total_wages));
output.push('}');
output
}
fn encoded_type(&self) -> &'static str {
match &self.zip_code_type[..] {
"STANDARD" => "Type::Standard",
"PO BOX" => "Type::PoBox",
"UNIQUE" => "Type::Unique",
"MILITARY" => "Type::Military",
t => panic!("invalid ZIP code type \"{}\"", t),
}
}
fn coordinates(&self) -> String {
if let Some(ref latitude) = self.latitude {
if let Some(ref longitude) = self.longitude {
return format!("Some(({}_f64, {}_f64))", latitude, longitude);
}
}
"None".into()
}
}
fn main() {
let path = Path::new(&env::var_os("OUT_DIR").unwrap()).join("codegen.rs");
let mut file = BufWriter::new(File::create(&path).unwrap());
let mut zip_codes_phf = phf_codegen::Map::new();
let mut city_to_zip_codes = phf_codegen::Map::new();
let mut inserted_zip_codes = std::collections::HashSet::<String>::new();
let mut reader = csv::ReaderBuilder::new()
.has_headers(true)
.from_path("./data/zip_codes.csv")
.unwrap();
let mut city_hash_map = collections::HashMap::new();
for result in reader.deserialize() {
let record: TempRecord = result.unwrap();
let syntax = record.to_syntax();
let zip_code = record.clone().zip_code;
if !inserted_zip_codes.contains(&zip_code) {
zip_codes_phf.entry(zip_code.clone(), &syntax.to_owned());
inserted_zip_codes.insert(zip_code.clone());
let city = titlecase::titlecase(&record.clone().city);
let state = record.clone().state;
let zip_codes_set = city_hash_map
.entry(format!("{}, {}", city, state))
.or_insert_with(phf_codegen::Set::new);
zip_codes_set.entry(zip_code);
}
}
for (city, records) in city_hash_map {
city_to_zip_codes.entry(city, format!("{}", records.build()).as_str());
}
write!(
&mut file,
"static ZIP_CODES: phf::Map<&'static str, Record> = \n{};\n",
zip_codes_phf.build()
)
.unwrap();
write!(
&mut file,
"static CITY_MAP: phf::Map<&'static str, phf::Set<&str>> = \n{};\n",
city_to_zip_codes.build()
)
.unwrap();
}