#![allow(clippy::field_reassign_with_default)]
use std::env;
use std::error;
use std::fmt::{self, Debug, Display, Formatter};
use std::hash::Hash;
use std::sync::Arc;
use reqwest::{header, Client, Method, Request, StatusCode, Url};
use serde::{Deserialize, Serialize};
const ENDPOINT: &str = "https://maps.google.com/maps/api/geocode/json";
pub struct Geocode {
key: String,
client: Arc<Client>,
}
impl Geocode {
pub fn new<K>(key: K) -> Self
where
K: ToString,
{
let client = Client::builder().build();
match client {
Ok(c) => Self {
key: key.to_string(),
client: Arc::new(c),
},
Err(e) => panic!("creating client failed: {:?}", e),
}
}
pub fn new_from_env() -> Self {
let key = env::var("GOOGLE_GEOCODE_API_KEY").unwrap();
Geocode::new(key)
}
fn request<B>(&self, method: Method, path: &str, body: B, query: Option<Vec<(&str, String)>>) -> Request
where
B: Serialize,
{
let base = Url::parse(ENDPOINT).unwrap();
let url = base.join(path).unwrap();
let mut headers = header::HeaderMap::new();
headers.append(header::CONTENT_TYPE, header::HeaderValue::from_static("application/json"));
let mut rb = self.client.request(method.clone(), url).headers(headers).basic_auth(&self.key, Some(""));
match query {
None => (),
Some(val) => {
rb = rb.query(&val);
}
}
if method != Method::GET && method != Method::DELETE {
rb = rb.json(&body);
}
rb.build().unwrap()
}
pub async fn get(&self, address: &str) -> Result<Reply, APIError> {
let request = self.request(Method::GET, "", (), Some(vec![("address", address.to_string()), ("key", self.key.to_string())]));
let resp = self.client.execute(request).await.unwrap();
match resp.status() {
StatusCode::OK => (),
s => {
return Err(APIError {
status_code: s,
body: resp.text().await.unwrap(),
})
}
};
let r: ReplyResult = resp.json().await.unwrap();
if r.results.is_empty() {
return Err(APIError {
status_code: StatusCode::NOT_FOUND,
body: "".to_string(),
});
}
Ok(r.results.get(0).unwrap().clone())
}
}
pub struct APIError {
pub status_code: StatusCode,
pub body: String,
}
impl fmt::Display for APIError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "APIError: status code -> {}, body -> {}", self.status_code.to_string(), self.body)
}
}
impl fmt::Debug for APIError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "APIError: status code -> {}, body -> {}", self.status_code.to_string(), self.body)
}
}
impl error::Error for APIError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AddressComponent {
#[serde(default)]
long_name: String,
#[serde(default)]
short_name: String,
#[serde(default)]
types: Vec<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Geometry {
pub location: Coordinates,
pub location_type: LocationType,
pub viewport: Viewport,
pub bounds: Option<Viewport>,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum LocationType {
Rooftop,
RangeInterpolated,
GeometricCenter,
Approximate,
}
#[derive(Debug, Clone, Deserialize)]
pub struct FormattedAddress(String);
impl Display for FormattedAddress {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
#[derive(Debug, Default, Deserialize)]
struct ReplyResult {
#[serde(default)]
error_message: String,
#[serde(default)]
results: Vec<Reply>,
#[serde(default)]
status: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Reply {
#[serde(default)]
pub address_components: Vec<AddressComponent>,
pub formatted_address: FormattedAddress,
pub geometry: Geometry,
pub place_id: PlaceId,
#[serde(default)]
pub postcode_localities: Vec<String>,
#[serde(default)]
pub types: Vec<String>,
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct Viewport {
pub northeast: Coordinates,
pub southwest: Coordinates,
}
#[derive(Clone, Copy, Debug, Serialize)]
#[allow(dead_code)]
pub enum Language {
#[serde(rename = "ar")]
Arabic,
#[serde(rename = "bg")]
Bulgarian,
#[serde(rename = "bn")]
Bengali,
#[serde(rename = "ca")]
Catalan,
#[serde(rename = "cs")]
Czech,
#[serde(rename = "da")]
Danish,
#[serde(rename = "de")]
German,
#[serde(rename = "el")]
Greek,
#[serde(rename = "en")]
English,
#[serde(rename = "en-AU")]
EnglishAustralian,
#[serde(rename = "en-GB")]
EnglishGreatBritain,
#[serde(rename = "es")]
Spanish,
#[serde(rename = "eu")]
Basque,
#[serde(rename = "fa")]
Farsi,
#[serde(rename = "fi")]
Finnish,
#[serde(rename = "fil")]
Filipino,
#[serde(rename = "fr")]
French,
#[serde(rename = "gl")]
Galician,
#[serde(rename = "gu")]
Gujarati,
#[serde(rename = "hi")]
Hindi,
#[serde(rename = "hr")]
Croatian,
#[serde(rename = "hu")]
Hungarian,
#[serde(rename = "id")]
Indonesian,
#[serde(rename = "it")]
Italian,
#[serde(rename = "iw")]
Hebrew,
#[serde(rename = "ja")]
Japanese,
#[serde(rename = "kn")]
Kannada,
#[serde(rename = "ko")]
Korean,
#[serde(rename = "lt")]
Lithuanian,
#[serde(rename = "lv")]
Latvian,
#[serde(rename = "ml")]
Malayalam,
#[serde(rename = "mr")]
Marathi,
#[serde(rename = "nl")]
Dutch,
#[serde(rename = "no")]
Norwegian,
#[serde(rename = "pl")]
Polish,
#[serde(rename = "pt")]
Portuguese,
#[serde(rename = "pt-BR")]
PortugueseBrazil,
#[serde(rename = "pt-PT")]
PortuguesePortugal,
#[serde(rename = "ro")]
Romanian,
#[serde(rename = "ru")]
Russian,
#[serde(rename = "sk")]
Slovak,
#[serde(rename = "sl")]
Slovenian,
#[serde(rename = "sr")]
Serbian,
#[serde(rename = "sv")]
Swedish,
#[serde(rename = "ta")]
Tamil,
#[serde(rename = "te")]
Telugu,
#[serde(rename = "th")]
Thai,
#[serde(rename = "tl")]
Tagalog,
#[serde(rename = "tr")]
Turkish,
#[serde(rename = "uk")]
Ukrainian,
#[serde(rename = "vi")]
Vietnamese,
#[serde(rename = "zh-CN")]
ChineseSimplified,
#[serde(rename = "zh-TW")]
ChineseTraditional,
}
#[derive(Clone, Copy, Debug, Serialize)]
#[allow(dead_code)]
pub enum Region {
#[serde(rename = ".ac")]
AscensionIsland,
#[serde(rename = ".ad")]
Andorra,
#[serde(rename = ".ae")]
UnitedArabEmirates,
#[serde(rename = ".af")]
Afghanistan,
#[serde(rename = ".ag")]
AntiguaAndBarbuda,
#[serde(rename = ".ai")]
Anguilla,
#[serde(rename = ".al")]
Albania,
#[serde(rename = ".am")]
Armenia,
#[serde(rename = ".an")]
AntillesNetherlands,
#[serde(rename = ".ao")]
Angola,
#[serde(rename = ".aq")]
Antarctica,
#[serde(rename = ".ar")]
Argentina,
#[serde(rename = ".as")]
AmericanSamoa,
#[serde(rename = ".at")]
Austria,
#[serde(rename = ".au")]
Australia,
#[serde(rename = ".aw")]
Aruba,
#[serde(rename = ".ax")]
AlandIslands,
#[serde(rename = ".az")]
Azerbaijan,
#[serde(rename = ".ba")]
BosniaAndHerzegovina,
#[serde(rename = ".bb")]
Barbados,
#[serde(rename = ".bd")]
Bangladesh,
#[serde(rename = ".be")]
Belgium,
#[serde(rename = ".bf")]
BurkinaFaso,
#[serde(rename = ".bg")]
Bulgaria,
#[serde(rename = ".bh")]
Bahrain,
#[serde(rename = ".bi")]
Burundi,
#[serde(rename = ".bj")]
Benin,
#[serde(rename = ".bl")]
SaintBarthelemy,
#[serde(rename = ".bm")]
Bermuda,
#[serde(rename = ".bn")]
BruneiDarussalam,
#[serde(rename = ".bo")]
Bolivia,
#[serde(rename = ".bq")]
BonaireSintEustatiusAndSaba,
#[serde(rename = ".br")]
Brazil,
#[serde(rename = ".bs")]
Bahamas,
#[serde(rename = ".bt")]
Bhutan,
#[serde(rename = ".bv")]
BouvetIsland,
#[serde(rename = ".bw")]
Botswana,
#[serde(rename = ".by")]
Belarus,
#[serde(rename = ".bz")]
Belize,
#[serde(rename = ".ca")]
Canada,
#[serde(rename = ".cc")]
CocosIslands,
#[serde(rename = ".cd")]
DemocraticRepublicOfTheCongo,
#[serde(rename = ".cf")]
CentralAfricanRepublic,
#[serde(rename = ".cg")]
RepublicOfCongo,
#[serde(rename = ".ch")]
Switzerland,
#[serde(rename = ".ci")]
CoteDivoire,
#[serde(rename = ".ck")]
CookIslands,
#[serde(rename = ".cl")]
Chile,
#[serde(rename = ".cm")]
Cameroon,
#[serde(rename = ".cn")]
China,
#[serde(rename = ".co")]
Colombia,
#[serde(rename = ".cr")]
CostaRica,
#[serde(rename = ".cu")]
Cuba,
#[serde(rename = ".cv")]
CapeVerde,
#[serde(rename = ".cw")]
Curacao,
#[serde(rename = ".cx")]
ChristmasIsland,
#[serde(rename = ".cy")]
Cyprus,
#[serde(rename = ".cz")]
CzechRepublic,
#[serde(rename = ".de")]
Germany,
#[serde(rename = ".dj")]
Djibouti,
#[serde(rename = ".dk")]
Denmark,
#[serde(rename = ".dm")]
Dominica,
#[serde(rename = ".do")]
DominicanRepublic,
#[serde(rename = ".dz")]
Algeria,
#[serde(rename = ".ec")]
Ecuador,
#[serde(rename = ".ee")]
Estonia,
#[serde(rename = ".eg")]
Egypt,
#[serde(rename = ".eh")]
WesternSahara,
#[serde(rename = ".er")]
Eritrea,
#[serde(rename = ".es")]
Spain,
#[serde(rename = ".et")]
Ethiopia,
#[serde(rename = ".eu")]
EuropeanUnion,
#[serde(rename = ".fi")]
Finland,
#[serde(rename = ".fj")]
Fiji,
#[serde(rename = ".fk")]
FalklandIslands,
#[serde(rename = ".fm")]
FederatedStatesOfMicronesia,
#[serde(rename = ".fo")]
FaroeIslands,
#[serde(rename = ".fr")]
France,
#[serde(rename = ".ga")]
Gabon,
#[serde(rename = ".gd")]
Grenada,
#[serde(rename = ".ge")]
Georgia,
#[serde(rename = ".gf")]
FrenchGuiana,
#[serde(rename = ".gg")]
Guernsey,
#[serde(rename = ".gh")]
Ghana,
#[serde(rename = ".gi")]
Gibraltar,
#[serde(rename = ".gl")]
Greenland,
#[serde(rename = ".gm")]
Gambia,
#[serde(rename = ".gn")]
Guinea,
#[serde(rename = ".gp")]
Guadeloupe,
#[serde(rename = ".gq")]
EquatorialGuinea,
#[serde(rename = ".gr")]
Greece,
#[serde(rename = ".gs")]
SouthGeorgiaAndTheSouthSandwichIslands,
#[serde(rename = ".gt")]
Guatemala,
#[serde(rename = ".gu")]
Guam,
#[serde(rename = ".gw")]
GuineaBissau,
#[serde(rename = ".gy")]
Guyana,
#[serde(rename = ".hk")]
HongKong,
#[serde(rename = ".hm")]
HeardIslandAndMcDonaldIslands,
#[serde(rename = ".hn")]
Honduras,
#[serde(rename = ".hr")]
Croatia,
#[serde(rename = ".ht")]
Haiti,
#[serde(rename = ".hu")]
Hungary,
#[serde(rename = ".id")]
Indonesia,
#[serde(rename = ".ie")]
Ireland,
#[serde(rename = ".il")]
Israel,
#[serde(rename = ".im")]
IsleOfMan,
#[serde(rename = ".in")]
India,
#[serde(rename = ".io")]
BritishIndianOceanTerritory,
#[serde(rename = ".iq")]
Iraq,
#[serde(rename = ".ir")]
IslamicRepublicOfIran,
#[serde(rename = ".is")]
Iceland,
#[serde(rename = ".it")]
Italy,
#[serde(rename = ".je")]
Jersey,
#[serde(rename = ".jm")]
Jamaica,
#[serde(rename = ".jo")]
Jordan,
#[serde(rename = ".jp")]
Japan,
#[serde(rename = ".ke")]
Kenya,
#[serde(rename = ".kg")]
Kyrgyzstan,
#[serde(rename = ".kh")]
Cambodia,
#[serde(rename = ".ki")]
Kiribati,
#[serde(rename = ".km")]
Comoros,
#[serde(rename = ".kn")]
SaintKittsAndNevis,
#[serde(rename = ".kp")]
DemocraticPeoplesRepublicOfKorea,
#[serde(rename = ".kp")]
RepublicOfKorea,
#[serde(rename = ".kw")]
Kuwait,
#[serde(rename = ".ky")]
CaymenIslands,
#[serde(rename = ".kz")]
Kazakhstan,
#[serde(rename = ".la")]
Laos,
#[serde(rename = ".lb")]
Lebanon,
#[serde(rename = ".lc")]
SaintLucia,
#[serde(rename = ".li")]
Liechtenstein,
#[serde(rename = ".lk")]
SriLanka,
#[serde(rename = ".lr")]
Liberia,
#[serde(rename = ".ls")]
Lesotho,
#[serde(rename = ".lt")]
Lithuania,
#[serde(rename = ".lu")]
Luxembourg,
#[serde(rename = ".lv")]
Latvia,
#[serde(rename = ".ly")]
Libya,
#[serde(rename = ".ma")]
Morocco,
#[serde(rename = ".mc")]
Monaco,
#[serde(rename = ".md")]
RepublicOfMoldova,
#[serde(rename = ".me")]
Montenegro,
#[serde(rename = ".mf")]
SaintMartin,
#[serde(rename = ".mg")]
Madagascar,
#[serde(rename = ".mh")]
MarshallIslands,
#[serde(rename = ".mk")]
Macedonia,
#[serde(rename = ".ml")]
Mali,
#[serde(rename = ".mm")]
Myanmar,
#[serde(rename = ".mn")]
Mongolia,
#[serde(rename = ".mo")]
Macao,
#[serde(rename = ".mp")]
NorthernMarianaIslands,
#[serde(rename = ".mq")]
Martinique,
#[serde(rename = ".mr")]
Mauritania,
#[serde(rename = ".ms")]
Montserrat,
#[serde(rename = ".mt")]
Malta,
#[serde(rename = ".mu")]
Mauritius,
#[serde(rename = ".mv")]
Maldives,
#[serde(rename = ".mw")]
Malawi,
#[serde(rename = ".mx")]
Mexico,
#[serde(rename = ".my")]
Malaysia,
#[serde(rename = ".mz")]
Mozambique,
#[serde(rename = ".na")]
Namibia,
#[serde(rename = ".nc")]
NewCaledonia,
#[serde(rename = ".ne")]
Niger,
#[serde(rename = ".nf")]
NorfolkIsland,
#[serde(rename = ".ng")]
Nigeria,
#[serde(rename = ".ni")]
Nicaragua,
#[serde(rename = ".nl")]
Netherlands,
#[serde(rename = ".no")]
Norway,
#[serde(rename = ".np")]
Nepal,
#[serde(rename = ".nr")]
Nauru,
#[serde(rename = ".nu")]
Niue,
#[serde(rename = ".nz")]
NewZealand,
#[serde(rename = ".om")]
Oman,
#[serde(rename = ".pa")]
Panama,
#[serde(rename = ".pe")]
Peru,
#[serde(rename = ".pf")]
FrenchPolynesia,
#[serde(rename = ".pg")]
PapuaNewGuinea,
#[serde(rename = ".ph")]
Philippines,
#[serde(rename = ".pk")]
Pakistan,
#[serde(rename = ".pl")]
Poland,
#[serde(rename = ".pm")]
SaintPierreAndMiquelon,
#[serde(rename = ".pn")]
Pitcairn,
#[serde(rename = ".pr")]
PuertoRico,
#[serde(rename = ".ps")]
Palestine,
#[serde(rename = ".pt")]
Portugal,
#[serde(rename = ".pw")]
Palau,
#[serde(rename = ".py")]
Paraguay,
#[serde(rename = ".qa")]
Qatar,
#[serde(rename = ".re")]
Reunion,
#[serde(rename = ".ro")]
Romania,
#[serde(rename = ".rs")]
Serbia,
#[serde(rename = ".ru")]
Russia,
#[serde(rename = ".rw")]
Rwanda,
#[serde(rename = ".sa")]
SaudiArabia,
#[serde(rename = ".sb")]
SolomonIslands,
#[serde(rename = ".sc")]
Seychelles,
#[serde(rename = ".sd")]
Sudan,
#[serde(rename = ".se")]
Sweden,
#[serde(rename = ".sg")]
Singapore,
#[serde(rename = ".sh")]
SaintHelena,
#[serde(rename = ".si")]
Slovenia,
#[serde(rename = ".sj")]
SvalbardAndJanMayen,
#[serde(rename = ".sk")]
Slovakia,
#[serde(rename = ".sl")]
SierraLeone,
#[serde(rename = ".sm")]
SanMarino,
#[serde(rename = ".sn")]
Senegal,
#[serde(rename = ".so")]
Somalia,
#[serde(rename = ".sr")]
Suriname,
#[serde(rename = ".ss")]
SouthSudan,
#[serde(rename = ".st")]
SaoTomeAndPrincipe,
#[serde(rename = ".su")]
SovietUnion,
#[serde(rename = ".sv")]
ElSalvador,
#[serde(rename = ".sx")]
SintMaarten,
#[serde(rename = ".sy")]
Syria,
#[serde(rename = ".sz")]
Swaziland,
#[serde(rename = ".tc")]
TurksAndCaicosIslands,
#[serde(rename = ".td")]
Chad,
#[serde(rename = ".tf")]
FrenchSouthernTerritories,
#[serde(rename = ".tg")]
Togo,
#[serde(rename = ".th")]
Thailand,
#[serde(rename = ".tj")]
Tajikistan,
#[serde(rename = ".tk")]
Tokelau,
#[serde(rename = ".tl")]
TimorLeste,
#[serde(rename = ".tm")]
Turkmenistan,
#[serde(rename = ".tn")]
Tunisia,
#[serde(rename = ".to")]
Tonga,
#[serde(rename = ".tp")]
PortugueseTimor,
#[serde(rename = ".tr")]
Turkey,
#[serde(rename = ".tt")]
TrinidadAndTobago,
#[serde(rename = ".tv")]
Tuvalu,
#[serde(rename = ".tw")]
Taiwan,
#[serde(rename = ".tz")]
Tanzania,
#[serde(rename = ".ua")]
Ukraine,
#[serde(rename = ".ug")]
Uganda,
#[serde(rename = ".uk")]
UnitedKingdom,
#[serde(rename = ".um")]
UnitedStatesMinorOutlyingIslands,
#[serde(rename = ".us")]
UnitedStates,
#[serde(rename = ".uy")]
Uruguay,
#[serde(rename = ".uz")]
Uzbekistan,
#[serde(rename = ".va")]
VaticanCity,
#[serde(rename = ".vc")]
SaintVincentAndTheGrenadines,
#[serde(rename = ".ve")]
Venezuela,
#[serde(rename = ".vg")]
BritishVirginIslands,
#[serde(rename = ".vi")]
USVirginIslands,
#[serde(rename = ".vn")]
Vietnam,
#[serde(rename = ".vu")]
Vanuatu,
#[serde(rename = ".wf")]
WallisAndFutuna,
#[serde(rename = ".ws")]
Samoa,
#[serde(rename = ".yt")]
Mayote,
#[serde(rename = ".za")]
SouthAfrica,
#[serde(rename = ".zm")]
Zambia,
#[serde(rename = ".zw")]
Zimbabwe,
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct Coordinates {
pub lat: f64,
pub lng: f64,
}
#[derive(Debug, Clone, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct PlaceId(String);