use lapin::ExchangeKind;
use lapin::types::{AMQPValue, FieldTable};
use ruststream::asyncapi::{Binding, Bindings};
use serde::Serialize;
use serde::ser::{SerializeMap, SerializeSeq, Serializer};
use crate::broker::PROTOCOL;
use crate::publish_policy::PublishOptions;
use crate::queue::QueueSpec;
const BINDING_VERSION: &str = "0.3.0";
pub(crate) const BINDINGS_EXTENSION: &str = "x-ruststream-amqp";
const PERSISTENT: u8 = 2;
const TRANSIENT: u8 = 1;
pub(crate) const REPLY_ADDRESS_LOCATION: &str = "$message.header#/reply-to";
#[derive(Serialize)]
struct QueueChannel<'a> {
is: &'static str,
queue: Queue<'a>,
}
#[derive(Serialize)]
struct Queue<'a> {
name: &'a str,
durable: bool,
exclusive: bool,
#[serde(rename = "autoDelete")]
auto_delete: bool,
}
#[derive(Serialize)]
struct PublishQueueChannel<'a> {
is: &'static str,
queue: PublishQueue<'a>,
}
#[derive(Serialize)]
struct PublishQueue<'a> {
name: &'a str,
}
#[derive(Serialize)]
struct RoutingKeyChannel<'a> {
is: &'static str,
exchange: Exchange<'a>,
}
#[derive(Serialize)]
struct Exchange<'a> {
name: &'a str,
}
#[derive(Serialize)]
struct ReceiveOperation {
ack: bool,
}
#[derive(Serialize)]
struct SendOperation {
#[serde(rename = "deliveryMode")]
delivery_mode: u8,
#[serde(skip_serializing_if = "Option::is_none")]
priority: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
expiration: Option<u64>,
}
#[derive(Serialize)]
struct DeclaredBindings<'a> {
bindings: Vec<DeclaredBinding<'a>>,
}
#[derive(Serialize)]
struct DeclaredBinding<'a> {
exchange: &'a str,
#[serde(rename = "type")]
kind: &'a str,
#[serde(rename = "routingKey")]
routing_key: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
arguments: Option<Arguments<'a>>,
}
struct Arguments<'a>(&'a FieldTable);
impl Serialize for Arguments<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let table = self.0.inner();
let mut map = serializer.serialize_map(Some(table.len()))?;
for (name, value) in table {
map.serialize_entry(name.as_str(), &Argument(value))?;
}
map.end()
}
}
struct Argument<'a>(&'a AMQPValue);
impl Serialize for Argument<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self.0 {
AMQPValue::Boolean(value) => serializer.serialize_bool(*value),
AMQPValue::ShortShortInt(value) => serializer.serialize_i8(*value),
AMQPValue::ShortShortUInt(value) => serializer.serialize_u8(*value),
AMQPValue::ShortInt(value) => serializer.serialize_i16(*value),
AMQPValue::ShortUInt(value) => serializer.serialize_u16(*value),
AMQPValue::LongInt(value) => serializer.serialize_i32(*value),
AMQPValue::LongUInt(value) => serializer.serialize_u32(*value),
AMQPValue::LongLongInt(value) => serializer.serialize_i64(*value),
AMQPValue::Float(value) => serializer.serialize_f32(*value),
AMQPValue::Double(value) => serializer.serialize_f64(*value),
AMQPValue::Timestamp(value) => serializer.serialize_u64(*value),
AMQPValue::ShortString(value) => serializer.serialize_str(value.as_str()),
AMQPValue::LongString(value) => {
serializer.serialize_str(&String::from_utf8_lossy(value.as_bytes()))
}
AMQPValue::ByteArray(value) => {
let bytes = value.as_slice();
let mut list = serializer.serialize_seq(Some(bytes.len()))?;
for byte in bytes {
list.serialize_element(byte)?;
}
list.end()
}
AMQPValue::FieldArray(values) => {
let values = values.as_slice();
let mut list = serializer.serialize_seq(Some(values.len()))?;
for value in values {
list.serialize_element(&Argument(value))?;
}
list.end()
}
AMQPValue::FieldTable(table) => Arguments(table).serialize(serializer),
AMQPValue::DecimalValue(value) => {
let mut decimal = serializer.serialize_map(Some(2))?;
decimal.serialize_entry("scale", &value.scale)?;
decimal.serialize_entry("value", &value.value)?;
decimal.end()
}
AMQPValue::Void => serializer.serialize_none(),
}
}
}
fn exchange_type(kind: &ExchangeKind) -> &str {
match kind {
ExchangeKind::Custom(kind) => kind.as_str(),
ExchangeKind::Direct => "direct",
ExchangeKind::Fanout => "fanout",
ExchangeKind::Headers => "headers",
ExchangeKind::Topic => "topic",
}
}
fn one<T: Serialize>(body: &T) -> Bindings {
Binding::new(PROTOCOL, BINDING_VERSION, body)
.map(|binding| Bindings::new().with(binding))
.unwrap_or_default()
}
pub(crate) fn queue_channel(spec: &QueueSpec) -> Bindings {
let mut reported = one(&QueueChannel {
is: "queue",
queue: Queue {
name: &spec.name,
durable: spec.durable,
exclusive: spec.exclusive,
auto_delete: spec.auto_delete,
},
});
if spec.bindings.is_empty() {
return reported;
}
let declared = DeclaredBindings {
bindings: spec
.bindings
.iter()
.map(|binding| DeclaredBinding {
exchange: binding.exchange.name(),
kind: exchange_type(binding.exchange.kind()),
routing_key: &binding.routing_key,
arguments: (!binding.arguments.inner().is_empty())
.then_some(Arguments(&binding.arguments)),
})
.collect(),
};
if let Ok(extension) = Binding::extension(BINDINGS_EXTENSION, &declared) {
reported = reported.with(extension);
}
reported
}
pub(crate) fn consumer_operation() -> Bindings {
one(&ReceiveOperation { ack: true })
}
pub(crate) fn publish_channel(options: &PublishOptions, destination: &str) -> Bindings {
if options.exchange.is_empty() {
one(&PublishQueueChannel {
is: "queue",
queue: PublishQueue { name: destination },
})
} else {
one(&RoutingKeyChannel {
is: "routingKey",
exchange: Exchange {
name: &options.exchange,
},
})
}
}
pub(crate) fn publish_operation(options: &PublishOptions) -> Bindings {
let defaults = &options.defaults;
one(&SendOperation {
delivery_mode: if defaults.persistent.unwrap_or(true) {
PERSISTENT
} else {
TRANSIENT
},
priority: defaults.priority,
expiration: defaults
.expiration
.map(|ttl| u64::try_from(ttl.as_millis()).unwrap_or(u64::MAX)),
})
}