warp10 0.11.1

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

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
}

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,Clone,PartialEq)]
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,Clone,PartialEq)]
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_else(|| "".to_string())
        )
    }
}

#[derive(Debug,Clone,PartialEq)]
pub struct Label {
    name:  String,
    value: String,
}

impl Label {
    pub fn new(name: &str, value: &str) -> Label {
        Label {
            name:  name.to_string(),
            value: value.to_string(),
        }
    }
}

impl Warp10Serializable for Label {
    fn warp10_serialize(&self) -> String {
        format!("{}={}", url_encode(&self.name), url_encode(&self.value))
    }
}

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

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

impl Warp10Serializable for Data {
    fn warp10_serialize(&self) -> String {
        let date_ms = self.date.sec * 1000000 + (self.date.nsec as Long) / 1000;
        let geo     = self.geo
            .as_ref()
            .map(Warp10Serializable::warp10_serialize)
            .unwrap_or_else(|| "/".to_string());
        let labels  = self.labels
            .iter()
            .map(|l| l.warp10_serialize())
            .fold(String::new(), |acc, cur| {
                if acc.is_empty() {
                    cur
                } else {
                    (acc + ",") + &cur
                }
            });
        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_label() {
        assert_eq!(
            Label::new("name 1", "凄い value 2").warp10_serialize(),
            "name 1=%E5%87%84%E3%81%84 value 2"
        );
    }

    #[test]
    fn serialize_data() {
        assert_eq!(
            Data::new(
                Timespec::new(25, 123456789),
                None,
                "original name".to_string(),
                vec![
                    Label::new("label1",  "value1"),
                    Label::new("label 2", "value 2"),
                ],
                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![
                    Label::new("label1",  "value1"),
                    Label::new("label 2", "value 2"),
                ],
                Value::String("foobar".to_string())
            ).warp10_serialize(),
            "25123456/42.66:32.85/10 original name{label1=value1,label 2=value 2} 'foobar'"
        );
    }
}