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