1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
/*
 * Copyright 2019, Ulf Lilleengen
 * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
 */

//! The types module contains the AMQP 1.0 types system encoders and decoders. By using these types you can enforce a certain encoding for your data.

use std::borrow::Cow;
use std::io::Write;
use std::vec::Vec;

use crate::error::*;
use crate::symbol::Symbol;

/**
 * Encoder trait that all types that can be serialized to an AMQP type must implement.
 */
pub trait Encoder {
    fn encode(&self, writer: &mut dyn Write) -> Result<TypeCode>;
}

// Various constants used in encoding and decoding.
pub const U8_MAX: usize = u8::MAX as usize;
pub const I8_MAX: usize = std::i8::MAX as usize;
pub const LIST8_MAX: usize = (u8::MAX as usize) - 1;
pub const LIST32_MAX: usize = (u32::MAX as usize) - 4;

pub const DESC_OPEN: Value = Value::Ulong(0x10);
pub const DESC_BEGIN: Value = Value::Ulong(0x11);
pub const DESC_ATTACH: Value = Value::Ulong(0x12);
pub const DESC_FLOW: Value = Value::Ulong(0x13);
pub const DESC_TRANSFER: Value = Value::Ulong(0x14);
pub const DESC_DISPOSITION: Value = Value::Ulong(0x15);
pub const DESC_SOURCE: Value = Value::Ulong(0x28);
pub const DESC_TARGET: Value = Value::Ulong(0x29);

pub const DESC_MESSAGE_HEADER: Value = Value::Ulong(0x70);
pub const DESC_MESSAGE_DELIVERY_ANNOTATIONS: Value = Value::Ulong(0x71);
pub const DESC_MESSAGE_ANNOTATIONS: Value = Value::Ulong(0x72);
pub const DESC_MESSAGE_PROPERTIES: Value = Value::Ulong(0x73);
pub const DESC_MESSAGE_APPLICATION_PROPERTIES: Value = Value::Ulong(0x74);
pub const DESC_MESSAGE_AMQP_DATA: Value = Value::Ulong(0x75);
pub const DESC_MESSAGE_AMQP_SEQUENCE: Value = Value::Ulong(0x76);
pub const DESC_MESSAGE_AMQP_VALUE: Value = Value::Ulong(0x77);
pub const DESC_MESSAGE_FOOTER: Value = Value::Ulong(0x78);

pub const DESC_DELIVERY_STATE_RECEIVED: Value = Value::Ulong(0x23);
pub const DESC_DELIVERY_STATE_ACCEPTED: Value = Value::Ulong(0x24);
pub const DESC_DELIVERY_STATE_REJECTED: Value = Value::Ulong(0x25);
pub const DESC_DELIVERY_STATE_RELEASED: Value = Value::Ulong(0x26);
pub const DESC_DELIVERY_STATE_MODIFIED: Value = Value::Ulong(0x27);

pub const DESC_DETACH: Value = Value::Ulong(0x16);
pub const DESC_END: Value = Value::Ulong(0x17);
pub const DESC_CLOSE: Value = Value::Ulong(0x18);

/// <http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-security-v1.0-os.html#type-sasl-mechanisms>
pub const DESC_SASL_MECHANISMS: Value = Value::Ulong(0x40);
/// <http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-security-v1.0-os.html#type-sasl-init>
pub const DESC_SASL_INIT: Value = Value::Ulong(0x41);
/// <http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-security-v1.0-os.html#type-sasl-challenge>
pub const DESC_SASL_CHALLENGE: Value = Value::Ulong(0x42);
/// <http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-security-v1.0-os.html#type-sasl-response>
pub const DESC_SASL_RESPONSE: Value = Value::Ulong(0x43);
/// <http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-security-v1.0-os.html#type-sasl-outcome>
pub const DESC_SASL_OUTCOME: Value = Value::Ulong(0x44);
pub const DESC_ERROR: Value = Value::Ulong(0x1D);

/// Milliseconds since the unix epoch
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub struct Timestamp(pub u64);

/// A reference to a type with a given value. This allows efficient zero copy of the provided values
/// and should be used when possible (depends on lifetime constraints where its used).
#[derive(Clone, PartialEq, Debug, PartialOrd, Ord, Eq, derive_more::From)]
pub enum ValueRef<'a> {
    Described(Box<ValueRef<'a>>, Box<ValueRef<'a>>),
    Null,
    #[from]
    Bool(&'a bool),
    #[from]
    Ubyte(&'a u8),
    #[from]
    Ushort(&'a u16),
    #[from]
    Uint(&'a u32),
    #[from]
    Ulong(&'a u64),
    #[from]
    Byte(&'a i8),
    #[from]
    Short(&'a i16),
    #[from]
    Int(&'a i32),
    #[from]
    Long(&'a i64),
    // Float(&'a f32),  does not impl Ord nor Eq, but is used as key in *Maps in this crate...
    // Double(&'a f64), does not impl Ord nor Eq, but is used as key in *Maps in this crate...
    // decimal32
    // decimal64
    // decimal128
    #[from]
    Char(&'a char),
    Timestamp(&'a u64),
    // uuid
    #[from]
    Binary(&'a [u8]),
    #[from]
    String(&'a str),
    Symbol(&'a [u8]),
    SymbolRef(&'a str),
    List(&'a Vec<Value>),
    #[from]
    ListRef(&'a Vec<ValueRef<'a>>),
    Map(&'a Vec<(Value, Value)>),
    #[from]
    MapRef(&'a Vec<(ValueRef<'a>, ValueRef<'a>)>),
    Array(&'a Vec<Value>),
    ArrayRef(&'a Vec<ValueRef<'a>>),
}

impl<'a> From<&'a Symbol> for ValueRef<'a> {
    fn from(s: &'a Symbol) -> Self {
        Self::Symbol(&s.0)
    }
}

impl<'a> From<&'a Timestamp> for ValueRef<'a> {
    fn from(t: &'a Timestamp) -> Self {
        Self::Timestamp(&t.0)
    }
}

impl<'a> From<&'a Value> for ValueRef<'a> {
    fn from(value: &'a Value) -> Self {
        match value {
            Value::Described(ref descriptor, ref value) => ValueRef::Described(
                Box::new(descriptor.value_ref()),
                Box::new(value.value_ref()),
            ),
            Value::Null => ValueRef::Null,
            Value::Bool(ref value) => ValueRef::Bool(value),
            Value::Str(value) => ValueRef::String(value),
            Value::String(ref value) => ValueRef::String(value),
            Value::Symbol(ref value) => ValueRef::Symbol(&value[..]),
            Value::SymbolSlice(value) => ValueRef::Symbol(&value[..]),
            Value::Ubyte(ref value) => ValueRef::Ubyte(value),
            Value::Ushort(ref value) => ValueRef::Ushort(value),
            Value::Uint(ref value) => ValueRef::Uint(value),
            Value::Ulong(ref value) => ValueRef::Ulong(value),
            Value::Byte(ref value) => ValueRef::Byte(value),
            Value::Short(ref value) => ValueRef::Short(value),
            Value::Int(ref value) => ValueRef::Int(value),
            Value::Long(ref value) => ValueRef::Long(value),
            Value::Array(ref value) => ValueRef::Array(value),
            Value::List(ref value) => ValueRef::List(value),
            Value::Map(ref value) => ValueRef::Map(value),
            Value::Binary(ref value) => ValueRef::Binary(value),
            Value::Char(ref value) => ValueRef::Char(value),
            Value::Timestamp(ref value) => ValueRef::Timestamp(value),
        }
    }
}

/// An owned value type that can be transferred to or from the broker.
/// <http://docs.oasis-open.org/amqp/core/v1.0/csprd01/amqp-core-types-v1.0-csprd01.html#toc>
#[derive(Clone, PartialEq, Debug, PartialOrd, Ord, Eq, derive_more::From)]
pub enum Value {
    Described(Box<Value>, Box<Value>),
    Null,
    #[from]
    Bool(bool),
    #[from]
    Ubyte(u8),
    #[from]
    Ushort(u16),
    #[from]
    Uint(u32),
    #[from]
    Ulong(u64),
    #[from]
    Byte(i8),
    #[from]
    Short(i16),
    #[from]
    Int(i32),
    #[from]
    Long(i64),
    // Float(f32),  does not impl Ord nor Eq, but is used as key in *Maps in this crate...
    // Double(f64), does not impl Ord nor Eq, but is used as key in *Maps in this crate...
    // decimal32
    // decimal64
    // decimal128
    #[from]
    Char(char),
    Timestamp(u64),
    // uuid
    #[from]
    Binary(Vec<u8>),
    #[from]
    Str(&'static str),
    #[from]
    String(String),
    Symbol(Vec<u8>),
    SymbolSlice(&'static [u8]),
    #[from]
    List(Vec<Value>),
    #[from]
    Map(Vec<(Value, Value)>),
    Array(Vec<Value>),
}

impl Value {
    /**
     * Convert to a reference type. Not currently implemented for all types.
     */
    pub fn value_ref(&self) -> ValueRef {
        ValueRef::from(self)
    }
}

impl From<Symbol> for Value {
    fn from(s: Symbol) -> Self {
        match s.0 {
            Cow::Owned(owned) => Self::Symbol(owned),
            Cow::Borrowed(borrowed) => Self::SymbolSlice(borrowed),
        }
    }
}

impl From<Timestamp> for Value {
    fn from(t: Timestamp) -> Self {
        Self::Timestamp(t.0)
    }
}

impl<V: Into<Value>, const N: usize> From<[V; N]> for Value {
    fn from(array: [V; N]) -> Self {
        Self::Array(
            std::array::IntoIter::new(array)
                .map(Into::into)
                .collect::<Vec<Value>>(),
        )
    }
}

/**
 * All basic type codes in AMQP.
 */
#[repr(u8)]
#[derive(Clone, PartialEq, Debug, PartialOrd, Copy)]
pub enum TypeCode {
    Described = 0x00,
    Null = 0x40,
    Boolean = 0x56,
    BooleanTrue = 0x41,
    BooleanFalse = 0x42,
    Ubyte = 0x50,
    Ushort = 0x60,
    Uint = 0x70,
    Uintsmall = 0x52,
    Uint0 = 0x43,
    Ulong = 0x80,
    Ulongsmall = 0x53,
    Ulong0 = 0x44,
    Byte = 0x51,
    Short = 0x61,
    Int = 0x71,
    Intsmall = 0x54,
    Long = 0x81,
    Longsmall = 0x55,
    // float
    // double
    // decimal32
    // decimal32
    // decimal12
    Char = 0x73,
    Timestamp = 0x83,
    // uuid
    Bin8 = 0xA0,
    Bin32 = 0xB0,
    Str8 = 0xA1,
    Str32 = 0xB1,
    Sym8 = 0xA3,
    Sym32 = 0xB3,
    List0 = 0x45,
    List8 = 0xC0,
    List32 = 0xD0,
    Map8 = 0xC1,
    Map32 = 0xD1,
    Array8 = 0xE0,
    Array32 = 0xF0,
}

#[cfg(test)]
mod tests {

    use super::*;
    use crate::decoding::decode_value;

    fn assert_type(value: &Value, expected_len: usize, expected_type: TypeCode) {
        let mut output: Vec<u8> = Vec::new();
        let ctype = value.encode(&mut output).unwrap();
        assert_eq!(expected_type, ctype);
        assert_eq!(expected_len, output.len());

        let decoded = decode_value(&mut &output[..]).unwrap();
        assert_eq!(&decoded, value);
    }

    #[test]
    fn check_types() {
        assert_type(&Value::Ulong(123), 2, TypeCode::Ulongsmall);
        assert_type(&Value::Ulong(1234), 9, TypeCode::Ulong);
        assert_type(
            &Value::String(String::from("Hello, world")),
            14,
            TypeCode::Str8,
        );
        assert_type(&Value::String(String::from("aaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccdddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffgggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooppppppppppppppppppqqqqqqqqqqqqqqqq")), 370, TypeCode::Str32);
        assert_type(
            &Value::List(vec![
                Value::Ulong(1),
                Value::Ulong(42),
                Value::String(String::from("Hello, world")),
            ]),
            21,
            TypeCode::List8,
        );
    }
}