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}