1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
use chrono::{DateTime, Timelike, Utc};
use chrono::serde::ts_seconds;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct Event {

    /// The event category. Must not be empty. (eg. Videos, Music, Games...)
    pub category: String,

    /// The event action. Must not be empty. (eg. Play, Pause, Duration, Add Playlist, Downloaded, Clicked...)
    pub action: String,

    /// The event name. OPTIONAL.
    pub name: Option<String>,

    /// The event value. OPTIONAL.
    pub value: Option<f64>,

    /// The campaign ID this data point is for.
    pub campaign_id: String,

    /// Number of times this data point has arisen between `first` and `last`. OPTIONAL, defaults to 1.
    pub times: u32,

    /// The first time this data point has arisen. OPTIONAL, defaults to now.
    #[serde(rename = "period_start", with = "ts_seconds")]
    pub first: DateTime<Utc>,

    /// The last time this data point has arisen. OPTIONAL, defaults to now.
    #[serde(rename = "period_end", with = "ts_seconds")]
    pub last: DateTime<Utc>,
}

impl Event {

    /// Returns a visit with the given scene_path and campaign_id and sane defaults for the other
    /// properties.
    ///
    /// # Arguments
    /// * category: The event category. Must not be empty. (eg. Videos, Music, Games...)
    /// * action: The event action. Must not be empty. (eg. Play, Pause, Duration, Add Playlist, Downloaded, Clicked...)
    /// * name: The event name. OPTIONAL.
    /// * value: The event value. OPTIONAL.
    /// * campaign_id: The campaign ID this data point is for.
    ///
    /// # Defaults:
    /// * times: 1.
    /// * first: now.
    /// * last: now.
    pub fn new(category: String, action: String, campaign_id: String) -> Event {
        Event {
            category, action, campaign_id,
            name: None,
            value: None,
            times: 1,
            first: Utc::now().with_nanosecond(0).unwrap(),
            last: Utc::now().with_nanosecond(0).unwrap() }
    }
}

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

    #[test]
    fn new_event() {
        let event = Event {
            category: "foo".to_string(),
            action: "bar".to_string(),
            name: Option::from("baz".to_string()),
            value: Option::from(1000.5),
            campaign_id: "test".to_string(),
            times: 100,
            first: Utc.timestamp(0, 0),
            last: Utc.timestamp(0, 0) };

        let json = serde_json::to_string(&event).unwrap();

        assert_eq!(json,
                   "{\"category\":\"foo\",\"action\":\"bar\",\"name\":\"baz\",\"value\":1000.5,\"campaign_id\":\"test\",\"times\":100,\"period_start\":0,\"period_end\":0}");

        let event2: Event = serde_json::from_str(json.as_str()).unwrap();

        assert_eq!(event2, event);
    }

    #[test]
    fn new_event_with_defaults() {
        let event = Event::new(
            "foo".to_string(),
            "bar".to_string(),
            "test".to_string());

        let json = serde_json::to_string(&event).unwrap();
        let now = Utc::now().timestamp();

        assert_eq!(json,
                   format!("{{\"category\":\"foo\",\"action\":\"bar\",\"name\":null,\"value\":null,\"campaign_id\":\"test\",\"times\":1,\"period_start\":{now},\"period_end\":{now}}}", now=now));

        let event2: Event = serde_json::from_str(json.as_str()).unwrap();

        assert_eq!(event2, event);
    }
}