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
149pub const MAX_SHORT_STRING_LENGTH: usize = ShortShortUInt::MAX as usize;
151
152#[derive(Clone, Debug, PartialEq, Eq)]
154pub struct ShortStringError(usize);
155
156impl fmt::Display for ShortStringError {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 write!(
159 f,
160 "ShortString exceeds maximum length of {MAX_SHORT_STRING_LENGTH} bytes (got {})",
161 self.0
162 )
163 }
164}
165
166impl std::error::Error for ShortStringError {}
167
168#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
170pub struct ShortString(String);
171#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
173pub struct LongString(Vec<u8>);
174#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
176pub struct FieldArray(Vec<AMQPValue>);
177#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
179pub struct FieldTable(BTreeMap<ShortString, AMQPValue>);
180#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
182pub struct ByteArray(Vec<u8>);
183
184#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
186pub struct DecimalValue {
187 pub scale: ShortShortUInt,
189 pub value: LongUInt,
191}
192
193impl ShortString {
194 pub fn as_str(&self) -> &str {
196 self.0.as_str()
197 }
198
199 pub fn try_new(s: impl Into<String>) -> Result<Self, ShortStringError> {
201 let s = s.into();
202 if s.len() > MAX_SHORT_STRING_LENGTH {
203 return Err(ShortStringError(s.len()));
204 }
205 Ok(Self(s))
206 }
207}
208
209impl From<String> for ShortString {
210 fn from(s: String) -> Self {
211 Self::try_new(s).expect("ShortString exceeds maximum length")
212 }
213}
214
215impl From<&str> for ShortString {
216 fn from(s: &str) -> Self {
217 Self::try_new(s).expect("ShortString exceeds maximum length")
218 }
219}
220
221impl borrow::Borrow<str> for ShortString {
222 fn borrow(&self) -> &str {
223 self.0.borrow()
224 }
225}
226
227impl fmt::Display for ShortString {
228 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229 self.0.fmt(f)
230 }
231}
232
233impl From<ShortString> for String {
234 fn from(value: ShortString) -> Self {
235 value.0
236 }
237}
238
239impl LongString {
240 pub fn as_bytes(&self) -> &[u8] {
242 &self.0[..]
243 }
244
245 pub fn len(&self) -> usize {
247 self.0.len()
248 }
249
250 pub fn is_empty(&self) -> bool {
252 self.0.is_empty()
253 }
254}
255
256impl<B> From<B> for LongString
257where
258 B: Into<Vec<u8>>,
259{
260 fn from(bytes: B) -> Self {
261 Self(bytes.into())
262 }
263}
264
265impl fmt::Display for LongString {
266 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267 String::from_utf8_lossy(&self.0).fmt(f)
268 }
269}
270
271impl FieldArray {
272 pub fn as_slice(&self) -> &[AMQPValue] {
274 self.0.as_slice()
275 }
276
277 pub fn push(&mut self, v: AMQPValue) {
279 self.0.push(v);
280 }
281}
282
283impl From<Vec<AMQPValue>> for FieldArray {
284 fn from(v: Vec<AMQPValue>) -> Self {
285 Self(v)
286 }
287}
288
289impl FieldTable {
290 pub fn insert(&mut self, k: ShortString, v: AMQPValue) -> &mut Self {
292 self.0.insert(k, v);
293 self
294 }
295
296 pub fn contains_key(&self, k: &str) -> bool {
298 self.0.contains_key(k)
299 }
300
301 pub fn inner(&self) -> &BTreeMap<ShortString, AMQPValue> {
303 &self.0
304 }
305}
306
307impl<'a> IntoIterator for &'a FieldTable {
308 type Item = (&'a ShortString, &'a AMQPValue);
309 type IntoIter = btree_map::Iter<'a, ShortString, AMQPValue>;
310
311 fn into_iter(self) -> Self::IntoIter {
312 self.0.iter()
313 }
314}
315
316impl From<BTreeMap<ShortString, AMQPValue>> for FieldTable {
317 fn from(m: BTreeMap<ShortString, AMQPValue>) -> Self {
318 Self(m)
319 }
320}
321
322impl ByteArray {
323 pub fn as_slice(&self) -> &[u8] {
325 self.0.as_slice()
326 }
327
328 pub fn len(&self) -> usize {
330 self.0.len()
331 }
332
333 pub fn is_empty(&self) -> bool {
335 self.0.is_empty()
336 }
337}
338
339impl From<Vec<u8>> for ByteArray {
340 fn from(v: Vec<u8>) -> Self {
341 Self(v)
342 }
343}
344
345impl From<&[u8]> for ByteArray {
346 fn from(v: &[u8]) -> Self {
347 Self(v.to_vec())
348 }
349}
350
351#[cfg(test)]
352mod test {
353 use super::*;
354
355 #[test]
356 fn test_type_from_id() {
357 assert_eq!(AMQPType::from_id('T'), Some(AMQPType::Timestamp));
358 assert_eq!(AMQPType::from_id('S'), Some(AMQPType::LongString));
359 assert_eq!(AMQPType::from_id('s'), Some(AMQPType::ShortInt));
360 assert_eq!(AMQPType::from_id('U'), Some(AMQPType::ShortInt));
361 assert_eq!(AMQPType::from_id('l'), Some(AMQPType::LongLongInt));
362 assert_eq!(AMQPType::from_id('z'), None);
363 }
364
365 #[test]
366 fn test_type_get_id() {
367 assert_eq!(AMQPType::LongLongInt.get_id(), 'l');
368 assert_eq!(AMQPType::LongLongUInt.get_id(), 'l');
369 assert_eq!(AMQPType::ShortString.get_id(), '_');
370 }
371
372 #[test]
373 fn test_type_to_string() {
374 assert_eq!(AMQPType::Boolean.to_string(), "Boolean");
375 assert_eq!(AMQPType::Void.to_string(), "Void");
376 }
377
378 #[test]
379 fn long_string_ergonomics() {
380 let str_ref = "string ref";
381 let str_owned = "string owned".to_owned();
382 let vec = b"bytes".to_vec();
383 let array = b"bytes".to_owned();
384 let slice = &b"bytes"[..];
385
386 let from_str_ref: LongString = str_ref.into();
387 let from_str_owned: LongString = str_owned.clone().into();
388 let from_vec: LongString = vec.clone().into();
389 let from_array: LongString = array.into();
390 let from_slice: LongString = slice.into();
391
392 for (left, right) in [
393 (str_ref.as_bytes(), from_str_ref.as_bytes()),
394 (str_owned.as_bytes(), from_str_owned.as_bytes()),
395 (vec.as_ref(), from_vec.as_bytes()),
396 (array.as_ref(), from_array.as_bytes()),
397 (slice, from_slice.as_bytes()),
398 ] {
399 assert_eq!(left, right);
400 }
401 }
402
403 #[test]
404 fn short_string_length_limit() {
405 assert!(ShortString::try_new("ok").is_ok());
406 assert!(ShortString::try_new("a".repeat(255)).is_ok());
407 assert_eq!(ShortString::try_new("a".repeat(256)), Err(ShortStringError(256)));
408 }
409}