warp10 0.1.0

Warp10 client for rust
Documentation
use itertools::Itertools;
use time::Timespec;
use url::percent_encoding;

pub trait Warp10Serializable {
    fn warp10_serialize(&self) -> String;
}

pub type Int     = i32;
pub type Long    = i64;
pub type Double  = f64;
pub type Boolean = bool;

#[derive(Debug)]
pub enum Value {
    Int(Int),
    Long(Long),
    Double(Double),
    Boolean(Boolean),
    String(String),
}

impl Warp10Serializable for Value {
    fn warp10_serialize(&self) -> String {
        match *self {
            Value::Int(i)        => i.to_string(),
            Value::Long(l)       => l.to_string(),
            Value::Double(d)     => d.to_string(),
            Value::Boolean(b)    => b.to_string(),
            Value::String(ref s) => format!("'{}'", s)
        }
    }
}

#[derive(Debug)]
pub struct GeoValue {
    lat:  Double,
    lon:  Double,
    elev: Option<Long>,
}

impl GeoValue {
    pub fn new(lat: Double, lon: Double, elev: Option<Long>) -> GeoValue {
        GeoValue {
            lat:  lat,
            lon:  lon,
            elev: elev,
        }
    }
}

impl Warp10Serializable for GeoValue {
    fn warp10_serialize(&self) -> String {
        format!("{}:{}/{}",
                self.lat,
                self.lon,
                self.elev.map(|e| e.to_string()).unwrap_or("".to_string()))
    }
}

#[derive(Debug)]
pub struct Data {
    date:   Timespec,
    geo:    Option<GeoValue>,
    name:   String,
    labels: Vec<(String, String)>,
    value:  Value,
}

impl Data {
    pub fn new(date:   Timespec,
               geo:    Option<GeoValue>,
               name:   String,
               labels: Vec<(String, String)>,
               value:  Value)
               -> Data {
        Data {
            date:   date,
            geo:    geo,
            name:   name,
            labels: labels,
            value:  value,
        }
    }
}

fn url_encode(input: &str) -> String {
    let mut s = String::new();
    s.extend(percent_encoding::utf8_percent_encode(input, percent_encoding::SIMPLE_ENCODE_SET));
    s
}

impl Warp10Serializable for Data {
    fn warp10_serialize(&self) -> String {
        let date_ms = self.date.sec * 1000000 + (self.date.nsec as Long) / 1000;
        let geo = match &self.geo {
            &None        => "/".to_string(),
            &Some(ref g) => g.warp10_serialize()
        };
        let labels = self.labels.iter().map(|&(ref k, ref v)| format!("{}={}", url_encode(k), url_encode(v))).join(",");
        format!("{}/{} {}{{{}}} {}",
                date_ms,
                geo,
                url_encode(&self.name),
                labels,
                self.value.warp10_serialize())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use time::Timespec;

    #[test]
    fn serialize_int() {
        assert_eq!(Value::Int(42).warp10_serialize(), "42");
    }

    #[test]
    fn serialize_long() {
        assert_eq!(Value::Long(42).warp10_serialize(), "42");
    }

    #[test]
    fn serialize_double() {
        assert_eq!(Value::Double(42.66).warp10_serialize(), "42.66");
    }

    #[test]
    fn serialize_boolean() {
        assert_eq!(Value::Boolean(true).warp10_serialize(), "true");
        assert_eq!(Value::Boolean(false).warp10_serialize(), "false");
    }

    #[test]
    fn serialize_string() {
        assert_eq!(Value::String("foobar".to_string()).warp10_serialize(),
                   "'foobar'");
    }

    #[test]
    fn serialize_geo() {
        assert_eq!(GeoValue::new(42.66, 32.85, None).warp10_serialize(),
                   "42.66:32.85/");
        assert_eq!(GeoValue::new(42.66, 32.85, Some(10)).warp10_serialize(),
                   "42.66:32.85/10");
    }

    #[test]
    fn serialize_data() {
        assert_eq!(Data::new(Timespec::new(25, 123456789),
                                   None,
                                   "original name".to_string(),
                                   vec![("label1".to_string(), "value1".to_string()),
                                        ("label 2".to_string(), "value 2".to_string())],
                                   Value::String("foobar".to_string()))
                       .warp10_serialize(),
                   "25123456// original name{label1=value1,label 2=value 2} 'foobar'");
        assert_eq!(Data::new(Timespec::new(25, 123456789),
                                   Some(GeoValue::new(42.66, 32.85, Some(10))),
                                   "original name".to_string(),
                                   vec![("label1".to_string(), "value1".to_string()),
                                        ("label 2".to_string(), "value 2".to_string())],
                                   Value::String("foobar".to_string()))
                       .warp10_serialize(),
                   "25123456/42.66:32.85/10 original name{label1=value1,label 2=value 2} 'foobar'");
    }
}