wamp_core/messages/
subscribe.rs

1use super::{MessageDirection, WampMessage};
2use crate::{messages::helpers, roles::Roles};
3use serde::{de::Visitor, Deserialize, Serialize};
4use serde_json::Value;
5use std::marker::PhantomData;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8/// # Subscribe - [wamp-proto](https://wamp-proto.org/wamp_latest_ietf.html#name-subscribe-2)
9/// Represents an unsubscribe frame in the WAMP protocol.
10/// ## Examples
11/// ```
12/// use wamp_core::messages::Subscribe;
13/// use wamp_core::subscribe;
14/// use serde_json::json;
15///
16/// # let mut subscribed1 = subscribe!("topic");
17///
18/// let subscribed = Subscribe {
19///     request_id: 1,
20///     topic: "topic".to_string(),
21///     options: json!({})
22/// };
23/// # subscribed1.options = json!({});
24/// # assert_eq!(subscribed, subscribed1);
25/// ```
26/// ### Serializer
27/// Implements serde Serialize trait for Subscribe
28/// ```
29/// use wamp_core::messages::Subscribe;
30/// use serde_json::{json, to_string};
31/// use wamp_core::subscribe;
32///
33/// let data = r#"[32,1,{},"com.myapp.mytopic1"]"#;
34///
35/// let subscribe1 = subscribe!("com.myapp.mytopic1");
36///
37/// let subscribe = Subscribe {
38///     request_id: 1,
39///     topic: "com.myapp.mytopic1".to_string(),
40///     options: json!({})
41/// };
42///
43/// let data2 = to_string(&subscribe1).unwrap();
44/// let data3 = to_string(&subscribe).unwrap();
45///
46/// assert_eq!(data, data2);
47/// assert_eq!(data2, data3);
48/// ```
49/// ### Deserializer
50/// Implements serde Deserialize trait for Subscribe
51/// ```
52/// use wamp_core::messages::Subscribe;
53/// use serde_json::from_str;
54/// use wamp_core::subscribe;
55///
56/// let data = r#"[32,1,{},"com.myapp.mytopic1"]"#;
57///
58/// let subscribe = from_str::<Subscribe>(data).unwrap();
59///
60/// let subscribe2 = subscribe!("com.myapp.mytopic1");
61///
62/// assert_eq!(subscribe, subscribe2);
63/// ```
64pub struct Subscribe {
65    pub request_id: u64,
66    pub options: Value,
67    pub topic: String,
68}
69
70#[macro_export]
71/// # Subscribe Macro - [wamp-proto](https://wamp-proto.org/wamp_latest_ietf.html#name-subscribe-2)
72/// Macro that allows for default implementations of subscribe with empty or custom options and auto incremented request id.
73/// ## Examples
74/// ```
75/// use wamp_core::messages::{self, Subscribe};
76/// use wamp_core::subscribe;
77/// use serde_json::json;
78///
79/// let topic = "topic";
80///
81/// // Construct with default empty options object
82/// let subscribe = subscribe!(topic);
83///
84/// let subscribe2 = Subscribe {
85///     request_id: 1,
86///     options: json!({}),
87///     topic: topic.to_string()
88/// };
89///
90/// assert_eq!(subscribe, subscribe2);
91/// ```
92macro_rules! subscribe {
93    ($topic:expr) => {
94        subscribe! {$topic, serde_json::json!({})}
95    };
96    ($topic:expr, $options:expr) => {
97        Subscribe {
98            topic: $topic.to_string(),
99            options: $options,
100            request_id: $crate::factories::increment(),
101        }
102    };
103}
104
105impl WampMessage for Subscribe {
106    const ID: u64 = 32;
107
108    fn direction(role: Roles) -> &'static MessageDirection {
109        match role {
110            Roles::Callee => &MessageDirection {
111                receives: &false,
112                sends: &false,
113            },
114            Roles::Caller => &MessageDirection {
115                receives: &false,
116                sends: &false,
117            },
118            Roles::Publisher => &MessageDirection {
119                receives: &false,
120                sends: &false,
121            },
122            Roles::Subscriber => &MessageDirection {
123                receives: &false,
124                sends: &true,
125            },
126            Roles::Dealer => &MessageDirection {
127                receives: &false,
128                sends: &false,
129            },
130            Roles::Broker => &MessageDirection {
131                receives: &true,
132                sends: &false,
133            },
134        }
135    }
136}
137
138impl Serialize for Subscribe {
139    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
140    where
141        S: serde::Serializer,
142    {
143        (Self::ID, &self.request_id, &self.options, &self.topic).serialize(serializer)
144    }
145}
146
147impl<'de> Deserialize<'de> for Subscribe {
148    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
149    where
150        D: serde::Deserializer<'de>,
151    {
152        struct SubscribeVisitor(
153            PhantomData<u64>,
154            PhantomData<u64>,
155            PhantomData<Value>,
156            PhantomData<String>,
157        );
158
159        impl<'vi> Visitor<'vi> for SubscribeVisitor {
160            type Value = Subscribe;
161
162            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
163                formatter.write_str("A sequence of Subscribe components.")
164            }
165
166            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
167            where
168                A: serde::de::SeqAccess<'vi>,
169            {
170                let message_id: u64 = helpers::deser_seq_element(
171                    &mut seq,
172                    "Message id must be present and type u64.",
173                )?;
174                helpers::validate_id::<Subscribe, A, _>(&message_id, "Subscribe")?;
175                let request_id: u64 = helpers::deser_seq_element(
176                    &mut seq,
177                    "Request ID must be present and type u64",
178                )?;
179                let options: Value = helpers::deser_seq_element(
180                    &mut seq,
181                    "options must be present and object like",
182                )?;
183                helpers::deser_value_is_object::<A, _>(&options, "options must be object like.")?;
184                let topic: String = helpers::deser_seq_element(
185                    &mut seq,
186                    "topic URI must be present and type String",
187                )?;
188                helpers::deser_value_is_object::<A, _>(&options, "options must be object like.")?;
189                Ok(Subscribe {
190                    request_id,
191                    options,
192                    topic,
193                })
194            }
195        }
196
197        deserializer.deserialize_struct(
198            "Subscribe",
199            &["request_id", "options", "topic"],
200            SubscribeVisitor(PhantomData, PhantomData, PhantomData, PhantomData),
201        )
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use serde_json::{from_str, json, to_string};
208
209    use super::Subscribe;
210
211    #[test]
212    fn subscribe_test() {
213        let d1 = r#"[32,713845233,{},"com.myapp.mytopic1"]"#;
214        let r1 = Subscribe {
215            request_id: 713845233,
216            options: json!({}),
217            topic: "com.myapp.mytopic1".to_string(),
218        };
219        assert_eq!(d1, to_string(&r1).unwrap());
220        assert_eq!(r1, from_str::<Subscribe>(d1).unwrap())
221    }
222}