#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Geo {
pub latitude: f64,
pub longitude: f64,
pub max_latitude: f64,
pub max_longitude: f64,
pub min_latitude: f64,
pub min_longitude: f64,
pub bounds: Bounds,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Bounds {
pub northeast: LatLng,
pub southwest: LatLng,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct LatLng {
pub lat: f64,
pub lng: f64,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Country {
#[cfg(feature = "iso_code")]
pub iso_code: &'static str,
#[cfg(feature = "alpha3")]
pub alpha3: &'static str,
#[cfg(feature = "continent")]
pub continent: &'static str,
#[cfg(feature = "continent")]
pub continent_code: &'static str,
#[cfg(feature = "country_code")]
pub country_code: &'static str,
#[cfg(feature = "currency_code")]
pub currency_code: &'static str,
#[cfg(feature = "distance_unit")]
pub distance_unit: &'static str,
#[cfg(feature = "economic_unions")]
pub economic_unions: &'static [&'static str],
#[cfg(feature = "gec")]
pub gec: &'static str,
#[cfg(feature = "geo")]
pub geo: Geo,
#[cfg(feature = "international_prefix")]
pub international_prefix: &'static str,
#[cfg(feature = "ioc")]
pub ioc: &'static str,
#[cfg(feature = "iso_long_name")]
pub iso_long_name: &'static str,
#[cfg(feature = "iso_short_name")]
pub iso_short_name: &'static str,
#[cfg(feature = "languages_official")]
pub languages_official: &'static [&'static str],
#[cfg(feature = "languages_spoken")]
pub languages_spoken: &'static [&'static str],
#[cfg(feature = "national_destination_code_lengths")]
pub national_destination_code_lengths: &'static [u8],
#[cfg(feature = "national_number_lengths")]
pub national_number_lengths: &'static [u8],
#[cfg(feature = "national_prefix")]
pub national_prefix: Option<&'static str>,
#[cfg(feature = "nationality")]
pub nationality: &'static str,
#[cfg(feature = "number")]
pub number: &'static str,
#[cfg(feature = "postal_code")]
pub postal_code: bool,
#[cfg(feature = "postal_code_format")]
pub postal_code_format: &'static str,
#[cfg(feature = "region")]
pub region: &'static str,
#[cfg(feature = "start_of_week")]
pub start_of_week: &'static str,
#[cfg(feature = "subregion")]
pub subregion: &'static str,
#[cfg(feature = "un_locode")]
pub un_locode: &'static str,
#[cfg(feature = "unofficial_names")]
pub unofficial_names: &'static [&'static str],
#[cfg(feature = "world_region")]
pub world_region: &'static str,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Port {
pub name: &'static str,
pub city: &'static str,
pub state: &'static str,
pub country: &'static str,
pub latitude: f64,
pub longitude: f64,
pub timezone: &'static str,
pub unlocs: &'static [&'static str],
pub code: &'static str,
pub port_code: &'static str,
pub alias: &'static [&'static str],
pub regions: &'static [&'static str],
}
impl Port {
pub fn distance_to(&self, latitude: f64, longitude: f64) -> f64 {
haversine_distance(self.latitude, self.longitude, latitude, longitude)
}
pub fn distance_to_nautical_miles(&self, latitude: f64, longitude: f64) -> f64 {
self.distance_to(latitude, longitude) * 0.539957
}
pub fn distance_to_miles(&self, latitude: f64, longitude: f64) -> f64 {
self.distance_to(latitude, longitude) * 0.621371
}
pub fn has_unloc(&self, unloc: &str) -> bool {
self.unlocs.iter().any(|&u| u.eq_ignore_ascii_case(unloc))
}
pub fn has_alias(&self, alias: &str) -> bool {
self.alias.iter().any(|&a| a.eq_ignore_ascii_case(alias))
}
pub fn serves_region(&self, region: &str) -> bool {
self.regions.iter().any(|&r| r.eq_ignore_ascii_case(region))
}
pub fn display_name(&self) -> String {
format!("{}, {}, {}", self.name, self.city, self.country)
}
pub fn matches_search(&self, query: &str) -> bool {
let query_lower = query.to_lowercase();
self.name.to_lowercase().contains(&query_lower)
|| self.city.to_lowercase().contains(&query_lower)
|| self.code.to_lowercase().contains(&query_lower)
|| self.port_code.to_lowercase().contains(&query_lower)
|| self.unlocs.iter().any(|&u| u.to_lowercase().contains(&query_lower))
}
pub fn matches_code(&self, code: &str) -> bool {
self.code.eq_ignore_ascii_case(code) || self.port_code.eq_ignore_ascii_case(code)
}
pub fn matches_city(&self, city: &str) -> bool {
self.city.eq_ignore_ascii_case(city)
}
pub fn matches_country(&self, country: &str) -> bool {
self.country.eq_ignore_ascii_case(country)
}
pub fn matches_state(&self, state: &str) -> bool {
self.state.eq_ignore_ascii_case(state)
}
pub fn matches_name(&self, name: &str) -> bool {
self.name.eq_ignore_ascii_case(name)
}
pub fn matches_timezone(&self, timezone: &str) -> bool {
self.timezone.eq_ignore_ascii_case(timezone)
}
}
pub fn haversine_distance(lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> f64 {
const EARTH_RADIUS_KM: f64 = 6371.0;
let lat1_rad = lat1.to_radians();
let lat2_rad = lat2.to_radians();
let delta_lat = (lat2 - lat1).to_radians();
let delta_lon = (lon2 - lon1).to_radians();
let a = (delta_lat / 2.0).sin().powi(2)
+ lat1_rad.cos() * lat2_rad.cos() * (delta_lon / 2.0).sin().powi(2);
let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
EARTH_RADIUS_KM * c
}
impl Country {
#[cfg(feature = "languages_official")]
pub fn is_language_official(&self, language: &str) -> bool {
let language_lower = language.to_lowercase();
self.languages_official
.iter()
.any(|&lang| lang.to_lowercase() == language_lower)
}
#[cfg(feature = "languages_spoken")]
pub fn is_language_spoken(&self, language: &str) -> bool {
let language_lower = language.to_lowercase();
self.languages_spoken
.iter()
.any(|&lang| lang.to_lowercase() == language_lower)
}
#[cfg(all(feature = "languages_official", feature = "languages_spoken"))]
pub fn has_language(&self, language: &str) -> bool {
self.is_language_official(language) || self.is_language_spoken(language)
}
#[cfg(feature = "unofficial_names")]
pub fn is_unofficial_name(&self, name: &str) -> bool {
let name_lower = name.to_lowercase();
self.unofficial_names
.iter()
.any(|&n| n.to_lowercase() == name_lower)
}
#[cfg(all(feature = "iso_short_name", feature = "unofficial_names"))]
pub fn has_unofficial_name(&self, name: &str) -> bool {
self.iso_short_name.to_lowercase() == name.to_lowercase() || self.is_unofficial_name(name)
}
#[cfg(feature = "region")]
pub fn is_region(&self, region: &str) -> bool {
self.region.to_lowercase() == region.to_lowercase()
}
#[cfg(feature = "subregion")]
pub fn is_subregion(&self, subregion: &str) -> bool {
self.subregion.to_lowercase() == subregion.to_lowercase()
}
#[cfg(all(feature = "region", feature = "subregion"))]
pub fn is_region_or_subregion(&self, region_or_subregion: &str) -> bool {
self.is_region(region_or_subregion) || self.is_subregion(region_or_subregion)
}
#[cfg(all(feature = "region", feature = "subregion"))]
pub fn is_region_or_subregion_case_insensitive(&self, region_or_subregion: &str) -> bool {
self.region.to_lowercase() == region_or_subregion.to_lowercase()
|| self.subregion.to_lowercase() == region_or_subregion.to_lowercase()
}
#[cfg(feature = "economic_unions")]
pub fn is_in_economic_union(&self, economic_union: &str) -> bool {
self.economic_unions.contains(&economic_union)
}
#[cfg(feature = "economic_unions")]
pub fn is_in_economic_union_case_insensitive(&self, economic_union: &str) -> bool {
let economic_union_lower = economic_union.to_lowercase();
self.economic_unions
.iter()
.any(|&u| u.to_lowercase() == economic_union_lower)
}
}