use anyhow::{Context, Error};
use dirs::home_dir;
use gip::{Provider, ProviderAny, ProviderInfoType};
use std::fs::File;
use std::io::Read;
use structopt::{clap, StructOpt};
#[derive(Debug, StructOpt)]
#[structopt(name = "gip")]
#[structopt(
long_version(option_env!("LONG_VERSION").unwrap_or(env!("CARGO_PKG_VERSION")))
)]
#[structopt(setting(clap::AppSettings::ColoredHelp))]
#[structopt(setting(clap::AppSettings::DeriveDisplayOrder))]
pub struct Opt {
#[structopt(short = "4", long = "v4", conflicts_with = "v6")]
pub v4: bool,
#[structopt(short = "6", long = "v6", conflicts_with = "v4")]
pub v6: bool,
#[structopt(
short = "p",
long = "plane",
conflicts_with = "show_string",
conflicts_with = "show_json"
)]
pub show_plane: bool,
#[structopt(
short = "s",
long = "string",
conflicts_with = "show_plane",
conflicts_with = "show_json"
)]
pub show_string: bool,
#[structopt(
short = "j",
long = "json",
conflicts_with = "show_plane",
conflicts_with = "show_string"
)]
pub show_json: bool,
#[structopt(long = "timeout", default_value = "1000")]
pub timeout: usize,
#[structopt(long = "json-key", default_value = "ip")]
pub json_key: String,
#[structopt(long = "proxy")]
pub proxy: Option<String>,
#[structopt(short = "l", long = "list")]
pub show_list: bool,
#[structopt(short = "v", long = "verbose")]
pub verbose: bool,
}
fn main() -> Result<(), Error> {
run()
}
pub fn run() -> Result<(), Error> {
let opt = Opt::from_args();
run_opt(&opt)
}
pub fn run_opt(opt: &Opt) -> Result<(), Error> {
let giprc = match home_dir() {
Some(mut p) => {
p.push(".gip.toml");
if p.exists() {
Some(p)
} else {
None
}
}
None => None,
};
let mut client = match giprc {
Some(p) => {
let mut f =
File::open(&p).context(format!("failed to open {}", p.to_string_lossy()))?;
let mut s = String::new();
let _ = f.read_to_string(&mut s);
ProviderAny::from_toml(&s)?
}
None => ProviderAny::from_toml(&gip::DEFAULT_TOML)?,
};
if opt.v6 {
client.ptype = ProviderInfoType::IPv6;
}
if opt.show_list {
for p in &client.providers {
println!("{:?}: {}", p.get_type(), p.get_name());
}
return Ok(());
}
client.set_timeout(opt.timeout);
if opt.proxy.is_some() {
let proxy_str = opt.proxy.clone().unwrap();
let (host, port) = proxy_str.split_at(proxy_str.find(':').unwrap_or(0));
let port = port
.trim_matches(':')
.parse::<u16>()
.context(format!("failed to parse proxy: {}", proxy_str))?;
client.set_proxy(host, port);
}
let addr = client.get_addr()?;
let addr_str = if opt.v6 {
format!("{:?}", addr.v6addr.unwrap())
} else {
format!("{:?}", addr.v4addr.unwrap())
};
if opt.verbose {
println!("IP Address: {}", addr_str);
println!("Provider : {}", addr.provider);
println!("Check Time: {}", addr.time);
println!("Latency : {}ms", addr.latency.as_millis());
} else {
if opt.show_string {
print!("{}", addr_str);
} else if opt.show_json {
println!("{{\"{}\": \"{}\"}}", opt.json_key, addr_str);
} else {
println!("{}", addr_str);
}
}
return Ok(());
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_run() {
let args = vec!["gip"];
let opt = Opt::from_iter(args.iter());
let ret = run_opt(&opt);
assert!(ret.is_ok());
}
#[test]
fn test_verbose() {
let args = vec!["gip", "-v"];
let opt = Opt::from_iter(args.iter());
let ret = run_opt(&opt);
assert!(ret.is_ok());
}
#[test]
fn test_string() {
let args = vec!["gip", "-s"];
let opt = Opt::from_iter(args.iter());
let ret = run_opt(&opt);
assert!(ret.is_ok());
}
#[test]
fn test_json() {
let args = vec!["gip", "-j"];
let opt = Opt::from_iter(args.iter());
let ret = run_opt(&opt);
assert!(ret.is_ok());
}
#[test]
fn test_list() {
let args = vec!["gip", "-l"];
let opt = Opt::from_iter(args.iter());
let ret = run_opt(&opt);
assert!(ret.is_ok());
}
#[test]
fn test_v6() {
let args = vec!["gip", "-6"];
let opt = Opt::from_iter(args.iter());
let _ = run_opt(&opt);
}
#[test]
fn test_proxy() {
let args = vec!["gip", "--proxy", "example.com:8080"];
let opt = Opt::from_iter(args.iter());
let _ = run_opt(&opt);
}
}