use place_matcher::{
Address as MAddress, Place as MPlace, PlaceCategory as MCategory, PlaceId as MPlaceId,
PlaceIdScheme as MScheme,
};
use crate::models::{
address::PostalAddress, identifier::IdentifierType, place::Place, place_type::PlaceType,
};
pub fn to_matcher_place(p: &Place) -> MPlace {
let mut b = MPlace::builder().name(p.name.trim());
if let Some(alt) = p.alternate_name.as_deref().map(str::trim).filter(|s| !s.is_empty()) {
b = b.add_alternate_name(alt);
}
if let Some(t) = &p.place_type {
b = b.category(map_place_type(t));
}
if let Some(addr) = p.address.as_ref().and_then(map_address) {
b = b.address(addr);
}
if let Some(country) = p
.address
.as_ref()
.and_then(|a| a.address_country.as_deref())
.map(str::trim)
.filter(|c| c.len() == 2)
{
b = b.country_code_as_iso_3166_1_alpha_2(country.to_ascii_uppercase());
}
if let Some(geo) = &p.geo {
b = b.latitude(geo.latitude).longitude(geo.longitude);
if let Some(e) = geo.elevation {
b = b.elevation_as_metre(e);
}
}
if let Some(t) = p.telephone.as_deref().map(str::trim).filter(|s| !s.is_empty()) {
b = b.phone(t);
}
if let Some(gln) = p
.global_location_number
.as_deref()
.map(str::trim)
.filter(|s| !s.is_empty())
{
if let Some(pid) = MPlaceId::new(MScheme::Other("GLN".into()), gln) {
b = b.add_place_id(pid);
}
}
if let Some(bc) = p
.branch_code
.as_deref()
.map(str::trim)
.filter(|s| !s.is_empty())
{
if let Some(pid) = MPlaceId::new(MScheme::Other("BranchCode".into()), bc) {
b = b.add_place_id(pid);
}
}
for id in &p.identifiers {
if let Some(pid) = MPlaceId::new(map_identifier_scheme(&id.identifier_type), id.value.trim()) {
b = b.add_place_id(pid);
}
}
if let Some(cap) = p.maximum_attendee_capacity {
b = b.maximum_capacity_count(cap);
}
b.build()
}
pub fn map_place_type(t: &PlaceType) -> MCategory {
match t {
PlaceType::LocalBusiness => MCategory::Other("LocalBusiness".into()),
PlaceType::CivicStructure => MCategory::Other("CivicStructure".into()),
PlaceType::AdministrativeArea => MCategory::Other("AdministrativeArea".into()),
PlaceType::Landform => MCategory::Mountain,
PlaceType::Park => MCategory::Park,
PlaceType::Airport => MCategory::Airport,
PlaceType::Hospital => MCategory::Hospital,
PlaceType::School => MCategory::School,
PlaceType::Library => MCategory::Library,
PlaceType::Museum => MCategory::Museum,
PlaceType::Restaurant => MCategory::Restaurant,
PlaceType::Hotel => MCategory::Hotel,
PlaceType::Other(s) => MCategory::Other(s.clone()),
}
}
pub fn map_identifier_scheme(t: &IdentifierType) -> MScheme {
match t {
IdentifierType::GlobalLocationNumber => MScheme::Other("GLN".into()),
IdentifierType::BranchCode => MScheme::Other("BranchCode".into()),
IdentifierType::Fips => MScheme::Other("FIPS".into()),
IdentifierType::Gnis => MScheme::Other("GNIS".into()),
IdentifierType::OpenStreetMap => MScheme::OsmNode,
IdentifierType::Custom(s) => MScheme::Other(s.clone()),
}
}
fn map_address(a: &PostalAddress) -> Option<MAddress> {
let any = a.street_address.is_some()
|| a.address_locality.is_some()
|| a.address_region.is_some()
|| a.address_country.is_some()
|| a.postal_code.is_some();
if !any {
return None;
}
let mut m = MAddress::new();
if let Some(v) = a.street_address.as_deref() {
m = m.with_line1(v);
}
if let Some(v) = a.address_locality.as_deref() {
m = m.with_city(v);
}
if let Some(v) = a.address_region.as_deref() {
m = m.with_county(v); }
if let Some(v) = a.postal_code.as_deref() {
m = m.with_postcode(v);
}
if let Some(v) = a.address_country.as_deref() {
m = m.with_country(v);
}
Some(m)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::identifier::PlaceIdentifier;
#[test]
fn round_trip_basic_name_and_geo() {
use crate::models::geo::GeoCoordinates;
let mut svc = Place::new("Central Park");
svc.geo = Some(GeoCoordinates {
latitude: 40.7829,
longitude: -73.9654,
elevation: None,
});
let m = to_matcher_place(&svc);
assert_eq!(m.name.as_deref(), Some("Central Park"));
assert_eq!(m.latitude, Some(40.7829));
assert_eq!(m.longitude, Some(-73.9654));
}
#[test]
fn address_renames_locality_region_to_city_county() {
let mut svc = Place::new("Test");
svc.address = Some(PostalAddress {
street_address: Some("1 Broadway".into()),
address_locality: Some("New York".into()),
address_region: Some("NY".into()),
address_country: Some("US".into()),
postal_code: Some("10004".into()),
});
let m = to_matcher_place(&svc);
let a = m.address.as_ref().unwrap();
assert_eq!(a.line1.as_deref(), Some("1 Broadway"));
assert_eq!(a.city.as_deref(), Some("New York"));
assert_eq!(a.county.as_deref(), Some("NY"));
assert_eq!(a.postcode.as_deref(), Some("10004"));
assert_eq!(m.country_code_as_iso_3166_1_alpha_2.as_deref(), Some("US"));
}
#[test]
fn gln_routed_to_place_id_with_gln_scheme() {
let mut svc = Place::new("Test");
svc.global_location_number = Some("0614141999996".into());
let m = to_matcher_place(&svc);
assert_eq!(m.place_ids.len(), 1);
assert_eq!(m.place_ids[0].scheme, MScheme::Other("GLN".into()));
assert_eq!(m.place_ids[0].value, "0614141999996");
}
#[test]
fn place_identifier_osm_to_osm_node() {
let mut svc = Place::new("Test");
svc.identifiers.push(PlaceIdentifier::new(
IdentifierType::OpenStreetMap,
"node:42",
));
let m = to_matcher_place(&svc);
assert_eq!(m.place_ids[0].scheme, MScheme::OsmNode);
}
#[test]
fn place_type_maps_to_category() {
let mut svc = Place::new("Test");
svc.place_type = Some(PlaceType::Museum);
let m = to_matcher_place(&svc);
assert_eq!(m.category, Some(MCategory::Museum));
}
}