netstack3_base/
matchers.rs1use alloc::string::String;
8use core::fmt::Debug;
9
10use net_types::ip::{IpAddress, Subnet};
11
12use crate::DeviceWithName;
13
14pub trait Matcher<T> {
18 fn matches(&self, actual: &T) -> bool;
20
21 fn required_matches(&self, actual: Option<&T>) -> bool {
23 actual.map_or(false, |actual| self.matches(actual))
24 }
25}
26
27impl<T, O> Matcher<T> for Option<O>
30where
31 O: Matcher<T>,
32{
33 fn matches(&self, actual: &T) -> bool {
34 self.as_ref().map_or(true, |expected| expected.matches(actual))
35 }
36
37 fn required_matches(&self, actual: Option<&T>) -> bool {
38 self.as_ref().map_or(true, |expected| expected.required_matches(actual))
39 }
40}
41
42#[derive(Debug, Copy, Clone, PartialEq, Eq)]
44pub struct SubnetMatcher<A: IpAddress>(pub Subnet<A>);
45
46impl<A: IpAddress> Matcher<A> for SubnetMatcher<A> {
47 fn matches(&self, actual: &A) -> bool {
48 let Self(matcher) = self;
49 matcher.contains(actual)
50 }
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
55pub struct DeviceNameMatcher(pub String);
56
57impl<D: DeviceWithName> Matcher<D> for DeviceNameMatcher {
58 fn matches(&self, actual: &D) -> bool {
59 let Self(name) = self;
60 actual.name_matches(name)
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use alloc::format;
67
68 use ip_test_macro::ip_test;
69 use net_types::ip::Ip;
70
71 use super::*;
72 use crate::testutil::{FakeDeviceId, TestIpExt};
73 use crate::DeviceNameMatcher;
74
75 #[derive(Debug)]
77 struct TrueMatcher;
78
79 impl Matcher<bool> for TrueMatcher {
80 fn matches(&self, actual: &bool) -> bool {
81 *actual
82 }
83 }
84
85 #[test]
86 fn test_optional_matcher_optional_value() {
87 assert!(TrueMatcher.matches(&true));
88 assert!(!TrueMatcher.matches(&false));
89
90 assert!(TrueMatcher.required_matches(Some(&true)));
91 assert!(!TrueMatcher.required_matches(Some(&false)));
92 assert!(!TrueMatcher.required_matches(None));
93
94 assert!(Some(TrueMatcher).matches(&true));
95 assert!(!Some(TrueMatcher).matches(&false));
96 assert!(None::<TrueMatcher>.matches(&true));
97 assert!(None::<TrueMatcher>.matches(&false));
98
99 assert!(Some(TrueMatcher).required_matches(Some(&true)));
100 assert!(!Some(TrueMatcher).required_matches(Some(&false)));
101 assert!(!Some(TrueMatcher).required_matches(None));
102 assert!(None::<TrueMatcher>.required_matches(Some(&true)));
103 assert!(None::<TrueMatcher>.required_matches(Some(&false)));
104 assert!(None::<TrueMatcher>.required_matches(None));
105 }
106
107 #[test]
108 fn device_name_matcher() {
109 let device = FakeDeviceId;
110 let positive_matcher = DeviceNameMatcher(FakeDeviceId::FAKE_NAME.into());
111 let negative_matcher = DeviceNameMatcher(format!("DONTMATCH-{}", FakeDeviceId::FAKE_NAME));
112 assert!(positive_matcher.matches(&device));
113 assert!(!negative_matcher.matches(&device));
114 }
115
116 #[ip_test(I)]
117 fn subnet_matcher<I: Ip + TestIpExt>() {
118 let matcher = SubnetMatcher(I::TEST_ADDRS.subnet);
119 assert!(matcher.matches(&I::TEST_ADDRS.local_ip));
120 assert!(!matcher.matches(&I::get_other_remote_ip_address(1)));
121 }
122}