1use crate::value::AMQPValue;
2
3use std::{
4 borrow,
5 collections::{BTreeMap, btree_map},
6 fmt, str,
7};
8
9use serde::{Deserialize, Serialize};
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
13pub enum AMQPType {
14 Boolean,
16 ShortShortInt,
18 ShortShortUInt,
20 ShortInt,
22 ShortUInt,
24 LongInt,
26 LongUInt,
28 LongLongInt,
30 LongLongUInt,
32 Float,
34 Double,
36 DecimalValue,
38 ShortString,
40 LongString,
42 FieldArray,
44 Timestamp,
46 FieldTable,
48 ByteArray, Void,
52}
53
54impl AMQPType {
55 pub fn from_id(id: char) -> Option<AMQPType> {
60 match id {
61 't' => Some(AMQPType::Boolean),
62 'b' => Some(AMQPType::ShortShortInt),
63 'B' => Some(AMQPType::ShortShortUInt),
64 's' | 'U' => Some(AMQPType::ShortInt),
66 'u' => Some(AMQPType::ShortUInt),
67 'I' => Some(AMQPType::LongInt),
68 'i' => Some(AMQPType::LongUInt),
69 'L' | 'l' => Some(AMQPType::LongLongInt),
71 'f' => Some(AMQPType::Float),
72 'd' => Some(AMQPType::Double),
73 'D' => Some(AMQPType::DecimalValue),
74 'S' => Some(AMQPType::LongString),
75 'A' => Some(AMQPType::FieldArray),
76 'T' => Some(AMQPType::Timestamp),
77 'F' => Some(AMQPType::FieldTable),
78 'x' => Some(AMQPType::ByteArray),
79 'V' => Some(AMQPType::Void),
80 _ => None,
81 }
82 }
83
84 pub fn get_id(self) -> char {
90 match self {
91 AMQPType::Boolean => 't',
92 AMQPType::ShortShortInt => 'b',
93 AMQPType::ShortShortUInt => 'B',
94 AMQPType::ShortInt => 's',
96 AMQPType::ShortUInt => 'u',
97 AMQPType::LongInt => 'I',
98 AMQPType::LongUInt => 'i',
99 AMQPType::LongLongInt | AMQPType::LongLongUInt => 'l',
101 AMQPType::Float => 'f',
102 AMQPType::Double => 'd',
103 AMQPType::DecimalValue => 'D',
104 AMQPType::ShortString => '_',
106 AMQPType::LongString => 'S',
107 AMQPType::FieldArray => 'A',
108 AMQPType::Timestamp => 'T',
109 AMQPType::FieldTable => 'F',
110 AMQPType::ByteArray => 'x',
111 AMQPType::Void => 'V',
112 }
113 }
114}
115
116impl fmt::Display for AMQPType {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 f.write_fmt(format_args!("{self:?}"))
119 }
120}
121
122pub type Boolean = bool;
124pub type ShortShortInt = i8;
126pub type ShortShortUInt = u8;
128pub type ShortInt = i16;
130pub type ShortUInt = u16;
132pub type LongInt = i32;
134pub type LongUInt = u32;
136pub type LongLongInt = i64;
138pub type LongLongUInt = u64;
140pub type Float = f32;
142pub type Double = f64;
144pub type Timestamp = LongLongUInt;
146pub type Void = ();
148
149#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
151pub struct ShortString(String);
152#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
154pub struct LongString(Vec<u8>);
155#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
157pub struct FieldArray(Vec<AMQPValue>);
158#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
160pub struct FieldTable(BTreeMap<ShortString, AMQPValue>);
161#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
163pub struct ByteArray(Vec<u8>);
164
165#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
167pub struct DecimalValue {
168 pub scale: ShortShortUInt,
170 pub value: LongUInt,
172}
173
174impl<'a> ShortString {
175 pub fn as_str(&'a self) -> &'a str {
177 self.0.as_str()
178 }
179}
180
181impl From<String> for ShortString {
182 fn from(s: String) -> Self {
183 Self(s)
184 }
185}
186
187impl From<&str> for ShortString {
188 fn from(s: &str) -> Self {
189 s.to_owned().into()
190 }
191}
192
193impl borrow::Borrow<str> for ShortString {
194 fn borrow(&self) -> &str {
195 self.0.borrow()
196 }
197}
198
199impl fmt::Display for ShortString {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 self.0.fmt(f)
202 }
203}
204
205impl From<ShortString> for String {
206 fn from(value: ShortString) -> Self {
207 value.0
208 }
209}
210
211impl<'a> LongString {
212 pub fn as_bytes(&'a self) -> &'a [u8] {
214 &self.0[..]
215 }
216}
217
218impl<B> From<B> for LongString
219where
220 B: Into<Vec<u8>>,
221{
222 fn from(bytes: B) -> Self {
223 Self(bytes.into())
224 }
225}
226
227impl fmt::Display for LongString {
228 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229 String::from_utf8_lossy(&self.0).fmt(f)
230 }
231}
232
233impl FieldArray {
234 pub fn as_slice(&self) -> &[AMQPValue] {
236 self.0.as_slice()
237 }
238
239 pub fn push(&mut self, v: AMQPValue) {
241 self.0.push(v);
242 }
243}
244
245impl From<Vec<AMQPValue>> for FieldArray {
246 fn from(v: Vec<AMQPValue>) -> Self {
247 Self(v)
248 }
249}
250
251impl FieldTable {
252 pub fn insert(&mut self, k: ShortString, v: AMQPValue) -> &mut Self {
254 self.0.insert(k, v);
255 self
256 }
257
258 pub fn contains_key(&self, k: &str) -> bool {
260 self.0.contains_key(k)
261 }
262
263 pub fn inner(&self) -> &BTreeMap<ShortString, AMQPValue> {
265 &self.0
266 }
267}
268
269impl<'a> IntoIterator for &'a FieldTable {
270 type Item = (&'a ShortString, &'a AMQPValue);
271 type IntoIter = btree_map::Iter<'a, ShortString, AMQPValue>;
272
273 fn into_iter(self) -> Self::IntoIter {
274 self.0.iter()
275 }
276}
277
278impl From<BTreeMap<ShortString, AMQPValue>> for FieldTable {
279 fn from(m: BTreeMap<ShortString, AMQPValue>) -> Self {
280 Self(m)
281 }
282}
283
284impl ByteArray {
285 pub fn as_slice(&self) -> &[u8] {
287 self.0.as_slice()
288 }
289
290 pub fn len(&self) -> usize {
292 self.0.len()
293 }
294
295 pub fn is_empty(&self) -> bool {
297 self.0.is_empty()
298 }
299}
300
301impl From<Vec<u8>> for ByteArray {
302 fn from(v: Vec<u8>) -> Self {
303 Self(v)
304 }
305}
306
307impl From<&[u8]> for ByteArray {
308 fn from(v: &[u8]) -> Self {
309 Self(v.to_vec())
310 }
311}
312
313#[cfg(test)]
314mod test {
315 use super::*;
316
317 #[test]
318 fn test_type_from_id() {
319 assert_eq!(AMQPType::from_id('T'), Some(AMQPType::Timestamp));
320 assert_eq!(AMQPType::from_id('S'), Some(AMQPType::LongString));
321 assert_eq!(AMQPType::from_id('s'), Some(AMQPType::ShortInt));
322 assert_eq!(AMQPType::from_id('U'), Some(AMQPType::ShortInt));
323 assert_eq!(AMQPType::from_id('l'), Some(AMQPType::LongLongInt));
324 assert_eq!(AMQPType::from_id('z'), None);
325 }
326
327 #[test]
328 fn test_type_get_id() {
329 assert_eq!(AMQPType::LongLongInt.get_id(), 'l');
330 assert_eq!(AMQPType::LongLongUInt.get_id(), 'l');
331 assert_eq!(AMQPType::ShortString.get_id(), '_');
332 }
333
334 #[test]
335 fn test_type_to_string() {
336 assert_eq!(AMQPType::Boolean.to_string(), "Boolean");
337 assert_eq!(AMQPType::Void.to_string(), "Void");
338 }
339
340 #[test]
341 fn long_string_ergonomics() {
342 let str_ref = "string ref";
343 let str_owned = "string owned".to_owned();
344 let vec = b"bytes".to_vec();
345 let array = b"bytes".to_owned();
346 let slice = &b"bytes"[..];
347
348 let from_str_ref: LongString = str_ref.into();
349 let from_str_owned: LongString = str_owned.clone().into();
350 let from_vec: LongString = vec.clone().into();
351 let from_array: LongString = array.into();
352 let from_slice: LongString = slice.into();
353
354 for (left, right) in [
355 (str_ref.as_bytes(), from_str_ref.as_bytes()),
356 (str_owned.as_bytes(), from_str_owned.as_bytes()),
357 (vec.as_ref(), from_vec.as_bytes()),
358 (array.as_ref(), from_array.as_bytes()),
359 (slice, from_slice.as_bytes()),
360 ] {
361 assert_eq!(left, right);
362 }
363 }
364}