siderite/
protocol.rs

1//! This module contains the `serde` datastructures for DDP
2
3use serde::{Serialize, Deserialize};
4use serde_json::{self, Value};
5
6/// A date represented by the JSON object `{ "$date": ts }`, with `ts` in millisecs since the epoch.
7#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq, Eq)]
8pub struct Timestamp {
9    #[serde(rename="$date")]
10    millis: Option<u64>,
11}
12
13/// DDP messages from client to server
14#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(tag = "msg")]
16#[serde(rename_all = "camelCase")]
17pub enum ClientMessage {
18    /// Connection request. This must be the first message sent, and is used for version negotiation.
19    Connect {
20        version: String,
21        support: Vec<String>,
22        #[serde(skip_serializing_if="Option::is_none")]
23        session: Option<String>,
24    },
25
26    Ping { 
27        #[serde(skip_serializing_if="Option::is_none")]
28        id: Option<String>
29    },
30    Pong { 
31        #[serde(skip_serializing_if="Option::is_none")]
32        id: Option<String>
33    },
34
35    /// Method calls
36    Method { 
37        id: String, 
38        method: String, 
39        params: Vec<Value>
40    },
41
42
43    Sub { 
44        id: String, name: String, params: Vec<Value> 
45    },
46    Unsub { 
47        id: String 
48    },
49
50}
51
52/// DDP messages from server to client
53#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
54#[serde(tag = "msg")]
55#[serde(rename_all = "camelCase")]
56pub enum ServerMessage {
57    Connected {
58        session: String,
59    },
60    Failed {
61        version: String,
62    },
63    Ping { 
64        #[serde(default, skip_serializing_if="Option::is_none")]
65        id: Option<String>
66    },
67    Pong { 
68        #[serde(default, skip_serializing_if="Option::is_none")]
69        id: Option<String>
70    },
71
72    /// The result of a method call
73    Result(MethodResponse),
74
75    /// Sent after unsubscribing, or to signal a subscription failure.
76    Nosub { 
77        id: String, 
78        #[serde(default, skip_serializing_if="Option::is_none")]
79        error: Option<Value>
80    },
81
82    /// Signals progress on one or several method calls.
83    Updated { 
84        methods: Vec<String> 
85    },
86    
87    Added {
88        collection: String,
89        id: String,
90        fields: Option<Value>,
91    },
92    Changed {
93        collection: String,
94        id: String,
95        #[serde(default, skip_serializing_if="Option::is_none")]
96        fields: Option<Value>,
97        #[serde(default, skip_serializing_if="Option::is_none")]
98        cleared: Option<Vec<String>>,
99    },
100    Removed {
101        collection: String,
102        id: String,
103    },
104    Ready {
105        subs: Vec<String>,
106    },
107    AddedBefore {
108        collection: String,
109        id: String,
110        #[serde(default, skip_serializing_if="Option::is_none")]
111        fields: Option<Value>,
112        before: Option<String>,
113    },
114    MovedBefore {
115        before: Option<String>,
116    }
117
118}
119
120impl ServerMessage {
121
122    pub fn pretty(&self) -> String {
123        serde_json::to_value(&self)
124            .and_then(|v| serde_json::to_string_pretty(&v))
125            .unwrap_or_else(|_| "<<serialization error>>".to_string())
126    }
127
128}
129
130#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
131pub struct MethodResponse {
132    pub id: String,
133    #[serde(default, skip_serializing_if="Option::is_none")]
134    pub result: Option<Value>,
135    #[serde(default, skip_serializing_if="Option::is_none")]
136    pub error: Option<Value>,
137}
138
139
140
141#[cfg(test)]
142mod tests {
143
144    use super::*;
145
146    use serde::de::DeserializeOwned;
147
148    fn check_message<M>(msg: &M, string: &str)
149        where M: Serialize + DeserializeOwned + PartialEq + std::fmt::Debug
150    {
151        let serialized = serde_json::to_string(msg).unwrap();
152        assert_eq!(serialized, string);
153        let deserialized: M = serde_json::from_str(&string).unwrap();
154        assert_eq!(msg, &deserialized);
155        
156    }
157
158    #[test]
159    fn test_method_result() {
160        check_message(&ServerMessage::Result( 
161            MethodResponse {
162                id: "123".to_string(),
163                result: Some(Value::String("burp".to_string())),
164                error: None
165            }
166        ), r#"{"msg":"result","id":"123","result":"burp"}"#);
167    }
168
169    #[test]
170    fn test_method_error() {
171        check_message(&ServerMessage::Result(
172            MethodResponse {
173                id: "456:kahcubwdasd".to_string(),
174                error: Some(Value::Bool(true)),
175                result: None,
176            }
177
178        ), r#"{"msg":"result","id":"456:kahcubwdasd","error":true}"#);
179    }
180
181    #[test]
182    fn test_pingpong() {
183
184        check_message(&ServerMessage::Ping { id: None }, r#"{"msg":"ping"}"#);
185        check_message(&ServerMessage::Ping { id: Some("pingpong".to_string()) }, r#"{"msg":"ping","id":"pingpong"}"#);
186    }
187
188    #[test]
189    fn test_timestamp() {
190        check_message(&Timestamp{ millis: Some(129348109238) }, r#"{"$date":129348109238}"#);
191    }
192}