canlink_hal/filter/
config.rs1use serde::Deserialize;
6
7use crate::error::FilterError;
8
9use super::{FilterChain, IdFilter, RangeFilter};
10
11#[derive(Debug, Clone, Default, Deserialize)]
27pub struct FilterConfig {
28 #[serde(default)]
30 pub id_filters: Vec<IdFilterConfig>,
31
32 #[serde(default)]
34 pub range_filters: Vec<RangeFilterConfig>,
35
36 #[serde(default = "default_max_hardware")]
38 pub max_hardware_filters: usize,
39}
40
41fn default_max_hardware() -> usize {
42 4
43}
44
45#[derive(Debug, Clone, Deserialize)]
47pub struct IdFilterConfig {
48 pub id: u32,
50
51 #[serde(default)]
53 pub mask: Option<u32>,
54
55 #[serde(default)]
57 pub extended: bool,
58}
59
60#[derive(Debug, Clone, Deserialize)]
62pub struct RangeFilterConfig {
63 pub start_id: u32,
65
66 pub end_id: u32,
68
69 #[serde(default)]
71 pub extended: bool,
72}
73
74impl FilterConfig {
75 pub fn from_toml(toml_str: &str) -> Result<Self, toml::de::Error> {
81 toml::from_str(toml_str)
82 }
83
84 pub fn into_chain(self) -> Result<FilterChain, FilterError> {
90 let mut chain = FilterChain::new(self.max_hardware_filters);
91
92 for config in self.id_filters {
94 let filter = if config.extended {
95 if let Some(mask) = config.mask {
96 IdFilter::with_mask_extended(config.id, mask)
97 } else {
98 IdFilter::try_new_extended(config.id)?
99 }
100 } else if let Some(mask) = config.mask {
101 IdFilter::with_mask(config.id, mask)
102 } else {
103 IdFilter::try_new(config.id)?
104 };
105 chain.add_filter(Box::new(filter));
106 }
107
108 for config in self.range_filters {
110 let filter = if config.extended {
111 RangeFilter::try_new_extended(config.start_id, config.end_id)?
112 } else {
113 RangeFilter::try_new(config.start_id, config.end_id)?
114 };
115 chain.add_filter(Box::new(filter));
116 }
117
118 Ok(chain)
119 }
120}
121
122impl FilterChain {
123 pub fn from_config(config: &FilterConfig) -> Result<Self, FilterError> {
129 config.clone().into_chain()
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use crate::message::CanMessage;
137
138 #[test]
139 fn test_parse_id_filter() {
140 let toml = r"
141 [[id_filters]]
142 id = 0x123
143 ";
144
145 let config: FilterConfig = toml::from_str(toml).unwrap();
146 assert_eq!(config.id_filters.len(), 1);
147 assert_eq!(config.id_filters[0].id, 0x123);
148 }
149
150 #[test]
151 fn test_parse_id_filter_with_mask() {
152 let toml = r"
153 [[id_filters]]
154 id = 0x120
155 mask = 0x7F0
156 ";
157
158 let config: FilterConfig = toml::from_str(toml).unwrap();
159 assert_eq!(config.id_filters[0].mask, Some(0x7F0));
160 }
161
162 #[test]
163 fn test_parse_range_filter() {
164 let toml = r"
165 [[range_filters]]
166 start_id = 0x100
167 end_id = 0x1FF
168 ";
169
170 let config: FilterConfig = toml::from_str(toml).unwrap();
171 assert_eq!(config.range_filters.len(), 1);
172 assert_eq!(config.range_filters[0].start_id, 0x100);
173 assert_eq!(config.range_filters[0].end_id, 0x1FF);
174 }
175
176 #[test]
177 fn test_into_chain() {
178 let toml = r"
179 max_hardware_filters = 2
180
181 [[id_filters]]
182 id = 0x123
183
184 [[range_filters]]
185 start_id = 0x200
186 end_id = 0x2FF
187 ";
188
189 let config: FilterConfig = toml::from_str(toml).unwrap();
190 let chain = config.into_chain().unwrap();
191
192 assert_eq!(chain.len(), 2);
193
194 let msg_123 = CanMessage::new_standard(0x123, &[0u8; 8]).unwrap();
195 let msg_250 = CanMessage::new_standard(0x250, &[0u8; 8]).unwrap();
196 let msg_300 = CanMessage::new_standard(0x300, &[0u8; 8]).unwrap();
197
198 assert!(chain.matches(&msg_123));
199 assert!(chain.matches(&msg_250));
200 assert!(!chain.matches(&msg_300));
201 }
202
203 #[test]
204 fn test_extended_filters() {
205 let toml = r"
206 [[id_filters]]
207 id = 0x12345678
208 extended = true
209
210 [[range_filters]]
211 start_id = 0x10000
212 end_id = 0x1FFFF
213 extended = true
214 ";
215
216 let config: FilterConfig = toml::from_str(toml).unwrap();
217 let chain = config.into_chain().unwrap();
218
219 let msg_ext = CanMessage::new_extended(0x1234_5678, &[0u8; 8]).unwrap();
220 let msg_range = CanMessage::new_extended(0x15000, &[0u8; 8]).unwrap();
221
222 assert!(chain.matches(&msg_ext));
223 assert!(chain.matches(&msg_range));
224 }
225}