extern crate config;
extern crate dirs;
extern crate prettytable;
extern crate reqwest;
use prettytable::{Table, Row, Cell};
use serde_xml_rs::from_str;
use std::path::Path;
use std::io::prelude::*;
use std::fs;
use std::fs::File;
#[derive(Debug, Deserialize)]
struct Session {
#[serde(default)]
session_id: String,
#[serde(default)]
error: String
}
impl Default for Session {
fn default() -> Session {
Session {
session_id: "".to_string(),
error: "".to_string(),
}
}
}
#[derive(Debug, Deserialize)]
struct Search {
#[serde(default)]
callsign: String,
#[serde(default)]
nick: String,
#[serde(default)]
qth: String,
#[serde(default)]
country: String,
#[serde(default)]
adif: String,
#[serde(default)]
itu: String,
#[serde(rename="CQ", default)]
cq: String,
#[serde(default)]
grid: String,
#[serde(default)]
adr_name: String,
#[serde(default)]
adr_street1: String,
#[serde(default)]
adr_street2: String,
#[serde(default)]
adr_street3: String,
#[serde(default)]
adr_city: String,
#[serde(default)]
adr_zip: String,
#[serde(default)]
adr_country: String,
#[serde(default)]
adr_adif: String,
#[serde(default)]
district: String,
#[serde(default)]
us_state: String,
#[serde(default)]
us_county: String,
#[serde(default)]
oblast: String,
#[serde(default)]
dok: String,
#[serde(default)]
iota: String,
#[serde(default)]
qsl_via: String,
#[serde(default)]
lotw: String,
#[serde(default)]
eqsl: String,
#[serde(default)]
qsl: String,
#[serde(default)]
qsldirect: String,
#[serde(default)]
email: String,
#[serde(default)]
jabber: String,
#[serde(default)]
icq: String,
#[serde(default)]
msn: String,
#[serde(default)]
skype: String,
#[serde(default)]
birth_year: String,
#[serde(default)]
lic_year: String,
#[serde(default)]
picture: String,
#[serde(default)]
latitude: String,
#[serde(default)]
longitude: String,
#[serde(default)]
continent: String,
#[serde(default)]
utc_offset: String,
#[serde(default)]
facebook: String,
#[serde(default)]
twitter: String,
#[serde(default)]
gplus: String,
#[serde(default)]
youtube: String,
#[serde(default)]
linkedin: String,
#[serde(default)]
flicker: String,
#[serde(default)]
vimeo: String
}
impl Default for Search {
fn default() -> Search {
Search {
callsign: "".to_string(),
nick: "".to_string(),
qth: "".to_string(),
country: "".to_string(),
adif: "".to_string(),
itu: "".to_string(),
cq: "".to_string(),
grid: "".to_string(),
adr_name: "".to_string(),
adr_street1: "".to_string(),
adr_street2: "".to_string(),
adr_street3: "".to_string(),
adr_city: "".to_string(),
adr_zip: "".to_string(),
adr_country: "".to_string(),
adr_adif: "".to_string(),
district: "".to_string(),
us_state: "".to_string(),
us_county: "".to_string(),
oblast: "".to_string(),
dok: "".to_string(),
iota: "".to_string(),
qsl_via: "".to_string(),
lotw: "".to_string(),
eqsl: "".to_string(),
qsl: "".to_string(),
qsldirect: "".to_string(),
email: "".to_string(),
jabber: "".to_string(),
icq: "".to_string(),
msn: "".to_string(),
skype: "".to_string(),
birth_year: "".to_string(),
lic_year: "".to_string(),
picture: "".to_string(),
latitude: "".to_string(),
longitude: "".to_string(),
continent: "".to_string(),
utc_offset: "".to_string(),
facebook: "".to_string(),
twitter: "".to_string(),
gplus: "".to_string(),
youtube: "".to_string(),
linkedin: "".to_string(),
flicker: "".to_string(),
vimeo: "".to_string()
}
}
}
#[derive(Debug, Deserialize)]
struct Dxcc {
#[serde(default)]
callsign: String,
#[serde(default)]
name: String,
#[serde(default)]
details: String,
#[serde(default)]
continent: String,
#[serde(default)]
utc: String,
#[serde(default)]
waz: String,
#[serde(default)]
itu: String,
#[serde(default)]
lat: String,
#[serde(default)]
lon: String,
#[serde(default)]
adif: String
}
impl Default for Dxcc {
fn default() -> Dxcc {
Dxcc {
callsign: "".to_string(),
name: "".to_string(),
details: "".to_string(),
continent: "".to_string(),
utc: "".to_string(),
waz: "".to_string(),
itu: "".to_string(),
lat: "".to_string(),
lon: "".to_string(),
adif: "".to_string()
}
}
}
#[derive(Debug, Deserialize)]
struct HamQTH {
version: String,
#[serde(default)]
session: Session,
#[serde(default)]
search: Search,
#[serde(default)]
dxcc: Dxcc
}
fn session() -> Result<(), reqwest::Error> {
let home_dir = match dirs::home_dir() {
Some(path) => path,
None => panic!("error"),
};
let config_path = format!("{}{}", home_dir.to_string_lossy(), "/.hrt.toml");
let mut settings = config::Config::default();
settings.merge(config::File::from(Path::new(&config_path))).unwrap();
let username = settings.get_str("hamqth_callsign").unwrap();
let password = settings.get_str("hamqth_password").unwrap();
let client = reqwest::Client::new();
let query_resp = client.get("https://www.hamqth.com/xml.php")
.query(&[("u", username), ("p", password)])
.send()?
.text()?;
let hqth: HamQTH = from_str(&query_resp).unwrap();
if hqth.session.error != ""
{
panic!("ERROR! {}", hqth.session.error);
} else {
set_session(hqth.session.session_id);
Ok(())
}
}
fn set_session(key: String) -> std::io::Result<()> {
let home_dir = match dirs::home_dir() {
Some(path) => path,
None => panic!("error"),
};
let session_path = format!("{}{}", home_dir.to_string_lossy(), "/.hrt.hamqth");
fs::write(&session_path, key)?;
Ok(())
}
fn get_session() -> String {
let home_dir = match dirs::home_dir() {
Some(path) => path,
None => panic!("error"),
};
let session_path = format!("{}{}", home_dir.to_string_lossy(), "/.hrt.hamqth");
if !Path::new(&session_path).exists() {
let _s = match session() {
Ok(k) => k,
Err(_e) => panic!("error")
};
}
let mut file = File::open(&session_path).expect("Unable to open the file");
let mut session_id = String::new();
file.read_to_string(&mut session_id).expect("Unable to read the file");
return session_id;
}
pub fn query(callsign: &str) -> Result<(), reqwest::Error> {
let session_id = get_session();
let client = reqwest::Client::new();
let query_resp = client.get("https://www.hamqth.com/xml.php")
.query(&[("id", session_id), ("callsign", callsign.to_string()), ("prg", "hrt".to_string())])
.send()?
.text()?;
let hqth: HamQTH = from_str(&query_resp).unwrap();
let mut table = Table::new();
if hqth.session.error == "Session does not exist or expired" {
let _s = match session() {
Ok(k) => k,
Err(_e) => panic!("error")
};
query(callsign)?;
Ok(())
} else if hqth.session.error != "" {
table.add_row(Row::new(vec![
Cell::new("ERROR"),
Cell::new(&hqth.session.error)]));
println!("");
table.printstd();
println!("Source: HamQTH\n");
Ok(())
} else {
table.add_row(Row::new(vec![
Cell::new("Callsign"),
Cell::new(&hqth.search.callsign.to_uppercase())]));
table.add_row(Row::new(vec![
Cell::new("Name"),
Cell::new(&hqth.search.adr_name)]));
if !hqth.search.email.is_empty() {
table.add_row(Row::new(vec![
Cell::new("Email"),
Cell::new(&hqth.search.email)]));
}
if !hqth.search.adr_street1.is_empty() {
table.add_row(Row::new(vec![
Cell::new("Address"),
Cell::new(&hqth.search.adr_street1)]));
}
if !hqth.search.adr_city.is_empty() {
if !hqth.search.us_state.is_empty() {
table.add_row(Row::new(vec![
Cell::new("Location"),
Cell::new(&format!("{}, {} {}", hqth.search.adr_city, hqth.search.us_state, hqth.search.adr_zip))]));
}
else {
table.add_row(Row::new(vec![
Cell::new("Location"),
Cell::new(&hqth.search.adr_city)]));
}
}
table.add_row(Row::new(vec![
Cell::new("Country"),
Cell::new(&hqth.search.adr_country)]));
println!("");
table.printstd();
println!("Source: HamQTH\n");
Ok(())
}
}
pub fn dxcc(entity: &str) -> Result<(), reqwest::Error> {
let client = reqwest::Client::new();
let query_resp = client.get("https://www.hamqth.com/dxcc.php")
.query(&[("callsign", entity)])
.send()?
.text()?;
let hqth: HamQTH = from_str(&query_resp).unwrap();
let mut table = Table::new();
table.add_row(Row::new(vec![
Cell::new("ADIF"),
Cell::new(&hqth.dxcc.adif)]));
table.add_row(Row::new(vec![
Cell::new("Name"),
Cell::new(&hqth.dxcc.name)]));
table.add_row(Row::new(vec![
Cell::new("ITU"),
Cell::new(&hqth.dxcc.itu)]));
table.add_row(Row::new(vec![
Cell::new("UTC"),
Cell::new(&hqth.dxcc.utc)]));
table.add_row(Row::new(vec![
Cell::new("Details"),
Cell::new(&hqth.dxcc.details)]));
println!("");
table.printstd();
println!("Source: HamQTH\n");
Ok(())
}