wamp_core/messages/
goodbye.rs

1use super::{helpers, MessageDirection, WampMessage};
2use crate::roles::Roles;
3use serde::{
4    de::{self, Visitor},
5    Deserialize, Serialize,
6};
7use serde_json::Value;
8use std::marker::PhantomData;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11/// # Goodbye - [wamp-proto](https://wamp-proto.org/wamp_latest_ietf.html#name-goodbye-2)
12/// represenets an goodbye message in wamp protocol.
13/// ## Examples
14/// ```
15/// use wamp_core::messages::Goodbye;
16/// use wamp_core::goodbye;
17/// use serde_json::json;
18/// # let mut goodbye_message2 = goodbye!("wamp.close.system_shutdown");
19///
20/// let goodbye_message = Goodbye {
21///     reason: "wamp.close.system_shutdown".to_string(),
22///     details: json!({})
23/// };
24///
25/// # assert_eq!(goodbye_message, goodbye_message2);
26/// ```
27///
28/// ### Serializer
29/// Implements serde Serialize trait for Goodbye
30/// ```
31/// use wamp_core::messages::Goodbye;
32/// use serde_json::{json, to_string};
33///
34/// // Create an goodbye message
35/// let goodbye = Goodbye {
36///     details: json!({"message": "The host is shutting down now."}),
37///     reason: "wamp.close.system_shutdown".to_string()
38/// };
39///
40/// // Establish raw json data string
41/// let data = r#"[6,{"message":"The host is shutting down now."},"wamp.close.system_shutdown"]"#;
42///
43/// // Here we convert it from an `Goodbye` frame, to a string representation.
44/// let goodbye = to_string(&goodbye).unwrap();
45///
46/// // Confirm that our Goodbye frame strings are equal to eachother
47/// assert_eq!(goodbye, data);
48/// ```
49/// ### Deserializer
50/// Implements serde Deserialize trait for Goodbye
51/// ```
52/// use wamp_core::messages::Goodbye;
53/// use serde_json::from_str;
54///
55/// // Here is our raw json data string
56/// let data = r#"[6,{"message": "The host is shutting down now."},"wamp.close.system_shutdown"]"#;
57///
58/// // Here we convert it to an `goodbye` frame
59/// let goodbye = from_str::<Goodbye>(data).unwrap();
60///
61/// // Confirm that our error type deserialized
62/// assert_eq!(goodbye.reason, "wamp.close.system_shutdown");
63/// ```
64pub struct Goodbye {
65    pub details: Value,
66    pub reason: String,
67}
68
69#[macro_export]
70/// # Goodbye Macro - [wamp-proto](https://wamp-proto.org/wamp_latest_ietf.html#name-goodbye-2)
71/// Macro that allows for default empty implementation of details object on Goodbye.
72/// ## Examples
73/// ```
74/// use wamp_core::messages::Goodbye;
75/// use wamp_core::goodbye;
76/// use serde_json::json;
77///
78/// // Construct with default empty details object
79/// let mut goodbye_message = goodbye!("wamp.close.system_shutdown");
80/// assert_eq!(goodbye_message.details, json!({}));
81///
82/// // Construct with custom details
83/// let goodbye_message2 = goodbye!("wamp.close.system_shutdown", json!({
84///     "message": "The host is shutting down now."
85/// }));
86///
87/// assert_ne!(goodbye_message, goodbye_message2);
88/// goodbye_message.details = json!({ "message": "The host is shutting down now." });
89/// assert_eq!(goodbye_message, goodbye_message2);
90///
91/// // These macro invocations are the same as the following:
92/// let goodbye_message3 = Goodbye {
93///     reason: "wamp.close.system_shutdown".to_string(),
94///     details: json!({
95///         "message": "The host is shutting down now."
96///     })
97/// };
98///
99/// assert_eq!(goodbye_message, goodbye_message3);
100/// assert_eq!(goodbye_message2, goodbye_message3);
101/// ```
102macro_rules! goodbye {
103    ($reason:expr) => {
104        goodbye! {$reason, serde_json::json!({})}
105    };
106
107    ($reason:expr, $details:expr) => {
108        Goodbye {
109            details: $details,
110            reason: $reason.to_string(),
111        }
112    };
113}
114
115impl WampMessage for Goodbye {
116    const ID: u64 = 6;
117
118    fn direction(role: Roles) -> &'static MessageDirection {
119        match role {
120            Roles::Callee => &MessageDirection {
121                receives: &true,
122                sends: &true,
123            },
124            Roles::Caller => &MessageDirection {
125                receives: &true,
126                sends: &true,
127            },
128            Roles::Publisher => &MessageDirection {
129                receives: &true,
130                sends: &true,
131            },
132            Roles::Subscriber => &MessageDirection {
133                receives: &true,
134                sends: &false,
135            },
136            Roles::Dealer => &MessageDirection {
137                receives: &true,
138                sends: &true,
139            },
140            Roles::Broker => &MessageDirection {
141                receives: &true,
142                sends: &true,
143            },
144        }
145    }
146}
147
148impl Serialize for Goodbye {
149    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
150    where
151        S: serde::Serializer,
152    {
153        let details =
154            helpers::ser_value_is_object::<S, _>(&self.details, "Details must be object like.")?;
155        (Self::ID, &details, &self.reason).serialize(serializer)
156    }
157}
158
159impl<'de> Deserialize<'de> for Goodbye {
160    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
161    where
162        D: serde::Deserializer<'de>,
163    {
164        struct GoodbyeVisitor(PhantomData<u8>, PhantomData<String>, PhantomData<Value>);
165
166        impl<'vi> Visitor<'vi> for GoodbyeVisitor {
167            type Value = Goodbye;
168
169            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
170                formatter.write_str("WAMP Goodbye frame, expressed as a sequence.")
171            }
172
173            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
174            where
175                A: de::SeqAccess<'vi>,
176            {
177                let message_id: u64 =
178                    helpers::deser_seq_element(&mut seq, "Message ID must be type u8.")?;
179                helpers::validate_id::<Goodbye, A, _>(&message_id, "Goodbye")?;
180                let details: Value =
181                    helpers::deser_seq_element(&mut seq, "Details must be a JSON value.")?;
182                let reason: String =
183                    helpers::deser_seq_element(&mut seq, "Reason must be a String.")?;
184                helpers::deser_value_is_object::<A, _>(&details, "Details must be object like.")?;
185                Ok(Goodbye { reason, details })
186            }
187        }
188
189        deserializer.deserialize_struct(
190            "Goodbye",
191            &["reason", "details"],
192            GoodbyeVisitor(PhantomData, PhantomData, PhantomData),
193        )
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use serde_json::{from_str, to_string};
200
201    use super::Goodbye;
202
203    #[test]
204    fn test() {
205        let d1 = r#"[6,{"message":"The host is shutting down now."},"wamp.close.system_shutdown"]"#;
206        let g1 = Goodbye {
207            details: serde_json::json!({"message":"The host is shutting down now."}),
208            reason: "wamp.close.system_shutdown".to_string(),
209        };
210        let d2 = to_string(&g1).unwrap();
211        let g2: Goodbye = from_str(d1).unwrap();
212        assert_eq!(d1, d2);
213        assert_eq!(g1, g2);
214    }
215}