use clap::{crate_name, crate_version};
use eyre::Result;
const ENDPOINT4: &str = "https://api.ipify.org";
const ENDPOINT6: &str = "https://api64.ipify.org";
const ENDPOINT4J: &str = "https://api.ipify.org?format=json";
const ENDPOINT6J: &str = "https://api64.ipify.org?format=json";
#[inline]
pub fn myip() -> String {
Ipify::new().set(Op::IPv6).call().unwrap()
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Op {
IPv4,
IPv6,
IPv4J,
IPv6J,
}
#[derive(Clone, Debug)]
pub struct Ipify {
pub t: Op,
pub endp: String,
}
impl Default for Ipify {
fn default() -> Self {
Self::new()
}
}
impl Ipify {
pub fn new() -> Self {
Self {
t: Op::IPv6,
endp: ENDPOINT6.to_owned(),
}
}
pub fn set(&self, op: Op) -> Self {
Self {
t: op,
endp: match op {
Op::IPv4 => ENDPOINT4.to_owned(),
Op::IPv6 => ENDPOINT6.to_owned(),
Op::IPv4J => ENDPOINT4J.to_owned(),
Op::IPv6J => ENDPOINT6J.to_owned(),
},
}
}
pub fn call(self) -> Result<String> {
let c = reqwest::blocking::ClientBuilder::new()
.user_agent(format!("{}/{}", crate_name!(), crate_version!()))
.build()?;
Ok(c.get(self.endp).send()?.text()?)
}
pub async fn call_async(self) -> Result<String> {
let c = reqwest::ClientBuilder::new()
.user_agent(format!("{}/{}", crate_name!(), crate_version!()))
.build()?;
Ok(c.get(self.endp).send().await?.text().await?)
}
}
#[cfg(test)]
mod tests {
use super::*;
use httpmock::prelude::*;
use std::net::IpAddr;
#[test]
fn test_set_1() {
let c = Ipify::new();
assert_eq!(Op::IPv6, c.t);
let c = c.set(Op::IPv4J);
assert_eq!(Op::IPv4J, c.t);
let c = c.set(Op::IPv6);
assert_eq!(Op::IPv6, c.t);
}
#[test]
fn test_set_2() {
let c = Ipify::new();
let c = c.set(Op::IPv4J).set(Op::IPv6J);
assert_eq!(Op::IPv6J, c.t);
}
#[test]
fn test_with_1() {
let c = Ipify::new();
assert_eq!(Op::IPv6, c.t);
}
#[test]
fn test_with_set() {
let c = Ipify::new();
assert_eq!(Op::IPv6, c.t);
let c = c.set(Op::IPv4);
assert_eq!(Op::IPv4, c.t);
let c = c.set(Op::IPv4J);
assert_eq!(Op::IPv4J, c.t);
}
#[test]
fn test_myip() {
let server = MockServer::start();
let m = server.mock(|when, then| {
when.method(GET).header(
"user-agent",
format!("{}/{}", crate_name!(), crate_version!()),
);
then.status(200).body("192.0.2.1");
});
let mut c = Ipify::new();
let b = server.base_url().clone();
c.endp = b.to_owned();
let str = c.call();
assert!(str.is_ok());
let str = str.unwrap();
let ip = str.parse::<IpAddr>();
m.assert();
assert!(ip.is_ok());
assert_eq!("192.0.2.1", str);
}
#[tokio::test]
async fn test_async_call() {
let server = MockServer::start_async().await;
let m = server
.mock_async(|when, then| {
when.method(GET).header(
"user-agent",
format!("{}/{}", crate_name!(), crate_version!()),
);
then.status(200).body("192.0.2.1");
})
.await;
let mut c = Ipify::new();
let b = server.base_url().clone();
c.endp = b.to_owned();
let str = c.call_async().await;
assert!(str.is_ok());
let str = str.unwrap();
let ip = str.parse::<IpAddr>();
m.assert_async().await;
assert!(ip.is_ok());
assert_eq!("192.0.2.1", str);
}
#[test]
fn test_set_to_ipv4j() {
let c = Ipify::new().set(Op::IPv4J);
assert_eq!(Op::IPv4J, c.t);
assert_eq!(ENDPOINT4J, c.endp);
}
#[test]
fn test_set_to_ipv6() {
let c = Ipify::new().set(Op::IPv6);
assert_eq!(Op::IPv6, c.t);
assert_eq!(ENDPOINT6, c.endp);
}
#[test]
fn test_call_ipv4() {
let server = MockServer::start();
let m = server.mock(|when, then| {
when.method(GET);
then.status(200).body("203.0.113.1");
});
let mut c = Ipify::new().set(Op::IPv4);
c.endp = server.base_url();
let str = c.call();
assert!(str.is_ok());
let str = str.unwrap();
m.assert();
assert_eq!("203.0.113.1", str);
}
#[tokio::test]
async fn test_async_call_with_ipv6j() {
let server = MockServer::start_async().await;
let m = server
.mock_async(|when, then| {
when.method(GET);
then.status(200).body("{\"ip\":\"2001:db8::2\"}");
})
.await;
let mut c = Ipify::new().set(Op::IPv6J);
c.endp = server.base_url();
let str = c.call_async().await;
assert!(str.is_ok());
let str = str.unwrap();
m.assert_async().await;
assert_eq!("{\"ip\":\"2001:db8::2\"}", str);
}
#[test]
fn test_default_values() {
let c = Ipify::new();
assert_eq!(Op::IPv6, c.t);
assert_eq!(ENDPOINT6, c.endp);
}
#[test]
fn test_chaining_set_calls() {
let c = Ipify::new().set(Op::IPv4).set(Op::IPv4J).set(Op::IPv6);
assert_eq!(Op::IPv6, c.t);
assert_eq!(ENDPOINT6, c.endp);
}
}