geoiplocation/
lib.rs

1//! Find location given the IP address.
2//!
3//! Provides abstraction over the fields returned by the JSON response. Returns a `Struct` for the
4//! given query.
5//!
6//! You can set your own env for location api using something like `export SUBCOM_LOCATION_API_URL=<your-url>`
7//! The final URL looks something like `your-url/IP?apikey=KEY`.
8//!
9//! Two methods are provides(more documentation provided):
10//!
11//! - `async fn get_location(ip: &str, key: &str) -> anyhow::Result<Option<Location>>`
12//! - `async fn get_location_fallback(ip: &str, key: &str) -> anyhow::Result<HashMap<String, String>>`
13//!
14//! **NOTE:** Still in production so a few things might break or change, you can use `cargo doc --open` for better
15//! experience or read it [here](https://docs.rs/geoiplocation/latest/geoiplocation/index.html).
16
17use crate::geoiplocation::Location;
18use reqwest::Url;
19
20pub mod geoiplocation;
21use std::collections::HashMap;
22use std::env;
23
24/// `async` function to get the location
25///
26/// Eg:
27/// ```rust
28/// use geoiplocation::get_location;
29///
30/// #[tokio::main]
31/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
32///     let resp = get_location(
33///         "8.8.8.8".parse().unwrap(),
34///         "YOUR-KEY",
35///     )
36///     .await?;
37///     println!("{:#?}", resp);
38///     // Some(Location { ip: Some("8.8.8.8"), city: Some(""), country: Some("United States"), continent: Some("North America") })
39///
40///     Ok(())
41/// }
42/// ```
43pub async fn get_location(ip: &str, key: &str) -> anyhow::Result<Option<Location>> {
44    let location_api_url = env::var("SUBCOM_LOCATION_API_URL").expect("cannot parse URL");
45
46    let mut url = Url::parse(&*format!("{}/{}", location_api_url, ip))?;
47    url.set_query(Some(&*format!("apikey={}", key)));
48
49    Ok(Some(reqwest::get(url).await?.json::<Location>().await?))
50}
51
52/// An `async` fallback method to get JSON response
53///
54/// Instead of holding the values in a Struct he hold the in `HashMap<String, String>`, this should
55/// provide a fallback in case the api is ever changing to keep on updating the struct
56pub async fn get_location_fallback(ip: &str, key: &str) -> anyhow::Result<HashMap<String, String>> {
57    let location_api_url = env::var("SUBCOM_LOCATION_API_URL").expect("cannot parse URL");
58
59    let mut url = Url::parse(&*format!("{}/{}", location_api_url, ip))?;
60    url.set_query(Some(&*format!("apikey={}", key)));
61
62    Ok(reqwest::get(url)
63        .await?
64        .json::<HashMap<String, String>>()
65        .await?)
66}