elastic-queries 0.1.0

Constuct and manipulate strongly typed Elasticsearch Queries using a source JSON files or builders.
use serde;
use serde::de::Visitor;
use serde::ser::{Serialize, Serializer, SerializeMap};
use std::fmt;
use super::super::Values;
use super::common::EsDateFormat;

//FIXME: Implement builder pattern for RangeFilter
#[derive(Clone, Debug, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq)]
pub struct RangeFilter {
    pub range: RangeField,
}

impl RangeFilter {
    pub fn new(field: &str, params: RangeParams) -> RangeFilter {
        RangeFilter {
            range: RangeField {
                field: field.to_string(),
                params: params,
            },
        }
    }
}


#[derive(Builder, Clone, Debug, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq)]
pub struct RangeParams {
    #[serde(skip_serializing_if = "Option::is_none")]
    #[builder(default = "None")]
    gte: Option<Values>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[builder(default = "None")]
    gt: Option<Values>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[builder(default = "None")]
    lte: Option<Values>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[builder(default = "None")]
    lt: Option<Values>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[builder(default = "None")]
    format: Option<EsDateFormat>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[builder(default = "None")]
    time_zone: Option<Values>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[builder(default = "None")]
    boost: Option<String>,
}

#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub struct RangeField {
    pub field: String,
    pub params: RangeParams,
}

impl Serialize for RangeField {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut map = serializer.serialize_map(Some(1))?;
        map.serialize_entry(&self.field, &self.params)?;
        map.end()
    }
}

impl<'de> serde::Deserialize<'de> for RangeField {
    fn deserialize<D>(deserializer: D) -> Result<RangeField, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        deserializer.deserialize_map(RangeFilterVisitor)
    }
}

pub(crate) struct RangeFilterVisitor;

impl<'de> Visitor<'de> for RangeFilterVisitor {
    type Value = RangeField;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a range filter structure")
    }

    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    where
        A: serde::de::MapAccess<'de>,
    {
        use serde::de::Error;
        let field: String = map.next_key()?.ok_or(A::Error::custom("expected field"))?;
        let value: RangeParams = map.next_value()?;

        Ok(RangeField {
            field: field,
            params: value,
        })
    }
}

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


    #[test]
    fn range_filter_new() {
        let p = RangeParamsBuilder::default()
            .gte(Some(0.into()))
            .lte(Some(1.into()))
            .build()
            .unwrap();

        let r = RangeFilter::new("foo", p);

        assert!(r.range.field == "foo");
        assert!(r.range.params.gte == Some(0.into()));
    }

    #[test]
    fn range_filter() {
        let o = r#"{"range":{"@timestamp":{"gte":1,"lte":2}}}"#;
        let s: RangeFilter = serde_json::from_str(o).unwrap();
        let j = serde_json::to_string(&s).unwrap();
        assert_eq!(o, j);

        let o = r#"{"range":{"@timestamp":{"gte":"now","lte":"now-2h"}}}"#;
        let s: RangeFilter = serde_json::from_str(o).unwrap();
        let j = serde_json::to_string(&s).unwrap();
        assert_eq!(o, j);
    }
}