use anyhow::Result;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ConnectionInfo {
pub ip: String,
pub isp: Option<String>,
pub location: Option<String>,
pub country: Option<String>,
pub city: Option<String>,
pub region: Option<String>,
pub timezone: Option<String>,
}
#[derive(Debug, Deserialize)]
struct IpInfoResponse {
ip: String,
org: Option<String>,
city: Option<String>,
region: Option<String>,
country: Option<String>,
timezone: Option<String>,
}
pub async fn get_connection_info() -> Result<ConnectionInfo> {
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(10))
.build()?;
let response = client
.get("https://ipinfo.io/json")
.header("Accept", "application/json")
.send()
.await?;
if response.status().is_success() {
let info: IpInfoResponse = response.json().await?;
let location = match (&info.city, &info.region, &info.country) {
(Some(city), Some(region), Some(country)) => {
Some(format!("{}, {}, {}", city, region, country))
}
(Some(city), _, Some(country)) => Some(format!("{}, {}", city, country)),
(_, _, Some(country)) => Some(country.clone()),
_ => None,
};
return Ok(ConnectionInfo {
ip: info.ip,
isp: info.org,
location,
country: info.country,
city: info.city,
region: info.region,
timezone: info.timezone,
});
}
let response = client
.get("https://api.ipify.org?format=json")
.send()
.await?;
#[derive(Deserialize)]
struct IpifyResponse {
ip: String,
}
let info: IpifyResponse = response.json().await?;
Ok(ConnectionInfo {
ip: info.ip,
..Default::default()
})
}
#[allow(dead_code)]
pub fn get_local_ip() -> Result<String> {
let ip = local_ip_address::local_ip()?;
Ok(ip.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_get_connection_info() {
let result = get_connection_info().await;
assert!(result.is_ok());
let info = result.unwrap();
assert!(!info.ip.is_empty());
}
#[test]
fn test_get_local_ip() {
let result = get_local_ip();
assert!(result.is_ok());
}
}