diff --git a/src/filter.rs b/src/filter.rs new file mode 100644 index 0000000..fb5f1ef --- /dev/null +++ b/src/filter.rs @@ -0,0 +1,75 @@ +use std::{ + collections::HashMap, + io::{Read, Write}, + str::FromStr, +}; + +pub enum Comparator { + EQUAL(T), + NOT_EQUAL(T), + GREATER_THAN(T), + LESS_THAN(T), + IN(Vec), + NOT_IN(Vec), +} + +impl Comparator { + pub fn is_valid(&self, value: T) -> bool { + match self { + Comparator::EQUAL(v) => value == *v, + Comparator::NOT_EQUAL(v) => value != *v, + Comparator::GREATER_THAN(v) => value > *v, + Comparator::LESS_THAN(v) => value < *v, + Comparator::IN(v) => v.contains(&value), + Comparator::NOT_IN(v) => !v.contains(&value), + } + } +} + +pub trait FieldName { + // Name of the field this validator should work on + fn get_field_name(&self) -> String; +} + +pub trait DataValidator: FieldName { + // Whether the given value is valid for the validator + fn is_valid(&self, s: &String) -> bool; +} + +pub struct FilterRule { + column_name: String, + comparator: Comparator, +} + +impl FieldName for FilterRule { + fn get_field_name(&self) -> String { + self.column_name.clone() + } +} + +impl DataValidator for FilterRule { + fn is_valid(&self, s: &String) -> bool { + s.parse().map_or(false, |f| self.comparator.is_valid(f)) + } +} + +/** + * Write all lines from the input file to the output file, skipping records + * that don't satisfy the filter criteria + */ +pub fn filter_file( + rules: Vec<&dyn DataValidator>, + input: &mut csv::Reader, + output: &mut csv::Writer, +) -> anyhow::Result<()> { + for line in input.deserialize() { + let line: HashMap = line?; + if rules.iter().all(|rule| { + line.get(&rule.get_field_name()) + .map_or(true, |value| rule.is_valid(value)) + }) { + output.serialize(line)?; + } + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 5663f51..50bd3d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,8 @@ pub use self::shared_models::*; pub mod link; +pub mod filter; + #[no_mangle] pub extern "C" fn move_money_from_text( rules: *const c_char,