Skip to content

Commit

Permalink
Merge pull request #7 from rjcortese/issue_#4
Browse files Browse the repository at this point in the history
Fix #4 -> implement ParsedAddress struct
  • Loading branch information
kodemartin authored Feb 16, 2022
2 parents 3770cee + c3bc5d8 commit 774a5a2
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 0 deletions.
142 changes: 142 additions & 0 deletions src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
//! Ok(())
//! }
//! ```
use std::collections::HashMap;
use std::ffi::{CStr, CString, NulError};
use std::slice::Iter;

Expand Down Expand Up @@ -184,6 +185,120 @@ pub fn parse_address(
options.parse(address)
}

/// A parsed address backed by a `HashMap`.
/// The only way to make one is from an `AddressParserResponse`.
/// It implements a getter method for each label that might
/// be included in the `AddressParserResponse`.
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct ParsedAddress {
label_to_token: HashMap<String, String>,
}

impl ParsedAddress {
pub fn house(&self) -> Option<String> {
self.label_to_token.get("house").cloned()
}

pub fn house_number(&self) -> Option<String> {
self.label_to_token.get("house_number").cloned()
}

pub fn po_box(&self) -> Option<String> {
self.label_to_token.get("po_box").cloned()
}

pub fn building(&self) -> Option<String> {
self.label_to_token.get("building").cloned()
}

pub fn entrance(&self) -> Option<String> {
self.label_to_token.get("entrance").cloned()
}

pub fn staircase(&self) -> Option<String> {
self.label_to_token.get("staircase").cloned()
}

pub fn level(&self) -> Option<String> {
self.label_to_token.get("level").cloned()
}

pub fn unit(&self) -> Option<String> {
self.label_to_token.get("unit").cloned()
}

pub fn road(&self) -> Option<String> {
self.label_to_token.get("road").cloned()
}

pub fn metro_station(&self) -> Option<String> {
self.label_to_token.get("metro_station").cloned()
}

pub fn suburb(&self) -> Option<String> {
self.label_to_token.get("suburb").cloned()
}

pub fn city_district(&self) -> Option<String> {
self.label_to_token.get("city_district").cloned()
}

pub fn city(&self) -> Option<String> {
self.label_to_token.get("city").cloned()
}

pub fn state_district(&self) -> Option<String> {
self.label_to_token.get("state_district").cloned()
}

pub fn island(&self) -> Option<String> {
self.label_to_token.get("island").cloned()
}

pub fn state(&self) -> Option<String> {
self.label_to_token.get("state").cloned()
}

// postcode may be referred to as postal_code somewheres
// https://github.com/openvenues/libpostal/blob/9c975972985b54491e756efd70e416f18ff97958/src/address_parser.h#L122
pub fn postcode(&self) -> Option<String> {
self.label_to_token.get("postcode").cloned()
}

pub fn country_region(&self) -> Option<String> {
self.label_to_token.get("country_region").cloned()
}

pub fn country(&self) -> Option<String> {
self.label_to_token.get("country").cloned()
}

pub fn world_region(&self) -> Option<String> {
self.label_to_token.get("world_region").cloned()
}

pub fn website(&self) -> Option<String> {
self.label_to_token.get("website").cloned()
}

pub fn telephone(&self) -> Option<String> {
self.label_to_token.get("telephone").cloned()
}
}

impl From<AddressParserResponse> for ParsedAddress {
/// Create a new `ParsedAddress` from an `AddressParserResponse`.
fn from(response: AddressParserResponse) -> Self {
let mut parsed_address = ParsedAddress::default();
for (label, token) in &response {
parsed_address
.label_to_token
.insert(label.clone(), token.clone());
}
parsed_address
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -216,4 +331,31 @@ mod tests {
}
Ok(())
}

#[test]
fn test_parsed_address_default() {
let parsed_address = ParsedAddress::default();
assert_eq!(parsed_address.house(), None);
assert_eq!(parsed_address.house_number(), None);
assert_eq!(parsed_address.po_box(), None);
assert_eq!(parsed_address.building(), None);
assert_eq!(parsed_address.entrance(), None);
assert_eq!(parsed_address.staircase(), None);
assert_eq!(parsed_address.level(), None);
assert_eq!(parsed_address.unit(), None);
assert_eq!(parsed_address.road(), None);
assert_eq!(parsed_address.metro_station(), None);
assert_eq!(parsed_address.suburb(), None);
assert_eq!(parsed_address.city_district(), None);
assert_eq!(parsed_address.city(), None);
assert_eq!(parsed_address.state_district(), None);
assert_eq!(parsed_address.island(), None);
assert_eq!(parsed_address.state(), None);
assert_eq!(parsed_address.postcode(), None);
assert_eq!(parsed_address.country_region(), None);
assert_eq!(parsed_address.country(), None);
assert_eq!(parsed_address.world_region(), None);
assert_eq!(parsed_address.website(), None);
assert_eq!(parsed_address.telephone(), None);
}
}
51 changes: 51 additions & 0 deletions tests/address.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
extern crate rustpostal;
use rustpostal::address::ParsedAddress;
use rustpostal::error::RuntimeError;
use rustpostal::LibModules;

Expand All @@ -24,6 +25,21 @@ fn us_parse() {
assert_actual_eq_expected(address, expected);
}

fn us_parse_to_struct() {
let address = "Black Alliance for Just Immigration 660 Nostrand Ave, Brooklyn, N.Y., 11216";
let response = rustpostal::address::parse_address(address, None, None).unwrap();
let actual = ParsedAddress::from(response);
assert_eq!(
actual.house(),
Some("black alliance for just immigration".to_string())
);
assert_eq!(actual.house_number(), Some("660".to_string()));
assert_eq!(actual.road(), Some("nostrand ave".to_string()));
assert_eq!(actual.city_district(), Some("brooklyn".to_string()));
assert_eq!(actual.state(), Some("n.y.".to_string()));
assert_eq!(actual.postcode(), Some("11216".to_string()));
}

fn gb_parse() {
let address = "St Johns Centre, Rope Walk, Bedford, Bedfordshire, MK42 0XE, United Kingdom";
let expected = vec![
Expand All @@ -37,6 +53,18 @@ fn gb_parse() {
assert_actual_eq_expected(address, expected);
}

fn gb_parse_to_struct() {
let address = "St Johns Centre, Rope Walk, Bedford, Bedfordshire, MK42 0XE, United Kingdom";
let response = rustpostal::address::parse_address(address, None, None).unwrap();
let actual = ParsedAddress::from(response);
assert_eq!(actual.house(), Some("st johns centre".to_string()));
assert_eq!(actual.road(), Some("rope walk".to_string()));
assert_eq!(actual.city(), Some("bedford".to_string()));
assert_eq!(actual.state_district(), Some("bedfordshire".to_string()));
assert_eq!(actual.postcode(), Some("mk42 0xe".to_string()));
assert_eq!(actual.country(), Some("united kingdom".to_string()));
}

fn es_parse() {
let address = "Museo del Prado C. de Ruiz de Alarcón,
23 28014 Madrid, España";
Expand All @@ -51,6 +79,19 @@ fn es_parse() {
assert_actual_eq_expected(address, expected);
}

fn es_parse_to_struct() {
let address = "Museo del Prado C. de Ruiz de Alarcón,
23 28014 Madrid, España";
let response = rustpostal::address::parse_address(address, None, None).unwrap();
let actual = ParsedAddress::from(response);
assert_eq!(actual.house(), Some("museo del prado".to_string()));
assert_eq!(actual.road(), Some("c. de ruiz de alarcón".to_string()));
assert_eq!(actual.house_number(), Some("23".to_string()));
assert_eq!(actual.postcode(), Some("28014".to_string()));
assert_eq!(actual.city(), Some("madrid".to_string()));
assert_eq!(actual.country(), Some("españa".to_string()));
}

#[test]
fn parse() -> Result<(), RuntimeError> {
let postal_module = LibModules::Address;
Expand All @@ -60,3 +101,13 @@ fn parse() -> Result<(), RuntimeError> {
es_parse();
Ok(())
}

#[test]
fn parse_address_to_parsed_address_struct() -> Result<(), RuntimeError> {
let postal_module = LibModules::Address;
postal_module.setup()?;
us_parse_to_struct();
gb_parse_to_struct();
es_parse_to_struct();
Ok(())
}

0 comments on commit 774a5a2

Please sign in to comment.