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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! Interpreting room status results.

use crate::{
    data::{self, RoomName, RoomState},
    decoders::optional_timespec_seconds,
    error::{ApiError, Result},
    EndpointResult,
};

/// Room overview raw result.
#[derive(serde_derive::Deserialize, Clone, Hash, Debug)]
pub(crate) struct Response {
    ok: i32,
    room: Option<InnerRoom>,
}

#[derive(serde_derive::Deserialize, Clone, Hash, Debug)]
struct InnerRoom {
    /// The room's name
    _id: String,
    /// The "status" string, usually "normal"? Unknown what else it could be.
    status: String,
    /// The end time for the novice area this room is or was last in.
    #[serde(with = "optional_timespec_seconds")]
    #[serde(default)]
    novice: Option<time::Timespec>,
    /// The time this room will open or did open into the novice area as a second tier novice room.
    #[serde(rename = "openTime")]
    #[serde(with = "optional_timespec_seconds")]
    #[serde(default)]
    open_time: Option<time::Timespec>,
}

/// Struct describing the status of a room
#[derive(Serialize, Deserialize, Clone, Hash, Debug)]
pub struct RoomStatus {
    /// The name of the room, or None if the room does not exist.
    pub room_name: Option<RoomName>,
    /// The state of the room, determined by comparing the API response timestamps with the current UTC time, as
    /// retrieved from the system.
    pub state: RoomState,
    /// Phantom data in order to allow adding any additional fields in the future.
    #[serde(skip)]
    _non_exhaustive: (),
}

impl EndpointResult for RoomStatus {
    type RequestResult = Response;
    type ErrorResult = data::ApiError;

    fn from_raw(raw: Response) -> Result<RoomStatus> {
        let Response { ok, room } = raw;

        if ok != 1 {
            return Err(ApiError::NotOk(ok).into());
        }

        let InnerRoom {
            _id: room_name,
            status,
            novice,
            open_time,
        } = match room {
            Some(v) => v,
            None => {
                return Ok(RoomStatus {
                    room_name: None,
                    state: RoomState::non_existant(),
                    _non_exhaustive: (),
                });
            }
        };

        if status != "normal" {
            return Err(ApiError::MalformedResponse(format!(
                "expected room status to be \"normal\", \
                 found \"{}\".",
                &status
            ))
            .into());
        }

        let state = RoomState::from_data(time::get_time(), novice, open_time)?;

        Ok(RoomStatus {
            room_name: Some(RoomName::new(&room_name)?),
            state: state,
            _non_exhaustive: (),
        })
    }
}

#[cfg(test)]
mod tests {
    use super::RoomStatus;
    use crate::EndpointResult;
    use serde_json;

    fn test_parse(json: serde_json::Value) {
        let response = serde_json::from_value(json).unwrap();

        let _ = RoomStatus::from_raw(response).unwrap();
    }

    #[test]
    fn parse_sample_novice_room() {
        test_parse(json! ({
            "ok": 1,
            "room": {
                "_id": "W6S83",
                "status": "normal",
                "novice": 1488394267175i64
            }
        }));
    }

    #[test]
    fn parse_sample_highway_room() {
        test_parse(json! ({
            "ok": 1,
            "room": {
                "_id": "E0N0",
                "status": "normal"
            }
        }));
    }

    #[test]
    fn parse_sample_center_novice_room() {
        test_parse(json! ({
            "ok": 1,
            "room": {
                "_id": "E15N51",
                "status": "normal",
                "openTime": "1474674699273",
                "novice": 1475538699273i64
            }
        }));
    }
}