libedgegrid 0.1.0

This library implements an Authentication handler for the Akamai OPEN EdgeGrid Authentication scheme in Rust
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),
    }
}