amqp_value_json/
lib.rs

1//! Module that publishes traits allowing to quickly convert
2//! between an [AMQPValue](amq_protocol_types::AMQPValue) and JSON
3//! [Value](serde_json::Value) (and vice versa).
4
5use 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
15/// Convert AMQPValue to a JSON Value.
16pub trait ToJson {
17    type Target;
18    type Error: Error + Debug + Clone;
19    fn to_json_value(&self) -> Result<Self::Target, Self::Error>;
20}
21
22/// Convert a JSON Value to an AMQPValue.
23pub 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/// Error that can happen when converting an [AMQPValue](amq_protocol_types::AMQPValue) to a [JSON Value](serde_json::Value).
30#[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/// Error converting something to an [AMQPValue](amq_protocol_types::AMQPValue).
89#[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}