strut_rabbitmq/repr/ingress/
header.rs

1use crate::util::Morph;
2use lapin::types::AMQPValue;
3use serde::de::{Error, Visitor};
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use std::fmt::Formatter;
6
7/// Represents the value assigned to a RabbitMQ header, specifically the binding
8/// header. This enumeration is used for deserializing
9/// [`Ingress`](crate::Ingress) definitions.
10///
11/// ## Important: integer size
12///
13/// It is not made abundantly clear, but when matching headers for the purposes
14/// of routing messages, RabbitMQ supports integer header values **only up to
15/// 32-bit size**. At the same time, RabbitMQ supports 64-bit integer header
16/// values just fine when simply attached to the message. Yet, during the routing
17/// process, the headers with 64-bit integer values are ignored.
18///
19/// Thus, `i32` and `u32` are chosen to underlie the integer variants of this
20/// enumeration.
21#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub enum Header {
23    /// Represents the boolean header value.
24    Boolean(bool),
25    /// Represents the signed integer header value.
26    Int(i32),
27    /// Represents the unsigned integer header value.
28    UInt(u32),
29    /// Represents the string header value.
30    String(String),
31}
32
33impl Serialize for Header {
34    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
35    where
36        S: Serializer,
37    {
38        match self {
39            Self::Boolean(b) => serializer.serialize_bool(*b),
40            Self::Int(i) => serializer.serialize_i32(*i),
41            Self::UInt(u) => serializer.serialize_u32(*u),
42            Self::String(s) => serializer.serialize_str(s),
43        }
44    }
45}
46
47impl<'de> Deserialize<'de> for Header {
48    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
49    where
50        D: Deserializer<'de>,
51    {
52        deserializer.deserialize_any(HeaderVisitor)
53    }
54}
55
56struct HeaderVisitor;
57
58impl<'de> Visitor<'de> for HeaderVisitor {
59    type Value = Header;
60
61    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
62        formatter.write_str("a RabbitMQ header value: a boolean, an integer, or a string")
63    }
64
65    fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
66    where
67        E: Error,
68    {
69        Ok(Header::Boolean(value))
70    }
71
72    fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
73    where
74        E: Error,
75    {
76        Ok(Header::Int(value.try_into().map_err(E::custom)?))
77    }
78
79    fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
80    where
81        E: Error,
82    {
83        Ok(Header::UInt(value.try_into().map_err(E::custom)?))
84    }
85
86    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
87    where
88        E: Error,
89    {
90        Ok(Header::String(value.to_string()))
91    }
92
93    fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
94    where
95        E: Error,
96    {
97        Ok(Header::String(value))
98    }
99}
100
101impl Header {
102    /// Reports whether this [`Header`] may be considered empty (which
103    /// means an empty string). Note that numerical zero values are **not**
104    /// empty, and neither is `false`.
105    pub fn is_empty(&self) -> bool {
106        match self {
107            Header::String(s) => s.is_empty(),
108            _ => false,
109        }
110    }
111}
112
113impl From<Header> for AMQPValue {
114    fn from(value: Header) -> Self {
115        match value {
116            Header::Boolean(b) => AMQPValue::morph(b),
117            Header::Int(i) => AMQPValue::morph(i),
118            Header::UInt(u) => AMQPValue::morph(u),
119            Header::String(s) => AMQPValue::morph(s),
120        }
121    }
122}
123
124impl From<bool> for Header {
125    fn from(value: bool) -> Self {
126        Self::Boolean(value)
127    }
128}
129
130impl From<i8> for Header {
131    fn from(value: i8) -> Self {
132        Self::Int(value as i32)
133    }
134}
135
136impl From<i16> for Header {
137    fn from(value: i16) -> Self {
138        Self::Int(value as i32)
139    }
140}
141
142impl From<i32> for Header {
143    fn from(value: i32) -> Self {
144        Self::Int(value)
145    }
146}
147
148impl From<u8> for Header {
149    fn from(value: u8) -> Self {
150        Self::UInt(value as u32)
151    }
152}
153
154impl From<u16> for Header {
155    fn from(value: u16) -> Self {
156        Self::UInt(value as u32)
157    }
158}
159
160impl From<u32> for Header {
161    fn from(value: u32) -> Self {
162        Self::UInt(value)
163    }
164}
165
166impl From<&str> for Header {
167    fn from(value: &str) -> Self {
168        Self::String(value.into())
169    }
170}
171
172impl From<String> for Header {
173    fn from(value: String) -> Self {
174        Self::String(value)
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181    use pretty_assertions::assert_eq;
182
183    #[test]
184    fn serialization() {
185        // Given
186        let input = vec![
187            Header::Boolean(true),
188            Header::Int(-1),
189            Header::UInt(0),
190            Header::UInt(1),
191            Header::String("hello".into()),
192        ];
193        let expected_result = "[true,-1,0,1,\"hello\"]";
194
195        // When
196        let actual_result = serde_json::to_string(&input).unwrap();
197
198        // Then
199        assert_eq!(expected_result, actual_result);
200    }
201
202    #[test]
203    fn deserialization() {
204        // Given
205        let input = "[true,-1,0,1,\"hello\"]";
206        let expected_result = vec![
207            AMQPValue::morph(true),
208            AMQPValue::LongInt(-1), // specify ambiguous types
209            AMQPValue::LongUInt(0),
210            AMQPValue::LongUInt(1),
211            AMQPValue::morph("hello"),
212        ];
213
214        // When
215        let actual_result = serde_json::from_str::<Vec<Header>>(input)
216            .unwrap()
217            .into_iter()
218            .map(AMQPValue::from)
219            .collect::<Vec<_>>();
220
221        // Then
222        assert_eq!(expected_result, actual_result);
223    }
224}