use specs::*;
use amq_protocol_types::*;
use serde_json::Value;
use std::collections::BTreeMap;
#[derive(Debug, Deserialize)]
pub struct _AMQProtocolDefinition {
name: ShortString,
#[serde(rename="major-version")]
major_version: ShortShortUInt,
#[serde(rename="minor-version")]
minor_version: ShortShortUInt,
revision: ShortShortUInt,
port: LongUInt,
copyright: Vec<LongString>,
domains: Vec<_AMQPDomain>,
constants: Vec<_AMQPConstant>,
classes: Vec<_AMQPClass>,
}
impl _AMQProtocolDefinition {
pub fn into_specs(self) -> AMQProtocolDefinition {
let domains = self.domains.iter().fold(BTreeMap::new(), |mut domains, domain| {
domains.insert(domain.0.clone(), domain.1.to_specs());
domains
});
let classes = self.classes.iter().map(|klass| klass.to_specs(&domains)).collect();
AMQProtocolDefinition {
name: self.name,
major_version: self.major_version,
minor_version: self.minor_version,
revision: self.revision,
port: self.port,
copyright: self.copyright.iter().fold(LongString::new(), |acc, cur| acc + cur),
domains: domains,
constants: self.constants.iter().filter_map(|constant| if constant.klass.is_none() { Some(constant.to_specs()) } else { None }).collect(),
soft_errors: self.constants.iter().filter_map(|constant| if let Some(_AMQPErrorKind::Soft) = constant.klass { Some(constant.to_specs()) } else { None }).collect(),
hard_errors: self.constants.iter().filter_map(|constant| if let Some(_AMQPErrorKind::Hard) = constant.klass { Some(constant.to_specs()) } else { None }).collect(),
classes: classes,
}
}
}
#[derive(Debug, Deserialize)]
struct _AMQPDomain(ShortString, _AMQPType);
#[derive(Debug, Deserialize)]
enum _AMQPType {
#[serde(rename="bit")]
Bit,
#[serde(rename="octet")]
Octet,
#[serde(rename="short")]
Short,
#[serde(rename="long")]
Long,
#[serde(rename="longlong")]
LongLong,
#[serde(rename="shortstr")]
ShortStr,
#[serde(rename="longstr")]
LongStr,
#[serde(rename="table")]
Table,
#[serde(rename="timestamp")]
Timestamp,
}
impl _AMQPType {
fn to_specs(&self) -> AMQPType {
match *self {
_AMQPType::Bit => AMQPType::Boolean,
_AMQPType::Octet => AMQPType::ShortShortUInt,
_AMQPType::Short => AMQPType::ShortUInt,
_AMQPType::Long => AMQPType::LongUInt,
_AMQPType::LongLong => AMQPType::LongLongUInt,
_AMQPType::ShortStr => AMQPType::ShortString,
_AMQPType::LongStr => AMQPType::LongString,
_AMQPType::Table => AMQPType::FieldTable,
_AMQPType::Timestamp => AMQPType::Timestamp,
}
}
}
#[derive(Debug, Deserialize)]
enum _AMQPErrorKind {
#[serde(rename="soft-error")]
Soft,
#[serde(rename="hard-error")]
Hard,
}
#[derive(Debug, Deserialize)]
struct _AMQPConstant {
pub name: ShortString,
pub value: ShortUInt,
#[serde(rename="class")]
pub klass: Option<_AMQPErrorKind>,
}
impl _AMQPConstant {
fn to_specs(&self) -> AMQPConstant {
AMQPConstant {
name: self.name.clone(),
value: self.value,
amqp_type: if self.value > 255 { AMQPType::ShortUInt } else { AMQPType::ShortShortUInt },
}
}
}
#[derive(Debug, Deserialize)]
struct _AMQPClass {
id: ShortUInt,
methods: Vec<_AMQPMethod>,
name: ShortString,
properties: Option<Vec<_AMQPProperty>>,
}
impl _AMQPClass {
fn to_specs(&self, domains: &BTreeMap<String, AMQPType>) -> AMQPClass {
let properties = match self.properties {
Some(ref properties) => properties.iter().map(|prop| prop.to_specs()).collect(),
None => Vec::new(),
};
AMQPClass {
id: self.id,
methods: self.methods.iter().map(|method| method.to_specs(domains)).collect(),
name: self.name.clone(),
properties: properties,
has_properties: self.properties.as_ref().map(|p| !p.is_empty()).unwrap_or(false),
is_connection: self.id == 10,
}
}
}
#[derive(Debug, Deserialize)]
struct _AMQPMethod {
id: ShortUInt,
arguments: Vec<_AMQPArgument>,
name: ShortString,
synchronous: Option<Boolean>,
}
impl _AMQPMethod {
fn to_specs(&self, domains: &BTreeMap<ShortString, AMQPType>) -> AMQPMethod {
let arguments = self.arguments_to_specs(domains);
let has_arguments = !arguments.is_empty();
let has_flags = arguments.iter().any(|arg| match *arg {
AMQPArgument::Value(_) => false,
AMQPArgument::Flags(_) => true,
});
AMQPMethod {
id: self.id,
arguments: arguments,
has_arguments: has_arguments,
has_flags: has_flags,
name: self.name.clone(),
synchronous: self.synchronous.unwrap_or(false),
}
}
fn arguments_to_specs(&self, domains: &BTreeMap<ShortString, AMQPType>) -> Vec<AMQPArgument> {
let mut arguments = Vec::new();
let mut flags : Option<Vec<AMQPFlagArgument>> = None;
for argument in &self.arguments {
let amqp_type = argument.get_type(domains);
if amqp_type == AMQPType::Boolean {
let mut flgs = flags.take().unwrap_or_else(Vec::new);
flgs.push(argument.to_flag_specs());
flags = Some(flgs);
} else {
if let Some(flags) = flags.take() {
arguments.push(AMQPArgument::Flags(flags));
}
arguments.push(AMQPArgument::Value(argument.to_value_specs(amqp_type)));
}
}
if let Some(flags) = flags.take() {
arguments.push(AMQPArgument::Flags(flags));
}
arguments
}
}
#[derive(Debug, Deserialize)]
struct _AMQPArgument {
#[serde(rename="type")]
amqp_type: Option<_AMQPType>,
name: ShortString,
#[serde(rename="default-value")]
default_value: Option<Value>,
domain: Option<ShortString>,
}
impl _AMQPArgument {
fn to_flag_specs(&self) -> AMQPFlagArgument {
AMQPFlagArgument {
name: self.name.clone(),
default_value: self.default_value.as_ref().and_then(|v| v.as_u64()).map(|u| u != 0).unwrap_or(false),
}
}
fn to_value_specs(&self, amqp_type: AMQPType) -> AMQPValueArgument {
AMQPValueArgument {
amqp_type: amqp_type,
name: self.name.clone(),
default_value: self.default_value.as_ref().map(From::from),
domain: self.domain.clone(),
}
}
fn get_type(&self, domains: &BTreeMap<ShortString, AMQPType>) -> AMQPType {
match self.amqp_type {
Some(ref amqp_type) => amqp_type.to_specs(),
None => {
let domain = match self.domain {
Some(ref domain) => domain,
None => panic!(format!("{} has no type nor domain", self.name)),
};
domains.get(domain).expect(&format!("No {} domain exists", domain)).clone()
},
}
}
}
#[derive(Debug, Deserialize)]
struct _AMQPProperty {
#[serde(rename="type")]
amqp_type: _AMQPType,
name: ShortString,
}
impl _AMQPProperty {
fn to_specs(&self) -> AMQPProperty {
AMQPProperty {
amqp_type: self.amqp_type.to_specs(),
name: self.name.clone(),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_convert_to_specs() {
let def = _AMQProtocolDefinition {
name: "amqp".to_string(),
major_version: 0,
minor_version: 9,
revision: 1,
port: 5672,
copyright: vec!["foo".to_string(), "bar".to_string()],
domains: vec![_AMQPDomain("d1".to_string(), _AMQPType::Bit)],
constants: vec![
_AMQPConstant {
name: "c1".to_string(),
value: 42,
klass: None,
},
_AMQPConstant {
name: "c2".to_string(),
value: 43,
klass: Some(_AMQPErrorKind::Soft),
},
_AMQPConstant {
name: "c3".to_string(),
value: 256,
klass: Some(_AMQPErrorKind::Hard),
},
],
classes: vec![_AMQPClass {
id: 42,
methods: vec![_AMQPMethod {
id: 42,
arguments: vec![_AMQPArgument {
amqp_type: Some(_AMQPType::Short),
name: "arg1".to_string(),
default_value: None,
domain: None,
}],
name: "meth1".to_string(),
synchronous: None,
}],
name: "class1".to_string(),
properties: Some(vec![_AMQPProperty {
amqp_type: _AMQPType::Octet,
name: "prop1".to_string(),
}]),
}],
};
let mut dom = BTreeMap::new();
dom.insert("d1".to_string(), AMQPType::Boolean);
let expected = AMQProtocolDefinition {
name: "amqp".to_string(),
major_version: 0,
minor_version: 9,
revision: 1,
port: 5672,
copyright: "foobar".to_string(),
domains: dom,
constants: vec![AMQPConstant {
name: "c1".to_string(),
value: 42,
amqp_type: AMQPType::ShortShortUInt,
}],
soft_errors: vec![AMQPConstant {
name: "c2".to_string(),
value: 43,
amqp_type: AMQPType::ShortShortUInt,
}],
hard_errors: vec![AMQPConstant {
name: "c3".to_string(),
value: 256,
amqp_type: AMQPType::ShortUInt,
}],
classes: vec![AMQPClass {
id: 42,
methods: vec![AMQPMethod {
id: 42,
arguments: vec![AMQPArgument::Value(AMQPValueArgument {
amqp_type: AMQPType::ShortUInt,
name: "arg1".to_string(),
default_value: None,
domain: None,
})],
name: "meth1".to_string(),
synchronous: false,
has_arguments: true,
has_flags: false,
}],
name: "class1".to_string(),
properties: vec![AMQPProperty {
amqp_type: AMQPType::ShortShortUInt,
name: "prop1".to_string(),
}],
has_properties: true,
is_connection: false,
}],
};
assert_eq!(def.into_specs(), expected);
}
}