1use amq_protocol_types::{AMQPValue, FieldArray, FieldTable};
6use anyhow::Result;
7use serde_json::{Map, Number, Value};
8use std::error::Error;
9use std::fmt::Debug;
10use thiserror::Error as ThisError;
11
12#[cfg(test)]
13mod tests;
14
15pub trait ToJson {
17 type Target;
18 type Error: Error + Debug + Clone;
19 fn to_json_value(&self) -> Result<Self::Target, Self::Error>;
20}
21
22pub trait ToAmqp {
24 type Target;
25 type Error: Error + Debug + Clone;
26 fn to_amqp_value(&self) -> Result<Self::Target, Self::Error>;
27}
28
29#[derive(Clone, ThisError, Debug, PartialEq)]
31pub enum ToJsonError {
32 #[error("Invalid float: {0:}")]
33 InvalidFloat(f32),
34 #[error("Invalid float: {0:}")]
35 InvalidDouble(f64),
36 #[error("Conversion not implemented")]
37 Unimplemented,
38}
39
40impl ToJson for AMQPValue {
41 type Target = Value;
42 type Error = ToJsonError;
43 fn to_json_value(&self) -> Result<Self::Target, Self::Error> {
44 use AMQPValue::*;
45 let result = match self {
46 Boolean(val) => Value::Bool(*val),
47 ShortShortInt(val) => Value::Number((*val).into()),
48 ShortShortUInt(val) => Value::Number((*val).into()),
49 ShortInt(val) => Value::Number((*val).into()),
50 ShortUInt(val) => Value::Number((*val).into()),
51 LongInt(val) => Value::Number((*val).into()),
52 LongUInt(val) => Value::Number((*val).into()),
53 LongLongInt(val) => Value::Number((*val).into()),
54 Float(val) => Number::from_f64((*val).into())
55 .map(|v| Value::Number(v))
56 .ok_or(ToJsonError::InvalidFloat(*val))?,
57 Double(val) => Number::from_f64(*val)
58 .map(|v| Value::Number(v))
59 .ok_or(ToJsonError::InvalidDouble(*val))?,
60 DecimalValue(_val) => Err(ToJsonError::Unimplemented)?,
61 LongString(val) => Value::String(val.to_string()),
62 ShortString(val) => Value::String(val.to_string()),
63 Timestamp(val) => Value::Number((*val).into()),
64 FieldArray(val) => val
65 .as_slice()
66 .iter()
67 .map(|val| val.to_json_value())
68 .collect::<Result<Vec<_>, _>>()
69 .map(|val| Value::Array(val))?,
70 FieldTable(val) => val
71 .inner()
72 .iter()
73 .map(|(key, value)| value.to_json_value().map(|v| (key.to_string(), v)))
74 .collect::<Result<Map<String, Value>, _>>()
75 .map(|res| Value::Object(res))?,
76 ByteArray(val) => Value::Array(
77 val.as_slice()
78 .iter()
79 .map(|val| Value::Number((*val).into()))
80 .collect::<Vec<_>>(),
81 ),
82 Void => Value::Null,
83 };
84 Ok(result)
85 }
86}
87
88#[derive(Clone, ThisError, Debug)]
90pub enum ToAmqpError {
91 #[error("Error converting number to an AMQPValue")]
92 NumberError,
93}
94
95impl ToAmqp for Value {
96 type Target = AMQPValue;
97 type Error = ToAmqpError;
98 fn to_amqp_value(&self) -> Result<Self::Target, Self::Error> {
99 use Value::*;
100 let result = match self {
101 Bool(val) => AMQPValue::Boolean(*val),
102 Null => AMQPValue::Void,
103 Number(val) => match (val.as_f64(), val.as_i64(), val.as_u64()) {
104 (Some(double), _, _) => AMQPValue::Double(double),
105 (_, Some(signed), _) => AMQPValue::LongLongInt(signed),
106 (_, _, Some(unsigned)) => AMQPValue::Timestamp(unsigned),
107 _ => Err(ToAmqpError::NumberError)?,
108 },
109 String(val) => AMQPValue::LongString(val.as_str().into()),
110 Array(val) => AMQPValue::FieldArray(
111 val.iter()
112 .map(|val| val.to_amqp_value())
113 .collect::<Result<Vec<_>, _>>()?
114 .into_iter()
115 .fold(FieldArray::default(), |mut array, item| {
116 array.push(item);
117 array
118 }),
119 ),
120 Object(val) => AMQPValue::FieldTable(
121 val.iter()
122 .map(|(key, val)| val.to_amqp_value().map(|val| (key, val)))
123 .collect::<Result<Vec<_>, _>>()?
124 .into_iter()
125 .fold(FieldTable::default(), |mut array, (key, val)| {
126 array.insert(key.as_str().into(), val);
127 array
128 }),
129 ),
130 };
131 Ok(result)
132 }
133}