#![doc = include_str!("../docs/filters.md")]
use thiserror::Error;
pub trait DimensionFilter {
fn accepts(&self, index: usize) -> bool;
}
pub struct ClosureDimensionFilter {
filter: Box<dyn Fn(usize) -> bool>,
}
impl ClosureDimensionFilter {
#[must_use]
pub fn new(filter: impl Fn(usize) -> bool + 'static) -> Self {
Self {
filter: Box::new(filter),
}
}
}
impl DimensionFilter for ClosureDimensionFilter {
fn accepts(&self, index: usize) -> bool {
(self.filter)(index)
}
}
#[derive(PartialEq, Debug, Clone)]
pub struct BoolVecDimensionFilter {
filter: Vec<bool>,
}
#[derive(Debug, Error)]
#[error("The boolean vector's length ({given_dimension}) must match the given dimension ({expected_dimension})")]
pub struct DimensionMismatchError {
given_dimension: usize,
expected_dimension: usize,
}
impl BoolVecDimensionFilter {
pub fn new(filter: Vec<bool>, dimension_size: usize) -> Result<Self, DimensionMismatchError> {
if filter.len() != dimension_size {
return Err(DimensionMismatchError {
given_dimension: filter.len(),
expected_dimension: dimension_size,
});
}
Ok(Self { filter })
}
#[must_use]
pub fn into_boolean_vector(self) -> Vec<bool> {
self.filter
}
}
impl DimensionFilter for BoolVecDimensionFilter {
fn accepts(&self, index: usize) -> bool {
self.filter[index]
}
}
impl AsRef<Vec<bool>> for BoolVecDimensionFilter {
fn as_ref(&self) -> &Vec<bool> {
&self.filter
}
}
#[cfg(test)]
mod tests {
mod closure_filter {
use super::super::ClosureDimensionFilter;
use super::super::DimensionFilter;
use rstest::{fixture, rstest};
#[fixture]
pub fn row_filter_closure() -> ClosureDimensionFilter {
ClosureDimensionFilter {
filter: Box::new(|i| i != 0),
}
}
#[rstest]
fn new(row_filter_closure: ClosureDimensionFilter) {
assert_eq!(
(ClosureDimensionFilter::new(|i| i != 0).filter)(0),
(row_filter_closure.filter)(0)
);
}
#[rstest]
fn accepts(row_filter_closure: ClosureDimensionFilter) {
assert!(!row_filter_closure.accepts(0));
assert!(row_filter_closure.accepts(1));
}
}
mod boolvec_filter {
use super::super::DimensionFilter;
use super::super::{BoolVecDimensionFilter, DimensionMismatchError};
use pretty_assertions::{assert_eq, assert_str_eq};
use rstest::{fixture, rstest};
#[fixture]
fn column_filter_boolvec() -> BoolVecDimensionFilter {
BoolVecDimensionFilter {
filter: vec![true, false, false, true],
}
}
#[rstest]
fn debug(column_filter_boolvec: BoolVecDimensionFilter) {
assert_eq!(
format!("{column_filter_boolvec:?}"),
"BoolVecDimensionFilter { filter: [true, false, false, true] }"
);
}
#[rstest]
fn clone(column_filter_boolvec: BoolVecDimensionFilter) {
assert_eq!(column_filter_boolvec, column_filter_boolvec.clone());
}
#[rstest]
fn new(column_filter_boolvec: BoolVecDimensionFilter) {
assert_eq!(
BoolVecDimensionFilter::new(vec![true, false, false, true], 4).unwrap(),
column_filter_boolvec
);
}
#[rstest]
fn new_dimensions_mismatch() {
match BoolVecDimensionFilter::new(vec![true, false, false], 4) {
Ok(_) => panic!("expected error"),
Err(err) => {
assert!(matches!(
err,
DimensionMismatchError {
given_dimension: 3,
expected_dimension: 4
}
));
assert_str_eq!(
format!("{err:?}"),
"DimensionMismatchError { given_dimension: 3, expected_dimension: 4 }"
);
assert_str_eq!(
format!("{err}"),
"The boolean vector's length (3) must match the given dimension (4)"
);
}
}
}
#[rstest]
fn into_boolean_vector(column_filter_boolvec: BoolVecDimensionFilter) {
assert_eq!(
column_filter_boolvec.into_boolean_vector(),
vec![true, false, false, true]
);
}
#[rstest]
fn as_ref(column_filter_boolvec: BoolVecDimensionFilter) {
assert_eq!(
column_filter_boolvec.as_ref(),
&vec![true, false, false, true]
);
}
#[rstest]
fn accepts(column_filter_boolvec: BoolVecDimensionFilter) {
assert!(column_filter_boolvec.accepts(0));
assert!(!column_filter_boolvec.accepts(1));
assert!(!column_filter_boolvec.accepts(2));
assert!(column_filter_boolvec.accepts(3));
}
}
}