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 LongString {
212 pub fn as_bytes(&self) -> &[u8] {
214 &self.0[..]
215 }
216
217 pub fn len(&self) -> usize {
219 self.0.len()
220 }
221
222 pub fn is_empty(&self) -> bool {
224 self.0.is_empty()
225 }
226}
227
228impl<B> From<B> for LongString
229where
230 B: Into<Vec<u8>>,
231{
232 fn from(bytes: B) -> Self {
233 Self(bytes.into())
234 }
235}
236
237impl fmt::Display for LongString {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 String::from_utf8_lossy(&self.0).fmt(f)
240 }
241}
242
243impl FieldArray {
244 pub fn as_slice(&self) -> &[AMQPValue] {
246 self.0.as_slice()
247 }
248
249 pub fn push(&mut self, v: AMQPValue) {
251 self.0.push(v);
252 }
253}
254
255impl From<Vec<AMQPValue>> for FieldArray {
256 fn from(v: Vec<AMQPValue>) -> Self {
257 Self(v)
258 }
259}
260
261impl FieldTable {
262 pub fn insert(&mut self, k: ShortString, v: AMQPValue) -> &mut Self {
264 self.0.insert(k, v);
265 self
266 }
267
268 pub fn contains_key(&self, k: &str) -> bool {
270 self.0.contains_key(k)
271 }
272
273 pub fn inner(&self) -> &BTreeMap<ShortString, AMQPValue> {
275 &self.0
276 }
277}
278
279impl<'a> IntoIterator for &'a FieldTable {
280 type Item = (&'a ShortString, &'a AMQPValue);
281 type IntoIter = btree_map::Iter<'a, ShortString, AMQPValue>;
282
283 fn into_iter(self) -> Self::IntoIter {
284 self.0.iter()
285 }
286}
287
288impl From<BTreeMap<ShortString, AMQPValue>> for FieldTable {
289 fn from(m: BTreeMap<ShortString, AMQPValue>) -> Self {
290 Self(m)
291 }
292}
293
294impl ByteArray {
295 pub fn as_slice(&self) -> &[u8] {
297 self.0.as_slice()
298 }
299
300 pub fn len(&self) -> usize {
302 self.0.len()
303 }
304
305 pub fn is_empty(&self) -> bool {
307 self.0.is_empty()
308 }
309}
310
311impl From<Vec<u8>> for ByteArray {
312 fn from(v: Vec<u8>) -> Self {
313 Self(v)
314 }
315}
316
317impl From<&[u8]> for ByteArray {
318 fn from(v: &[u8]) -> Self {
319 Self(v.to_vec())
320 }
321}
322
323#[cfg(test)]
324mod test {
325 use super::*;
326
327 #[test]
328 fn test_type_from_id() {
329 assert_eq!(AMQPType::from_id('T'), Some(AMQPType::Timestamp));
330 assert_eq!(AMQPType::from_id('S'), Some(AMQPType::LongString));
331 assert_eq!(AMQPType::from_id('s'), Some(AMQPType::ShortInt));
332 assert_eq!(AMQPType::from_id('U'), Some(AMQPType::ShortInt));
333 assert_eq!(AMQPType::from_id('l'), Some(AMQPType::LongLongInt));
334 assert_eq!(AMQPType::from_id('z'), None);
335 }
336
337 #[test]
338 fn test_type_get_id() {
339 assert_eq!(AMQPType::LongLongInt.get_id(), 'l');
340 assert_eq!(AMQPType::LongLongUInt.get_id(), 'l');
341 assert_eq!(AMQPType::ShortString.get_id(), '_');
342 }
343
344 #[test]
345 fn test_type_to_string() {
346 assert_eq!(AMQPType::Boolean.to_string(), "Boolean");
347 assert_eq!(AMQPType::Void.to_string(), "Void");
348 }
349
350 #[test]
351 fn long_string_ergonomics() {
352 let str_ref = "string ref";
353 let str_owned = "string owned".to_owned();
354 let vec = b"bytes".to_vec();
355 let array = b"bytes".to_owned();
356 let slice = &b"bytes"[..];
357
358 let from_str_ref: LongString = str_ref.into();
359 let from_str_owned: LongString = str_owned.clone().into();
360 let from_vec: LongString = vec.clone().into();
361 let from_array: LongString = array.into();
362 let from_slice: LongString = slice.into();
363
364 for (left, right) in [
365 (str_ref.as_bytes(), from_str_ref.as_bytes()),
366 (str_owned.as_bytes(), from_str_owned.as_bytes()),
367 (vec.as_ref(), from_vec.as_bytes()),
368 (array.as_ref(), from_array.as_bytes()),
369 (slice, from_slice.as_bytes()),
370 ] {
371 assert_eq!(left, right);
372 }
373 }
374}