Skip to content

Commit 6de39b1

Browse files
committed
Move from dog to trust-dns-resolver, refactor code
1 parent c269c91 commit 6de39b1

9 files changed

+999
-415
lines changed

Cargo.lock

+692-55
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "myip"
3-
version = "1.1.0"
4-
authors = ["Vyacheslav Konovalov <🦀vk@protonmail.com>"]
3+
version = "1.2.0"
4+
authors = ["Vyacheslav Konovalov <crabvk@protonmail.com>"]
55
description = "Simple command-line tool to get your external IP address."
66
homepage = "https://github.com/crabvk/myip"
77
repository = "https://github.com/crabvk/myip"
@@ -12,14 +12,14 @@ categories = ["command-line-utilities"]
1212
edition = "2018"
1313

1414
[dependencies]
15-
dns = { git = "https://github.com/ogham/dog" }
16-
dns-transport = { git = "https://github.com/ogham/dog" }
17-
maxminddb = "0.21"
15+
trust-dns-resolver = "0.23.0"
16+
maxminddb = "0.23.0"
1817
ansi_term = "0.12"
1918
serde = "1.0"
2019
serde_json = "1.0"
21-
lexopt = "0.2"
20+
lexopt = "0.3.0"
2221
atty = "0.2"
22+
rand = "0.8"
2323

2424
[profile.release]
2525
lto = true

src/args_parser.rs src/argparser.rs

+23-12
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use std::net::IpAddr;
22
use std::path::PathBuf;
33
use std::process::exit;
4-
use std::str::FromStr;
4+
use std::{error, fmt, str::FromStr};
55

66
fn help_msg() -> String {
77
format!(
8-
"Simple command-line tool to get your external IP address.
8+
"{}
99
1010
Usage:
1111
myip [options]
@@ -19,7 +19,8 @@ Options:
1919
2020
-h --help Prints help information
2121
-v --version Prints version information",
22-
crate::DATABASE_PATH,
22+
env!("CARGO_PKG_DESCRIPTION"),
23+
crate::MMDB_PATH,
2324
crate::COLORS.join(", ")
2425
)
2526
}
@@ -31,7 +32,6 @@ pub struct Args {
3132
pub color: Color,
3233
pub inet6: bool,
3334
pub json: bool,
34-
version: bool,
3535
}
3636

3737
pub fn from_env() -> Result<Args, lexopt::Error> {
@@ -43,7 +43,6 @@ pub fn from_env() -> Result<Args, lexopt::Error> {
4343
color: Color::Auto,
4444
inet6: false,
4545
json: false,
46-
version: false,
4746
};
4847

4948
let mut parser = lexopt::Parser::from_env();
@@ -57,7 +56,7 @@ pub fn from_env() -> Result<Args, lexopt::Error> {
5756
if let Some(path) = parser.optional_value() {
5857
args.lookup = Some(path.into_string()?.into())
5958
} else {
60-
args.lookup = Some(PathBuf::from(crate::DATABASE_PATH))
59+
args.lookup = Some(PathBuf::from(crate::MMDB_PATH))
6160
}
6261
}
6362
Short('c') | Long("color") => args.color = parser.value()?.parse()?,
@@ -85,19 +84,31 @@ pub enum Color {
8584
Never,
8685
}
8786

87+
#[derive(Debug)]
88+
pub struct ParseColorError(String);
89+
90+
impl fmt::Display for ParseColorError {
91+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92+
write!(
93+
f,
94+
"unknown variant \"{}\", possible values: {}",
95+
self.0,
96+
crate::COLORS.join(", ")
97+
)
98+
}
99+
}
100+
101+
impl error::Error for ParseColorError {}
102+
88103
impl FromStr for Color {
89-
type Err = String;
104+
type Err = ParseColorError;
90105

91106
fn from_str(input: &str) -> Result<Color, Self::Err> {
92107
match input {
93108
"auto" => Ok(Color::Auto),
94109
"always" => Ok(Color::Always),
95110
"never" => Ok(Color::Never),
96-
_ => Err(format!(
97-
"Invalid color '{}' [possible values: {}]",
98-
input,
99-
crate::COLORS.join(", ")
100-
)),
111+
color => Err(ParseColorError(color.to_owned())),
101112
}
102113
}
103114
}

src/dns_error.rs

-59
This file was deleted.

src/formatter.rs

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
use ansi_term::Color::{Blue, Green};
2+
use ansi_term::Style;
3+
use maxminddb::geoip2::{Asn, City};
4+
use std::char;
5+
use std::collections::HashMap;
6+
use std::net::IpAddr;
7+
8+
pub struct GeoIpData<'a> {
9+
city: Option<&'a str>,
10+
country: Option<(&'a str, &'a str)>,
11+
region: Option<(&'a str, &'a str)>,
12+
registred: Option<(&'a str, &'a str)>,
13+
isp: Option<&'a str>,
14+
time_zone: Option<&'a str>,
15+
}
16+
17+
impl<'a> GeoIpData<'a> {
18+
fn new() -> Self {
19+
Self {
20+
city: None,
21+
country: None,
22+
region: None,
23+
registred: None,
24+
isp: None,
25+
time_zone: None,
26+
}
27+
}
28+
}
29+
30+
pub trait Formatter {
31+
fn format(&self, ip: IpAddr, data: GeoIpData) -> String;
32+
}
33+
34+
pub struct TextFormatter(pub bool);
35+
36+
impl TextFormatter {
37+
fn format_pair(&self, name: &str, value: &str) -> String {
38+
let indent = 10 - name.len();
39+
40+
if self.0 {
41+
format!(
42+
"{:indent$}{}{} {}",
43+
"",
44+
Blue.bold().paint(name),
45+
Style::new().bold().paint(":"),
46+
Green.paint(value),
47+
indent = indent
48+
)
49+
} else {
50+
format!("{:indent$}{}: {}", "", name, value, indent = indent)
51+
}
52+
}
53+
}
54+
55+
impl Formatter for TextFormatter {
56+
fn format(&self, ip: IpAddr, data: GeoIpData) -> String {
57+
let mut lines = vec![];
58+
59+
lines.push(self.format_pair("IP", ip.to_string().as_str()));
60+
if let Some(city) = data.city {
61+
lines.push(self.format_pair("City", city));
62+
}
63+
if let Some((country, country_code)) = data.country {
64+
let value = format!("{} ({})", country, country_code);
65+
lines.push(self.format_pair("Country", &value));
66+
}
67+
if let Some((region, region_code)) = data.region {
68+
let value = format!("{} ({})", region, region_code);
69+
lines.push(self.format_pair("Region", &value));
70+
}
71+
if let Some((registred, registred_code)) = data.registred {
72+
let value = format!("{} ({})", registred, registred_code);
73+
lines.push(self.format_pair("Registered", &value));
74+
}
75+
if let Some(isp) = data.isp {
76+
lines.push(self.format_pair("ISP", isp));
77+
}
78+
if let Some(time_zone) = data.time_zone {
79+
lines.push(self.format_pair("Time zone", time_zone));
80+
}
81+
82+
lines.join("\n")
83+
}
84+
}
85+
86+
pub struct JsonFormatter;
87+
88+
impl Formatter for JsonFormatter {
89+
fn format(&self, ip: IpAddr, data: GeoIpData) -> String {
90+
let ip = ip.to_string();
91+
let mut hm = HashMap::new();
92+
let flag;
93+
94+
hm.insert("IP", ip.as_str());
95+
if let Some(city) = data.city {
96+
hm.insert("City", city);
97+
}
98+
if let Some((country, country_code)) = data.country {
99+
hm.insert("Country", country);
100+
hm.insert("CountryCode", country_code);
101+
flag = get_flag(&country_code);
102+
hm.insert("Flag", &flag);
103+
}
104+
if let Some((region, region_code)) = data.region {
105+
hm.insert("Region", region);
106+
hm.insert("RegionCode", region_code);
107+
}
108+
if let Some((registred, registred_code)) = data.registred {
109+
hm.insert("Registered", registred);
110+
hm.insert("RegisteredCode", registred_code);
111+
}
112+
if let Some(isp) = data.isp {
113+
hm.insert("ISP", isp);
114+
}
115+
if let Some(time_zone) = data.time_zone {
116+
hm.insert("TimeZone", time_zone);
117+
}
118+
119+
serde_json::to_string(&hm).unwrap()
120+
}
121+
}
122+
123+
pub fn map_data<'a>(city: City<'a>, asn: Asn<'a>) -> GeoIpData<'a> {
124+
let mut data = GeoIpData::new();
125+
126+
data.city = city.city.map(|city| city.names.as_ref().unwrap()["en"]);
127+
data.country = city.country.map(|country| {
128+
let name = country.names.as_ref().unwrap()["en"];
129+
let code = country.iso_code.unwrap();
130+
(name, code)
131+
});
132+
data.region = city.subdivisions.map(|subdiv| {
133+
let region = subdiv.first().unwrap();
134+
let name = region.names.as_ref().unwrap()["en"];
135+
let code = region.iso_code.unwrap();
136+
(name, code)
137+
});
138+
city.registered_country.map(|reg| {
139+
let name = reg.names.as_ref().unwrap()["en"];
140+
let code = reg.iso_code.unwrap();
141+
(name, code)
142+
});
143+
data.time_zone = city.location.map(|location| location.time_zone.unwrap());
144+
data.isp = asn.autonomous_system_organization.map(|isp| isp);
145+
data
146+
}
147+
148+
// https://stackoverflow.com/a/42235254/1878180
149+
fn get_flag(iso_code: &str) -> String {
150+
let offset: u32 = 0x1F1A5;
151+
let mut country = iso_code.bytes();
152+
let char0 = country.next().unwrap() as u32 + offset;
153+
let char1 = country.next().unwrap() as u32 + offset;
154+
155+
format!(
156+
"{}{}",
157+
char::from_u32(char0).unwrap(),
158+
char::from_u32(char1).unwrap()
159+
)
160+
}

src/ip.rs

-61
This file was deleted.

0 commit comments

Comments
 (0)