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 #[must_use]
60 pub fn from_id(id: char) -> Option<AMQPType> {
61 match id {
62 't' => Some(AMQPType::Boolean),
63 'b' => Some(AMQPType::ShortShortInt),
64 'B' => Some(AMQPType::ShortShortUInt),
65 's' | 'U' => Some(AMQPType::ShortInt),
67 'u' => Some(AMQPType::ShortUInt),
68 'I' => Some(AMQPType::LongInt),
69 'i' => Some(AMQPType::LongUInt),
70 'L' | 'l' => Some(AMQPType::LongLongInt),
72 'f' => Some(AMQPType::Float),
73 'd' => Some(AMQPType::Double),
74 'D' => Some(AMQPType::DecimalValue),
75 'S' => Some(AMQPType::LongString),
76 'A' => Some(AMQPType::FieldArray),
77 'T' => Some(AMQPType::Timestamp),
78 'F' => Some(AMQPType::FieldTable),
79 'x' => Some(AMQPType::ByteArray),
80 'V' => Some(AMQPType::Void),
81 _ => None,
82 }
83 }
84
85 #[must_use]
91 pub fn get_id(self) -> char {
92 match self {
93 AMQPType::Boolean => 't',
94 AMQPType::ShortShortInt => 'b',
95 AMQPType::ShortShortUInt => 'B',
96 AMQPType::ShortInt => 's',
98 AMQPType::ShortUInt => 'u',
99 AMQPType::LongInt => 'I',
100 AMQPType::LongUInt => 'i',
101 AMQPType::LongLongInt | AMQPType::LongLongUInt => 'l',
103 AMQPType::Float => 'f',
104 AMQPType::Double => 'd',
105 AMQPType::DecimalValue => 'D',
106 AMQPType::ShortString => '_',
108 AMQPType::LongString => 'S',
109 AMQPType::FieldArray => 'A',
110 AMQPType::Timestamp => 'T',
111 AMQPType::FieldTable => 'F',
112 AMQPType::ByteArray => 'x',
113 AMQPType::Void => 'V',
114 }
115 }
116}
117
118impl fmt::Display for AMQPType {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 f.write_fmt(format_args!("{self:?}"))
121 }
122}
123
124pub type Boolean = bool;
126pub type ShortShortInt = i8;
128pub type ShortShortUInt = u8;
130pub type ShortInt = i16;
132pub type ShortUInt = u16;
134pub type LongInt = i32;
136pub type LongUInt = u32;
138pub type LongLongInt = i64;
140pub type LongLongUInt = u64;
142pub type Float = f32;
144pub type Double = f64;
146pub type Timestamp = LongLongUInt;
148pub type Void = ();
150
151pub const MAX_SHORT_STRING_LENGTH: usize = ShortShortUInt::MAX as usize;
153
154#[derive(Clone, Debug, PartialEq, Eq)]
156pub struct ShortStringError(usize);
157
158impl fmt::Display for ShortStringError {
159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160 write!(
161 f,
162 "ShortString exceeds maximum length of {MAX_SHORT_STRING_LENGTH} bytes (got {})",
163 self.0
164 )
165 }
166}
167
168impl std::error::Error for ShortStringError {}
169
170#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
172pub struct ShortString(String);
173#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
175pub struct LongString(Vec<u8>);
176#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
178pub struct FieldArray(Vec<AMQPValue>);
179#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
181pub struct FieldTable(BTreeMap<ShortString, AMQPValue>);
182#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
184pub struct ByteArray(Vec<u8>);
185
186#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
188pub struct DecimalValue {
189 pub scale: ShortShortUInt,
191 pub value: LongUInt,
193}
194
195impl ShortString {
196 #[must_use]
198 pub fn as_str(&self) -> &str {
199 self.0.as_str()
200 }
201
202 pub fn try_new(s: impl Into<String>) -> Result<Self, ShortStringError> {
204 let s = s.into();
205 if s.len() > MAX_SHORT_STRING_LENGTH {
206 return Err(ShortStringError(s.len()));
207 }
208 Ok(Self(s))
209 }
210}
211
212impl From<String> for ShortString {
213 fn from(s: String) -> Self {
214 Self::try_new(s).expect("ShortString exceeds maximum length")
215 }
216}
217
218impl From<&str> for ShortString {
219 fn from(s: &str) -> Self {
220 Self::try_new(s).expect("ShortString exceeds maximum length")
221 }
222}
223
224impl borrow::Borrow<str> for ShortString {
225 fn borrow(&self) -> &str {
226 self.0.borrow()
227 }
228}
229
230impl fmt::Display for ShortString {
231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232 self.0.fmt(f)
233 }
234}
235
236impl From<ShortString> for String {
237 fn from(value: ShortString) -> Self {
238 value.0
239 }
240}
241
242impl LongString {
243 #[must_use]
245 pub fn as_bytes(&self) -> &[u8] {
246 &self.0[..]
247 }
248
249 #[must_use]
251 pub fn len(&self) -> usize {
252 self.0.len()
253 }
254
255 #[must_use]
257 pub fn is_empty(&self) -> bool {
258 self.0.is_empty()
259 }
260}
261
262impl<B> From<B> for LongString
263where
264 B: Into<Vec<u8>>,
265{
266 fn from(bytes: B) -> Self {
267 Self(bytes.into())
268 }
269}
270
271impl fmt::Display for LongString {
272 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273 String::from_utf8_lossy(&self.0).fmt(f)
274 }
275}
276
277impl FieldArray {
278 #[must_use]
280 pub fn as_slice(&self) -> &[AMQPValue] {
281 self.0.as_slice()
282 }
283
284 pub fn push(&mut self, v: AMQPValue) {
286 self.0.push(v);
287 }
288}
289
290impl From<Vec<AMQPValue>> for FieldArray {
291 fn from(v: Vec<AMQPValue>) -> Self {
292 Self(v)
293 }
294}
295
296impl FieldTable {
297 pub fn insert(&mut self, k: ShortString, v: AMQPValue) -> &mut Self {
299 self.0.insert(k, v);
300 self
301 }
302
303 #[must_use]
305 pub fn contains_key(&self, k: &str) -> bool {
306 self.0.contains_key(k)
307 }
308
309 #[must_use]
311 pub fn inner(&self) -> &BTreeMap<ShortString, AMQPValue> {
312 &self.0
313 }
314}
315
316impl<'a> IntoIterator for &'a FieldTable {
317 type Item = (&'a ShortString, &'a AMQPValue);
318 type IntoIter = btree_map::Iter<'a, ShortString, AMQPValue>;
319
320 fn into_iter(self) -> Self::IntoIter {
321 self.0.iter()
322 }
323}
324
325impl From<BTreeMap<ShortString, AMQPValue>> for FieldTable {
326 fn from(m: BTreeMap<ShortString, AMQPValue>) -> Self {
327 Self(m)
328 }
329}
330
331impl ByteArray {
332 #[must_use]
334 pub fn as_slice(&self) -> &[u8] {
335 self.0.as_slice()
336 }
337
338 #[must_use]
340 pub fn len(&self) -> usize {
341 self.0.len()
342 }
343
344 #[must_use]
346 pub fn is_empty(&self) -> bool {
347 self.0.is_empty()
348 }
349}
350
351impl From<Vec<u8>> for ByteArray {
352 fn from(v: Vec<u8>) -> Self {
353 Self(v)
354 }
355}
356
357impl From<&[u8]> for ByteArray {
358 fn from(v: &[u8]) -> Self {
359 Self(v.to_vec())
360 }
361}
362
363#[cfg(test)]
364mod test {
365 use super::*;
366
367 #[test]
368 fn test_type_from_id() {
369 assert_eq!(AMQPType::from_id('T'), Some(AMQPType::Timestamp));
370 assert_eq!(AMQPType::from_id('S'), Some(AMQPType::LongString));
371 assert_eq!(AMQPType::from_id('s'), Some(AMQPType::ShortInt));
372 assert_eq!(AMQPType::from_id('U'), Some(AMQPType::ShortInt));
373 assert_eq!(AMQPType::from_id('l'), Some(AMQPType::LongLongInt));
374 assert_eq!(AMQPType::from_id('z'), None);
375 }
376
377 #[test]
378 fn test_type_get_id() {
379 assert_eq!(AMQPType::LongLongInt.get_id(), 'l');
380 assert_eq!(AMQPType::LongLongUInt.get_id(), 'l');
381 assert_eq!(AMQPType::ShortString.get_id(), '_');
382 }
383
384 #[test]
385 fn test_type_to_string() {
386 assert_eq!(AMQPType::Boolean.to_string(), "Boolean");
387 assert_eq!(AMQPType::Void.to_string(), "Void");
388 }
389
390 #[test]
391 fn long_string_ergonomics() {
392 let str_ref = "string ref";
393 let str_owned = "string owned".to_owned();
394 let vec = b"bytes".to_vec();
395 let array = b"bytes".to_owned();
396 let slice = &b"bytes"[..];
397
398 let from_str_ref: LongString = str_ref.into();
399 let from_str_owned: LongString = str_owned.clone().into();
400 let from_vec: LongString = vec.clone().into();
401 let from_array: LongString = array.into();
402 let from_slice: LongString = slice.into();
403
404 for (left, right) in [
405 (str_ref.as_bytes(), from_str_ref.as_bytes()),
406 (str_owned.as_bytes(), from_str_owned.as_bytes()),
407 (vec.as_ref(), from_vec.as_bytes()),
408 (array.as_ref(), from_array.as_bytes()),
409 (slice, from_slice.as_bytes()),
410 ] {
411 assert_eq!(left, right);
412 }
413 }
414
415 #[test]
416 fn short_string_length_limit() {
417 assert!(ShortString::try_new("ok").is_ok());
418 assert!(ShortString::try_new("a".repeat(255)).is_ok());
419 assert_eq!(
420 ShortString::try_new("a".repeat(256)),
421 Err(ShortStringError(256))
422 );
423 }
424}