ipapi_client/
lib.rs

1use serde::{Deserialize, Serialize};
2#[derive(Serialize, Deserialize, Debug)]
3pub struct IpInfo {
4    pub ip: String,
5    #[serde(rename = "type")]
6    pub ip_type: String,
7    pub continent_code: String,
8    pub continent_name: String,
9    pub country_code: String,
10    pub country_name: String,
11    pub region_code: String,
12    pub region_name: String,
13    pub city: String,
14    pub zip: String,
15    pub latitude: f64,
16    pub longitude: f64,
17    pub location: Location,
18    pub time_zone: TimeZone,
19    pub currency: Currency,
20    pub connection: Connection,
21}
22
23#[derive(Serialize, Deserialize, Debug)]
24pub struct Location {
25    pub geoname_id: u32,
26    pub capital: String,
27    pub languages: Vec<Language>,
28    pub country_flag: String,
29    pub country_flag_emoji: String,
30    pub country_flag_emoji_unicode: String,
31    pub calling_code: String,
32    pub is_eu: bool,
33}
34
35#[derive(Serialize, Deserialize, Debug)]
36pub struct Language {
37    pub code: String,
38    pub name: String,
39    pub native: String,
40}
41
42#[derive(Serialize, Deserialize, Debug)]
43pub struct TimeZone {
44    pub id: String,
45    pub current_time: String,
46    pub gmt_offset: i32,
47    pub code: String,
48    pub is_daylight_saving: bool,
49}
50
51#[derive(Serialize, Deserialize, Debug)]
52pub struct Currency {
53    pub code: String,
54    pub name: String,
55    pub plural: String,
56    pub symbol: String,
57    pub symbol_native: String,
58}
59
60#[derive(Serialize, Deserialize, Debug)]
61pub struct Connection {
62    pub asn: u32,
63    pub isp: String,
64}
65use std::fmt;
66
67impl fmt::Display for IpInfo {
68    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69        write!(
70            f,
71            "{}, {}, {}, {}",
72            self.city, self.region_name, self.country_name, self.zip
73        )
74    }
75}
76
77#[derive(Clone)]
78#[cfg(feature = "reqwest")]
79pub struct IpApiClient {
80    client: reqwest::Client,
81    base_url: String,
82    access_key: Option<String>,
83}
84#[cfg(feature = "reqwest")]
85extern crate reqwest;
86#[cfg(feature = "reqwest")]
87impl IpApiClient {
88    pub fn new_from_env() -> Self {
89        Self::new(if let Ok(x) = std::env::var("IPAPI_ACCESS_KEY") {
90            Some(x)
91        } else {
92            None
93        })
94    }
95    pub fn new(access_key: Option<String>) -> Self {
96        Self {
97            access_key,
98            client: reqwest::Client::new(),
99            base_url: "https://api.ipapi.com/api".into(),
100        }
101    }
102
103    pub async fn lookup(&self, ip: String) -> Result<IpInfo, reqwest::Error> {
104        let mut params = vec![];
105        if let Some(x) = &self.access_key {
106            params.push(("access_key", x.clone()));
107        }
108        let mut url = reqwest::Url::parse_with_params(&self.base_url, params).unwrap();
109        url.path_segments_mut().unwrap().push(ip.as_str());
110        Ok(self.client.get(url).send().await?.json().await?)
111    }
112}