use std::collections::HashMap;
use super::constants::SET_ID_DATA_MIN;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct FieldSpec {
pub information_element_id: u16,
pub length: u16,
pub enterprise_number: Option<u32>,
}
impl FieldSpec {
pub const fn new(ie_id: u16, length: u16) -> Self {
Self {
information_element_id: ie_id,
length,
enterprise_number: None,
}
}
pub const fn wire_length(&self) -> usize {
if self.enterprise_number.is_some() {
8
} else {
4
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct TemplateDefinition {
pub template_id: u16,
pub fields: Vec<FieldSpec>,
}
impl TemplateDefinition {
pub fn new(template_id: u16, fields: Vec<FieldSpec>) -> Self {
debug_assert!(template_id >= SET_ID_DATA_MIN, "template_id must be >= 256");
Self {
template_id,
fields,
}
}
pub fn fixed_record_length(&self) -> Option<usize> {
let mut total = 0;
for f in &self.fields {
if f.length == super::FIELD_LENGTH_VARIABLE {
return None;
}
total += f.length as usize;
}
Some(total)
}
}
#[derive(Debug, Clone)]
pub struct TemplateRegistry {
pub observation_domain_id: u32,
templates: HashMap<u16, TemplateDefinition>,
}
impl TemplateRegistry {
pub fn new(observation_domain_id: u32) -> Self {
Self {
observation_domain_id,
templates: HashMap::new(),
}
}
pub fn register(&mut self, def: TemplateDefinition) {
self.templates.insert(def.template_id, def);
}
pub fn get(&self, template_id: u16) -> Option<&TemplateDefinition> {
self.templates.get(&template_id)
}
pub fn len(&self) -> usize {
self.templates.len()
}
pub fn is_empty(&self) -> bool {
self.templates.is_empty()
}
}
pub const TEMPLATE_ID_FLOW_IPV4: u16 = 0x0100;
pub const TEMPLATE_ID_FLOW_IPV6: u16 = 0x0101;
pub static FLOWSCOPE_TEMPLATE_FLOW_IPV4: std::sync::LazyLock<TemplateDefinition> =
std::sync::LazyLock::new(|| {
TemplateDefinition::new(
TEMPLATE_ID_FLOW_IPV4,
vec![
FieldSpec::new(4, 1), FieldSpec::new(7, 2), FieldSpec::new(8, 4), FieldSpec::new(11, 2), FieldSpec::new(12, 4), FieldSpec::new(1, 8), FieldSpec::new(2, 8), FieldSpec::new(85, 8), FieldSpec::new(86, 8), FieldSpec::new(152, 8), FieldSpec::new(153, 8), FieldSpec::new(6, 2), FieldSpec::new(136, 1), ],
)
});
pub static FLOWSCOPE_TEMPLATE_FLOW_IPV6: std::sync::LazyLock<TemplateDefinition> =
std::sync::LazyLock::new(|| {
TemplateDefinition::new(
TEMPLATE_ID_FLOW_IPV6,
vec![
FieldSpec::new(4, 1),
FieldSpec::new(7, 2),
FieldSpec::new(27, 16), FieldSpec::new(11, 2),
FieldSpec::new(28, 16), FieldSpec::new(1, 8),
FieldSpec::new(2, 8),
FieldSpec::new(85, 8),
FieldSpec::new(86, 8),
FieldSpec::new(152, 8),
FieldSpec::new(153, 8),
FieldSpec::new(6, 2),
FieldSpec::new(136, 1),
],
)
});
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ipv4_template_total_octets() {
let t = &FLOWSCOPE_TEMPLATE_FLOW_IPV4;
assert_eq!(t.fixed_record_length(), Some(64));
}
#[test]
fn ipv6_template_total_octets() {
let t = &FLOWSCOPE_TEMPLATE_FLOW_IPV6;
assert_eq!(t.fixed_record_length(), Some(88));
}
#[test]
fn template_registry_register_get() {
let mut reg = TemplateRegistry::new(0x1234_5678);
reg.register(FLOWSCOPE_TEMPLATE_FLOW_IPV4.clone());
assert_eq!(reg.len(), 1);
assert!(reg.get(TEMPLATE_ID_FLOW_IPV4).is_some());
assert!(reg.get(0x9999).is_none());
}
#[test]
fn field_spec_wire_length() {
let f = FieldSpec::new(4, 1);
assert_eq!(f.wire_length(), 4);
let mut f = FieldSpec::new(0x8001, 4);
f.enterprise_number = Some(0x1234);
assert_eq!(f.wire_length(), 8);
}
}