use csv::ReaderBuilder; use extindex::{Builder, Entry, SerdeWrapper, Reader as ExtReader}; use std::error::Error; use std::io; use std::path::Path; use std::process; use serde::{Deserialize, Serialize}; use serde; use serde_repr::*; #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "lowercase")] enum Status { Live, Terminated, } #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "lowercase")] enum UserType { Small, Large, } #[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug, Clone)] #[repr(u8)] enum PositionalQuality { MatchedAddressPostcodeMean = 1, LandlineMapsInspection = 2, ApproximateWithin50Meters = 3, PostcodeUnitMean = 4, ImputedByONS = 5, PostcodeSectorMean = 6, TerminatedPriorToGridlink = 8, NoGridReferenceAvailable = 9, // 1 Within the building of the matched address closest to the postcode mean. // 2 As for status value 1, except by visual inspection of Landline maps (Scotland only). // 3 Approximate to within 50 metres. // 4 Postcode unit mean (mean of matched addresses with the same postcode, but not snapped to a building). // 5 Imputed by ONS, by reference to surrounding postcode grid references. // 6 Postcode sector mean, (mainly PO Boxes). // 7 Not used. // 8 Postcode terminated prior to Gridlink® initiative, last known ONS postcode grid reference. // 9 No grid reference available. } #[derive(Debug, Deserialize, Serialize)] struct Postcode { pub postcode: String, status: Status, usertype: UserType, #[serde(deserialize_with = "csv::invalid_option")] easting: Option, #[serde(deserialize_with = "csv::invalid_option")] northing: Option, positional_quality_indicator: PositionalQuality, country: String, latitude: Option, longitude: Option, postcode_no_space: String, postcode_fixed_width_seven: String, postcode_fixed_width_eight: String, postcode_area: String, postcode_district: String, postcode_sector: String, outcode: String, incode: String, } #[derive(Serialize, Deserialize, Debug)] struct SmolPostcode { postcode: String, status: Status, usertype: UserType, positional_quality_indicator: PositionalQuality, country: String, latitude: Option, longitude: Option, } impl SmolPostcode { fn from_postcode(full: Postcode) -> Self { SmolPostcode { postcode: full.postcode.to_owned(), status: full.status, usertype: full.usertype, positional_quality_indicator: full.positional_quality_indicator.to_owned(), country: full.country.to_owned(), latitude: full.latitude.to_owned(), longitude: full.longitude.to_owned(), } } } fn read() -> Result<(), Box> { let mut rdr = ReaderBuilder::new() .has_headers(false) .from_reader(io::stdin()); let mut i: u32 = 0; for result in rdr.deserialize() { let record: Postcode = result?; // println!("{}:: {:?}", i, record); i += 1; } println!("Read {:?} postcodes", i); Ok(()) } /* * Thought this would be easy.. * let postcodes = csv_iter(io::stdin()); for p in postcodes.take(10) { println!("Postcode: {}", p); } fn csv_iter(file: impl io::Read + 'static) -> impl Iterator { let mut reader = ReaderBuilder::new() .has_headers(false) .from_reader(file); let iter = reader.deserialize(); iter .filter_map(|r: Result| r.ok()) .map(|postcode| postcode.postcode.to_owned()) .collect() } */ fn main() { build(); if let Err(err) = read() { println!("error running example: {}", err); process::exit(1); } } fn build() { let index_file_path = Path::new("./postcodes.db"); let builder: Builder> = Builder::new(index_file_path); let mut csv_reader = ReaderBuilder::new() .has_headers(false) .from_reader(io::stdin()); let entries = csv_reader.deserialize() .filter_map(|r: Result| r.ok()) .map(|full: Postcode| SmolPostcode::from_postcode(full)) .map(|smol| Entry::new(smol.postcode.to_owned(), SerdeWrapper(smol))); builder.build(entries.into_iter()).unwrap(); let reader = ExtReader::>::open(index_file_path).unwrap(); let here = reader.find(&"LS27 8BW".to_string()).unwrap().expect("Not found"); println!("Here: {:?}", here.value().0); }