2022-09-18 14:41:07 +00:00
|
|
|
use csv::ReaderBuilder;
|
2022-10-09 17:43:20 +00:00
|
|
|
use extindex::{Builder, Entry, SerdeWrapper, Reader as ExtReader};
|
2022-09-18 14:41:07 +00:00
|
|
|
use std::error::Error;
|
|
|
|
use std::io;
|
2022-10-09 17:43:20 +00:00
|
|
|
use std::path::Path;
|
2022-09-18 14:41:07 +00:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
2022-10-09 17:43:20 +00:00
|
|
|
#[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug, Clone)]
|
2022-09-18 14:41:07 +00:00
|
|
|
#[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.
|
|
|
|
}
|
|
|
|
|
2022-10-09 17:43:20 +00:00
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
2022-09-18 14:41:07 +00:00
|
|
|
struct Postcode {
|
2022-10-09 17:43:20 +00:00
|
|
|
pub postcode: String,
|
2022-09-18 14:41:07 +00:00
|
|
|
status: Status,
|
|
|
|
usertype: UserType,
|
|
|
|
#[serde(deserialize_with = "csv::invalid_option")]
|
|
|
|
easting: Option<u32>,
|
|
|
|
#[serde(deserialize_with = "csv::invalid_option")]
|
|
|
|
northing: Option<u32>,
|
|
|
|
positional_quality_indicator: PositionalQuality,
|
|
|
|
country: String,
|
|
|
|
latitude: Option<String>,
|
|
|
|
longitude: Option<String>,
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
2022-10-09 17:43:20 +00:00
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
struct SmolPostcode {
|
|
|
|
postcode: String,
|
|
|
|
status: Status,
|
|
|
|
usertype: UserType,
|
|
|
|
positional_quality_indicator: PositionalQuality,
|
|
|
|
country: String,
|
|
|
|
latitude: Option<String>,
|
|
|
|
longitude: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
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(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-18 14:41:07 +00:00
|
|
|
fn read() -> Result<(), Box<dyn Error>> {
|
|
|
|
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(())
|
|
|
|
}
|
|
|
|
|
2022-10-09 17:43:20 +00:00
|
|
|
/*
|
|
|
|
* 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<Item=String> {
|
|
|
|
let mut reader = ReaderBuilder::new()
|
|
|
|
.has_headers(false)
|
|
|
|
.from_reader(file);
|
|
|
|
let iter = reader.deserialize();
|
|
|
|
iter
|
|
|
|
.filter_map(|r: Result<Postcode, csv::Error>| r.ok())
|
|
|
|
.map(|postcode| postcode.postcode.to_owned())
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2022-09-18 14:41:07 +00:00
|
|
|
fn main() {
|
2022-10-09 17:43:20 +00:00
|
|
|
build();
|
2022-09-18 14:41:07 +00:00
|
|
|
if let Err(err) = read() {
|
|
|
|
println!("error running example: {}", err);
|
|
|
|
process::exit(1);
|
|
|
|
}
|
|
|
|
}
|
2022-10-09 17:43:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
fn build() {
|
|
|
|
let index_file_path = Path::new("./postcodes.db");
|
|
|
|
|
|
|
|
let builder: Builder<String, SerdeWrapper<SmolPostcode>> = 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<Postcode, csv::Error>| r.ok())
|
2022-10-09 18:24:05 +00:00
|
|
|
.filter(|full| match full.status { Status::Live => true, _ => false })
|
2022-10-09 17:43:20 +00:00
|
|
|
.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::<String, SerdeWrapper<SmolPostcode>>::open(index_file_path).unwrap();
|
|
|
|
let here = reader.find(&"LS27 8BW".to_string()).unwrap().expect("Not found");
|
|
|
|
println!("Here: {:?}", here.value().0);
|
|
|
|
}
|