use super::value::FieldValue;
use crate::registry::{self, InformationElement};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MessageHeader {
pub version: u16,
pub length: u16,
pub export_time: u32,
pub sequence_number: u32,
pub observation_domain_id: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SetHeader {
pub set_id: u16,
pub length: u16,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FieldSpecifier {
pub information_element_identifier: u16,
pub field_length: u16,
pub enterprise_number: Option<u32>,
}
impl FieldSpecifier {
pub const VARIABLE_LENGTH: u16 = 65535;
pub fn is_variable_length(&self) -> bool {
self.field_length == Self::VARIABLE_LENGTH
}
pub fn is_reverse(&self) -> bool {
self.enterprise_number == Some(registry::REVERSE_INFORMATION_ELEMENT_PEN)
}
pub fn information_element(&self) -> Option<&'static InformationElement> {
match self.enterprise_number {
None | Some(registry::REVERSE_INFORMATION_ELEMENT_PEN) => {
registry::lookup(self.information_element_identifier)
}
Some(_) => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TemplateRecord {
pub template_id: u16,
pub field_count: u16,
pub field_specifiers: Vec<FieldSpecifier>,
}
impl TemplateRecord {
pub fn is_withdrawal(&self) -> bool {
self.field_count == 0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OptionsTemplateRecord {
pub template_id: u16,
pub field_count: u16,
pub scope_field_count: u16,
pub field_specifiers: Vec<FieldSpecifier>,
}
impl OptionsTemplateRecord {
pub fn is_withdrawal(&self) -> bool {
self.field_count == 0
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Field {
pub specifier: FieldSpecifier,
pub value: FieldValue,
}
impl Field {
pub fn information_element(&self) -> Option<&'static InformationElement> {
self.specifier.information_element()
}
pub fn name(&self) -> Option<&'static str> {
self.information_element().map(|ie| ie.name)
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DataRecord {
pub template_id: u16,
pub scope_field_count: u16,
pub fields: Vec<Field>,
}
impl DataRecord {
pub fn get(&self, name: &str) -> Option<&FieldValue> {
self.fields
.iter()
.find(|f| f.specifier.enterprise_number.is_none() && f.name() == Some(name))
.map(|f| &f.value)
}
pub fn get_by_id(&self, id: u16, enterprise_number: Option<u32>) -> Option<&FieldValue> {
self.fields
.iter()
.find(|f| {
f.specifier.information_element_identifier == id
&& f.specifier.enterprise_number == enterprise_number
})
.map(|f| &f.value)
}
pub fn scope_fields(&self) -> &[Field] {
let n = (self.scope_field_count as usize).min(self.fields.len());
&self.fields[..n]
}
pub fn non_scope_fields(&self) -> &[Field] {
let n = (self.scope_field_count as usize).min(self.fields.len());
&self.fields[n..]
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Set {
Template {
header: SetHeader,
records: Vec<TemplateRecord>,
},
OptionsTemplate {
header: SetHeader,
records: Vec<OptionsTemplateRecord>,
},
Data {
header: SetHeader,
records: Vec<DataRecord>,
},
UndecodedData { header: SetHeader, payload: Vec<u8> },
Reserved { header: SetHeader, payload: Vec<u8> },
}
impl Set {
pub fn header(&self) -> &SetHeader {
match self {
Set::Template { header, .. }
| Set::OptionsTemplate { header, .. }
| Set::Data { header, .. }
| Set::UndecodedData { header, .. }
| Set::Reserved { header, .. } => header,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Message {
pub header: MessageHeader,
pub sets: Vec<Set>,
}
impl Message {
pub fn data_records(&self) -> impl Iterator<Item = &DataRecord> {
self.sets.iter().flat_map(|set| match set {
Set::Data { records, .. } => records.as_slice(),
_ => &[],
})
}
}