use crate::types::Presence;
#[derive(Debug, Clone)]
pub struct MessageDef {
pub name: String,
pub id: u16,
pub block_length: u16,
pub semantic_type: Option<String>,
pub description: Option<String>,
pub since_version: Option<u16>,
pub deprecated: Option<u16>,
pub fields: Vec<FieldDef>,
pub groups: Vec<GroupDef>,
pub data_fields: Vec<DataFieldDef>,
}
impl MessageDef {
#[must_use]
pub fn new(name: String, id: u16, block_length: u16) -> Self {
Self {
name,
id,
block_length,
semantic_type: None,
description: None,
since_version: None,
deprecated: None,
fields: Vec::new(),
groups: Vec::new(),
data_fields: Vec::new(),
}
}
pub fn add_field(&mut self, field: FieldDef) {
self.fields.push(field);
}
pub fn add_group(&mut self, group: GroupDef) {
self.groups.push(group);
}
pub fn add_data_field(&mut self, data_field: DataFieldDef) {
self.data_fields.push(data_field);
}
#[must_use]
pub fn has_groups(&self) -> bool {
!self.groups.is_empty()
}
#[must_use]
pub fn has_var_data(&self) -> bool {
!self.data_fields.is_empty()
}
#[must_use]
pub fn min_encoded_length(&self) -> usize {
8 + self.block_length as usize }
}
#[derive(Debug, Clone)]
pub struct FieldDef {
pub name: String,
pub id: u16,
pub type_name: String,
pub offset: usize,
pub presence: Presence,
pub semantic_type: Option<String>,
pub description: Option<String>,
pub since_version: Option<u16>,
pub deprecated: Option<u16>,
pub value_ref: Option<String>,
pub encoded_length: usize,
}
impl FieldDef {
#[must_use]
pub fn new(name: String, id: u16, type_name: String, offset: usize) -> Self {
Self {
name,
id,
type_name,
offset,
presence: Presence::Required,
semantic_type: None,
description: None,
since_version: None,
deprecated: None,
value_ref: None,
encoded_length: 0,
}
}
#[must_use]
pub fn is_optional(&self) -> bool {
self.presence == Presence::Optional
}
#[must_use]
pub fn is_constant(&self) -> bool {
self.presence == Presence::Constant
}
#[must_use]
pub fn end_offset(&self) -> usize {
self.offset + self.encoded_length
}
}
#[derive(Debug, Clone)]
pub struct GroupDef {
pub name: String,
pub id: u16,
pub block_length: u16,
pub dimension_type: String,
pub description: Option<String>,
pub since_version: Option<u16>,
pub deprecated: Option<u16>,
pub fields: Vec<FieldDef>,
pub nested_groups: Vec<GroupDef>,
pub data_fields: Vec<DataFieldDef>,
}
impl GroupDef {
#[must_use]
pub fn new(name: String, id: u16, block_length: u16) -> Self {
Self {
name,
id,
block_length,
dimension_type: "groupSizeEncoding".to_string(),
description: None,
since_version: None,
deprecated: None,
fields: Vec::new(),
nested_groups: Vec::new(),
data_fields: Vec::new(),
}
}
pub fn add_field(&mut self, field: FieldDef) {
self.fields.push(field);
}
pub fn add_nested_group(&mut self, group: GroupDef) {
self.nested_groups.push(group);
}
pub fn add_data_field(&mut self, data_field: DataFieldDef) {
self.data_fields.push(data_field);
}
#[must_use]
pub fn has_nested_groups(&self) -> bool {
!self.nested_groups.is_empty()
}
#[must_use]
pub fn has_var_data(&self) -> bool {
!self.data_fields.is_empty()
}
#[must_use]
pub const fn header_size(&self) -> usize {
4 }
}
#[derive(Debug, Clone)]
pub struct DataFieldDef {
pub name: String,
pub id: u16,
pub type_name: String,
pub description: Option<String>,
pub since_version: Option<u16>,
pub deprecated: Option<u16>,
}
impl DataFieldDef {
#[must_use]
pub fn new(name: String, id: u16, type_name: String) -> Self {
Self {
name,
id,
type_name,
description: None,
since_version: None,
deprecated: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_message_def_creation() {
let mut msg = MessageDef::new("NewOrderSingle".to_string(), 1, 56);
msg.add_field(FieldDef::new(
"clOrdId".to_string(),
11,
"ClOrdId".to_string(),
0,
));
msg.add_field(FieldDef::new(
"symbol".to_string(),
55,
"Symbol".to_string(),
20,
));
assert_eq!(msg.name, "NewOrderSingle");
assert_eq!(msg.id, 1);
assert_eq!(msg.block_length, 56);
assert_eq!(msg.fields.len(), 2);
assert_eq!(msg.min_encoded_length(), 64); }
#[test]
fn test_field_def() {
let mut field = FieldDef::new("price".to_string(), 44, "decimal".to_string(), 30);
field.encoded_length = 9;
field.presence = Presence::Optional;
assert!(field.is_optional());
assert!(!field.is_constant());
assert_eq!(field.end_offset(), 39);
}
#[test]
fn test_group_def() {
let mut group = GroupDef::new("mdEntries".to_string(), 268, 31);
group.add_field(FieldDef::new(
"securityId".to_string(),
48,
"uint64".to_string(),
0,
));
group.add_field(FieldDef::new(
"rptSeq".to_string(),
83,
"uint32".to_string(),
8,
));
assert_eq!(group.name, "mdEntries");
assert_eq!(group.fields.len(), 2);
assert_eq!(group.header_size(), 4);
assert!(!group.has_nested_groups());
assert!(!group.has_var_data());
}
#[test]
fn test_data_field_def() {
let data = DataFieldDef::new("rawData".to_string(), 96, "varDataEncoding".to_string());
assert_eq!(data.name, "rawData");
assert_eq!(data.type_name, "varDataEncoding");
}
#[test]
fn test_message_with_groups_and_data() {
let mut msg = MessageDef::new("MarketDataRefresh".to_string(), 3, 16);
msg.add_group(GroupDef::new("mdEntries".to_string(), 268, 31));
msg.add_data_field(DataFieldDef::new(
"rawData".to_string(),
96,
"varDataEncoding".to_string(),
));
assert!(msg.has_groups());
assert!(msg.has_var_data());
}
}