use curl::http;
use serde_json;
use std::fmt;
use self::DTResponseType::*;
pub enum DTResponseType {
Dig,
ErrorTranslate,
Geo,
Locations,
Mtr,
Translate,
Verify,
}
#[cfg(feature = "serde_macros")]
include!("resp.rs.in");
#[cfg(not(feature = "serde_macros"))]
include!(concat!(env!("OUT_DIR"), "/luna/dt/resp.rs"));
impl fmt::Display for DTError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let detail = match self.detail {
Some(ref s) => &s[..],
None => "",
};
write!(f, "{}", detail)
}
}
impl fmt::Display for LogLine {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let log_line = match self.log_line {
Some(ref l) => &l[..],
None => "",
};
let fields = match self.fields {
Some(ref f) => &f[..],
None => "",
};
write!(f, "{}: {}", log_line, fields)
}
}
fn to_str<'a>(opt: &'a Option<String>) -> &'a str {
match *opt {
Some(ref o) => &o[..],
None => "",
}
}
impl fmt::Display for MtrHop {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
" {: <7}{: <27} loss: {: >5.5} sent: {: >4.4} last: {: >6.6} \
avg: {: >6.6} best: {: >6.6} worst: {: >6.6} stdev: {: >6.6}",
to_str(&self.num),
to_str(&self.host),
to_str(&self.loss),
to_str(&self.sent),
to_str(&self.last),
to_str(&self.avg),
to_str(&self.best),
to_str(&self.worst),
to_str(&self.st_dev)
)
}
}
fn push_opt<T>(out: &mut String, prefix: &str, opt: Option<T>) where
T: fmt::Display
{
match opt {
Some(o) => { out.push_str(&format!("{}{}\n", prefix, o)[..]); },
None => {},
}
}
fn parse_dt_error(body: String) -> ::EdgeGridResult {
let dte: DTError = try!(serde_json::from_str(&body));
debug!("{:?}", dte);
Err(::EdgeGridError{
title: match dte.title {
Some(t) => t,
None => String::from("DTError"),
},
detail: match dte.detail {
Some(d) => d,
None => String::from("Unknown Error!"),
},
})
}
fn parse_response<F>(
resp: http::Response,
success: F
) -> Result<String, ::EdgeGridError> where
F: Fn(String) -> ::EdgeGridResult
{
let body_vec = Vec::from(resp.get_body());
let body = try!(String::from_utf8(body_vec));
::response::check_code(resp, body, success, parse_dt_error)
}
fn parse_dig(resp: http::Response) -> ::EdgeGridResult {
parse_response(resp, |b| {
let dr: DigResponse = try!(serde_json::from_str(&b));
debug!("{:?}", dr);
let out = match dr.dig.result {
Some(r) => r,
None => String::from("DIG: No Results!"),
};
Ok(out)
})
}
fn parse_error_translate(resp: http::Response) -> ::EdgeGridResult {
parse_response(resp, |b| {
let etr: ErrorTranslateResponse = try!(serde_json::from_str(&b));
debug!("{:?}", etr);
let mut out = String::new();
let et = etr.error_translator;
push_opt(&mut out, "URL: ", et.url);
push_opt(&mut out, "HTTP Response Code: ", et.http_response_code);
push_opt(&mut out, "Date/Time: ", et.date_time);
push_opt(&mut out, "Epoch Time: ", et.epoch_time);
push_opt(&mut out, "Client IP: ", et.client_ip);
push_opt(&mut out, "Server IP: ", et.server_ip);
push_opt(&mut out, "Origin Hostname: ", et.origin_hostname);
push_opt(&mut out, "Origin IP: ", et.origin_ip);
push_opt(&mut out, "User Agent: ", et.user_agent);
push_opt(&mut out, "Request Method: ", et.request_method);
push_opt(&mut out, "Reason For Failure: ", et.reason_for_failure);
push_opt(&mut out, "Logs:", Some(""));
match et.logs {
Some(logs) => {
for log in logs.iter() {
out.push_str(&format!("{}\n", log)[..]);
}
},
None => {}
}
push_opt(&mut out, "Error: ", et.error_string);
if out.is_empty() {
out.push_str("ERROR TRANSLATE: No result!");
} else {
out = String::from(out.trim_right_matches("\n"));
}
Ok(out)
})
}
fn parse_geo(resp: http::Response) -> ::EdgeGridResult {
parse_response(resp, |b| {
let gr: IPGeoLocationResponse = try!(serde_json::from_str(&b));
debug!("{:?}", gr);
let mut out = String::new();
push_opt(&mut out, "Client IP: ", gr.geo.client_ip);
push_opt(&mut out, "Country Code: ", gr.geo.country_code);
push_opt(&mut out, "Region Code: ", gr.geo.region_code);
push_opt(&mut out, "City: ", gr.geo.city);
push_opt(&mut out, "DMA: ", gr.geo.dma);
push_opt(&mut out, "MSA: ", gr.geo.msa);
push_opt(&mut out, "PMSA: ", gr.geo.pmsa);
push_opt(&mut out, "Area Code: ", gr.geo.area_code);
push_opt(&mut out, "Latitude: ", gr.geo.latitude);
push_opt(&mut out, "Longitude: ", gr.geo.longitude);
push_opt(&mut out, "County: ", gr.geo.county);
push_opt(&mut out, "Continent: ", gr.geo.continent);
push_opt(&mut out, "FIPS: ", gr.geo.fips);
push_opt(&mut out, "Time Zone: ", gr.geo.time_zone);
push_opt(&mut out, "Network: ", gr.geo.network);
push_opt(&mut out, "Network Type: ", gr.geo.network_type);
push_opt(&mut out, "Zip Code: ", gr.geo.zip_code);
push_opt(&mut out, "Throughput: ", gr.geo.throughput);
push_opt(&mut out, "As Num: ", gr.geo.as_num);
push_opt(&mut out, "Error: ", gr.geo.error_string);
if out.is_empty() {
out.push_str("GEO: No result!");
} else {
out = String::from(out.trim_right_matches("\n"));
}
Ok(out)
})
}
fn parse_locations(resp: http::Response) -> ::EdgeGridResult {
parse_response(resp, |b| {
let lr: LocationsResponse = try!(serde_json::from_str(&b));
debug!("{:?}", lr);
let mut out = String::new();
match lr.locations {
Some(l) => {
for loc in l.iter() {
out.push_str(loc);
out.push_str("\n");
}
out = String::from(out.trim_right_matches("\n"));
},
None => { out.push_str("No locations found!"); }
}
Ok(out)
})
}
fn parse_mtr(resp: http::Response) -> ::EdgeGridResult {
parse_response(resp, |b| {
let mr: MtrResponse = try!(serde_json::from_str(&b));
debug!("{:?}", mr);
let mut out = String::new();
push_opt(&mut out, "Source: ", mr.mtr.source);
push_opt(&mut out, "Destination: ", mr.mtr.destination);
push_opt(&mut out, "Host: ", mr.mtr.host);
push_opt(&mut out, "Packet Loss: ", mr.mtr.packet_loss);
push_opt(&mut out, "Avg Latency: ", mr.mtr.avg_latency);
push_opt(&mut out, "Analysis: ", mr.mtr.analysis);
push_opt(&mut out, "Hops:", Some(""));
match mr.mtr.hops {
Some(hops) => {
for hop in hops.iter() {
out.push_str(&format!("{}\n", hop)[..]);
}
},
None => {}
}
push_opt(&mut out, "Error: ", mr.mtr.error_string);
if out.is_empty() {
out.push_str("MTR: No result!");
} else {
out = String::from(out.trim_right_matches("\n"));
}
Ok(out)
})
}
fn parse_translate(resp: http::Response) -> ::EdgeGridResult {
parse_response(resp, |b| {
let ar: ArlResponse = try!(serde_json::from_str(&b));
debug!("{:?}", ar);
let mut out = String::new();
push_opt(&mut out, "Type Code: ", ar.arl.type_code);
push_opt(&mut out, "Origin Server: ", ar.arl.origin_server);
push_opt(&mut out, "CPCode: ", ar.arl.cp_code);
push_opt(&mut out, "Serial Number: ", ar.arl.serial_number);
push_opt(&mut out, "TTL: ", ar.arl.ttl);
push_opt(&mut out, "Pragma: ", ar.arl.pragma);
push_opt(&mut out, "Cache Control: ", ar.arl.cache_control);
push_opt(&mut out, "Error: ", ar.arl.error_string);
if out.is_empty() {
out.push_str("TRANSLATE: No result!");
} else {
out = String::from(out.trim_right_matches("\n"));
}
Ok(out)
})
}
fn parse_verify(resp: http::Response) -> ::EdgeGridResult {
parse_response(resp, |b| {
let vr: VerifyIPCDNResponse = try!(serde_json::from_str(&b));
debug!("{:?}", vr);
let mut out = String::new();
push_opt(&mut out, "Is CDN IP: ", vr.is_cdn_ip);
push_opt(&mut out, "IP: ", vr.ip);
push_opt(&mut out, "Error: ", vr.error_string);
if out.is_empty() {
out.push_str("VERIFY CDN IP: No result!");
} else {
out = String::from(out.trim_right_matches("\n"));
}
Ok(out)
})
}
pub fn parse(
resp: http::Response,
dt_type: DTResponseType
) -> ::EdgeGridResult {
match dt_type {
Dig => parse_dig(resp),
ErrorTranslate => parse_error_translate(resp),
Geo => parse_geo(resp),
Locations => parse_locations(resp),
Mtr => parse_mtr(resp),
Translate => parse_translate(resp),
Verify => parse_verify(resp),
}
}