use serde::Deserialize;
use crate::error::FilterError;
use super::{FilterChain, IdFilter, RangeFilter};
#[derive(Debug, Clone, Default, Deserialize)]
pub struct FilterConfig {
#[serde(default)]
pub id_filters: Vec<IdFilterConfig>,
#[serde(default)]
pub range_filters: Vec<RangeFilterConfig>,
#[serde(default = "default_max_hardware")]
pub max_hardware_filters: usize,
}
fn default_max_hardware() -> usize {
4
}
#[derive(Debug, Clone, Deserialize)]
pub struct IdFilterConfig {
pub id: u32,
#[serde(default)]
pub mask: Option<u32>,
#[serde(default)]
pub extended: bool,
}
#[derive(Debug, Clone, Deserialize)]
pub struct RangeFilterConfig {
pub start_id: u32,
pub end_id: u32,
#[serde(default)]
pub extended: bool,
}
impl FilterConfig {
pub fn from_toml(toml_str: &str) -> Result<Self, toml::de::Error> {
toml::from_str(toml_str)
}
pub fn into_chain(self) -> Result<FilterChain, FilterError> {
let mut chain = FilterChain::new(self.max_hardware_filters);
for config in self.id_filters {
let filter = if config.extended {
if let Some(mask) = config.mask {
IdFilter::with_mask_extended(config.id, mask)
} else {
IdFilter::try_new_extended(config.id)?
}
} else if let Some(mask) = config.mask {
IdFilter::with_mask(config.id, mask)
} else {
IdFilter::try_new(config.id)?
};
chain.add_filter(Box::new(filter));
}
for config in self.range_filters {
let filter = if config.extended {
RangeFilter::try_new_extended(config.start_id, config.end_id)?
} else {
RangeFilter::try_new(config.start_id, config.end_id)?
};
chain.add_filter(Box::new(filter));
}
Ok(chain)
}
}
impl FilterChain {
pub fn from_config(config: &FilterConfig) -> Result<Self, FilterError> {
config.clone().into_chain()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::message::CanMessage;
#[test]
fn test_parse_id_filter() {
let toml = r"
[[id_filters]]
id = 0x123
";
let config: FilterConfig = toml::from_str(toml).unwrap();
assert_eq!(config.id_filters.len(), 1);
assert_eq!(config.id_filters[0].id, 0x123);
}
#[test]
fn test_parse_id_filter_with_mask() {
let toml = r"
[[id_filters]]
id = 0x120
mask = 0x7F0
";
let config: FilterConfig = toml::from_str(toml).unwrap();
assert_eq!(config.id_filters[0].mask, Some(0x7F0));
}
#[test]
fn test_parse_range_filter() {
let toml = r"
[[range_filters]]
start_id = 0x100
end_id = 0x1FF
";
let config: FilterConfig = toml::from_str(toml).unwrap();
assert_eq!(config.range_filters.len(), 1);
assert_eq!(config.range_filters[0].start_id, 0x100);
assert_eq!(config.range_filters[0].end_id, 0x1FF);
}
#[test]
fn test_into_chain() {
let toml = r"
max_hardware_filters = 2
[[id_filters]]
id = 0x123
[[range_filters]]
start_id = 0x200
end_id = 0x2FF
";
let config: FilterConfig = toml::from_str(toml).unwrap();
let chain = config.into_chain().unwrap();
assert_eq!(chain.len(), 2);
let msg_123 = CanMessage::new_standard(0x123, &[0u8; 8]).unwrap();
let msg_250 = CanMessage::new_standard(0x250, &[0u8; 8]).unwrap();
let msg_300 = CanMessage::new_standard(0x300, &[0u8; 8]).unwrap();
assert!(chain.matches(&msg_123));
assert!(chain.matches(&msg_250));
assert!(!chain.matches(&msg_300));
}
#[test]
fn test_extended_filters() {
let toml = r"
[[id_filters]]
id = 0x12345678
extended = true
[[range_filters]]
start_id = 0x10000
end_id = 0x1FFFF
extended = true
";
let config: FilterConfig = toml::from_str(toml).unwrap();
let chain = config.into_chain().unwrap();
let msg_ext = CanMessage::new_extended(0x1234_5678, &[0u8; 8]).unwrap();
let msg_range = CanMessage::new_extended(0x15000, &[0u8; 8]).unwrap();
assert!(chain.matches(&msg_ext));
assert!(chain.matches(&msg_range));
}
}