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
141
142
143
144
145
use error::*;
use serde_json;

const CRLF: &'static str = "\r\n";
const STRING_PREFIX: &'static str = "+";
const INT_PREFIX: &'static str = ":";
const JSON_PERFIX: &'static str = "?";

macro_rules! serialize_template { () => ("{prefix}{value}{crlf}") }

#[derive(Debug, Clone, PartialEq)]
pub enum Data {
    String(String),
    Int(i64),
    JSON(serde_json::Value),
}

impl Data {
    pub fn into_string(self) -> String {
        match self {
            Data::String(string) => Self::serialize_string(string),
            Data::Int(int) => Self::serialize_int(int),
            Data::JSON(json) => Self::serialize_json(json),
        }
    }

    pub fn try_from(string: String) -> Result<Data> {
        if string.len() < 2 || !string.ends_with(CRLF) {
            return Err(Error::new(ErrorKind::InvalidSerializedString));
        }

        if string.starts_with(STRING_PREFIX) {
            Ok(Self::from_string(string)?)
        } else if string.starts_with(INT_PREFIX) {
            Ok(Self::from_int(string)?)
        } else if string.starts_with(JSON_PERFIX) {
            Ok(Self::from_json(string)?)
        } else {
            Err(Error::new(ErrorKind::InvalidSerializedString))
        }
    }

    fn from_string(s: String) -> Result<Data> {
        Ok(Data::String(String::from(&s[1..s.len() - 2])))
    }

    fn from_int(s: String) -> Result<Data> {
        Ok(Data::Int(s[1..s.len() - 2].parse::<i64>()?))
    }

    fn from_json(s: String) -> Result<Data> {
        Ok(Data::JSON(serde_json::from_str(&s[1..s.len() - 2])?))
    }

    fn serialize_string(s: String) -> String {
        format!(serialize_template!(),
                prefix = STRING_PREFIX,
                value = s,
                crlf = CRLF)
    }

    fn serialize_int(i: i64) -> String {
        format!(serialize_template!(),
                prefix = INT_PREFIX,
                value = i,
                crlf = CRLF)
    }

    fn serialize_json(json: serde_json::Value) -> String {
        format!(serialize_template!(),
                prefix = JSON_PERFIX,
                value = json.to_string(),
                crlf = CRLF)
    }
}

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

    #[test]
    fn test_from_string() {
        assert_eq!(Data::String(String::from("test_\r\nfrom_string")),
                   Data::from_string(String::from("+test_\r\nfrom_string\r\n")).unwrap());
    }

    #[test]
    fn test_from_int() {
        assert_eq!(Data::Int(22),
                   Data::from_int(String::from(":22\r\n")).unwrap());
    }

    #[test]
    fn test_from_invalid_int() {
        assert!(Data::from_int(String::from(":22invalid888\r\n")).is_err());
    }

    #[test]
    fn test_serialize_string() {
        assert_eq!("+test\r\n_serialize\r\n_string\r\n",
                   Data::serialize_string(String::from("test\r\n_serialize\r\n_string")));
    }

    #[test]
    fn test_serialize_int() {
        assert_eq!(":666\r\n", Data::serialize_int(666));
    }

    #[test]
    fn test_serialize_json() {
        assert_eq!("?{\"age\":18,\"name\":\"David\"}\r\n",
                   Data::serialize_json(json!({"name": "David","age": 18})));
    }

    #[test]
    fn test_serializble_into() {
        assert_eq!("+666\r\n", Data::String(String::from("666")).into_string());
        assert_eq!(":666\r\n", Data::Int(666).into_string());
    }

    #[test]
    fn test_try_from_invalid() {
        assert!(Data::try_from(String::from("")).is_err());
        assert!(Data::try_from(String::from("\r\n")).is_err());
        assert!(Data::try_from(String::from("11111")).is_err());
    }

    #[test]
    fn test_try_from_string() {
        assert_eq!(Data::String(String::from("666")),
                   Data::try_from(String::from("+666\r\n")).unwrap());
    }

    #[test]
    fn test_try_from_int() {
        assert_eq!(Data::Int(666),
                   Data::try_from(String::from(":666\r\n")).unwrap());
    }

    #[test]
    fn test_try_from_json() {
        assert_eq!(Data::JSON(json!({"name": "David","age": 18})),
                   Data::try_from(String::from("?{\"age\":18,\"name\":\"David\"}\r\n")).unwrap());
    }
}