use std::collections::HashSet;
use regex::Regex;
use super::{AdapterKind, AdapterSnapshot, AddressFetcher, FetchError};
pub trait AdapterFilter: Send + Sync {
fn matches(&self, adapter: &AdapterSnapshot) -> bool;
}
#[derive(Debug, Clone)]
pub struct KindFilter {
kinds: HashSet<AdapterKind>,
}
impl KindFilter {
#[must_use]
pub fn new(kinds: impl IntoIterator<Item = AdapterKind>) -> Self {
Self {
kinds: kinds.into_iter().collect(),
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.kinds.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.kinds.len()
}
#[must_use]
#[allow(clippy::missing_const_for_fn)] pub fn kinds(&self) -> &HashSet<AdapterKind> {
&self.kinds
}
}
impl AdapterFilter for KindFilter {
fn matches(&self, adapter: &AdapterSnapshot) -> bool {
self.kinds.contains(&adapter.kind)
}
}
#[derive(Default)]
pub struct FilterChain {
includes: Vec<Box<dyn AdapterFilter>>,
excludes: Vec<Box<dyn AdapterFilter>>,
}
impl FilterChain {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn include<F: AdapterFilter + 'static>(mut self, filter: F) -> Self {
self.includes.push(Box::new(filter));
self
}
#[must_use]
pub fn exclude<F: AdapterFilter + 'static>(mut self, filter: F) -> Self {
self.excludes.push(Box::new(filter));
self
}
#[must_use]
pub fn include_count(&self) -> usize {
self.includes.len()
}
#[must_use]
pub fn exclude_count(&self) -> usize {
self.excludes.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.includes.is_empty() && self.excludes.is_empty()
}
}
impl AdapterFilter for FilterChain {
fn matches(&self, adapter: &AdapterSnapshot) -> bool {
if self.excludes.iter().any(|f| f.matches(adapter)) {
return false;
}
self.includes.is_empty() || self.includes.iter().any(|f| f.matches(adapter))
}
}
impl std::fmt::Debug for FilterChain {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FilterChain")
.field("include_count", &self.includes.len())
.field("exclude_count", &self.excludes.len())
.finish()
}
}
#[derive(Debug)]
pub struct NameRegexFilter {
pattern: Regex,
}
impl NameRegexFilter {
pub fn new(pattern: &str) -> Result<Self, regex::Error> {
Ok(Self {
pattern: Regex::new(pattern)?,
})
}
#[must_use]
#[allow(clippy::missing_const_for_fn)] pub fn pattern(&self) -> &Regex {
&self.pattern
}
}
impl AdapterFilter for NameRegexFilter {
fn matches(&self, adapter: &AdapterSnapshot) -> bool {
self.pattern.is_match(&adapter.name)
}
}
#[derive(Debug)]
pub struct FilteredFetcher<F, A> {
inner: F,
filter: A,
}
impl<F, A> FilteredFetcher<F, A> {
#[must_use]
pub const fn new(inner: F, filter: A) -> Self {
Self { inner, filter }
}
pub const fn inner(&self) -> &F {
&self.inner
}
pub const fn filter(&self) -> &A {
&self.filter
}
pub fn into_inner(self) -> F {
self.inner
}
}
impl<F: AddressFetcher, A: AdapterFilter> AddressFetcher for FilteredFetcher<F, A> {
fn fetch(&self) -> Result<Vec<AdapterSnapshot>, FetchError> {
let snapshots = self.inner.fetch()?;
Ok(snapshots
.into_iter()
.filter(|adapter| self.filter.matches(adapter))
.collect())
}
}
impl<T: AdapterFilter + ?Sized> AdapterFilter for &T {
fn matches(&self, adapter: &AdapterSnapshot) -> bool {
(*self).matches(adapter)
}
}
impl AdapterFilter for Box<dyn AdapterFilter> {
fn matches(&self, adapter: &AdapterSnapshot) -> bool {
self.as_ref().matches(adapter)
}
}