diff --git a/src/filter.rs b/src/filter.rs index fb5f1ef..a5d3b9c 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -4,6 +4,8 @@ use std::{ str::FromStr, }; +use crate::io::RecordSerializer; + pub enum Comparator { EQUAL(T), NOT_EQUAL(T), @@ -59,14 +61,20 @@ impl DataValidator for FilterRule { */ pub fn filter_file( rules: Vec<&dyn DataValidator>, + // TODO: Custom serialisers/deserialisers so we don't rely on csv only input: &mut csv::Reader, - output: &mut csv::Writer, + output: &mut impl RecordSerializer, ) -> 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)) + line.get(&rule.get_field_name()).map_or(true, |value| { + if value.trim().is_empty() { + true + } else { + rule.is_valid(value) + } + }) }) { output.serialize(line)?; } diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000..36d422c --- /dev/null +++ b/src/io.rs @@ -0,0 +1,53 @@ +use std::{ + collections::HashMap, + io::{Read, Write}, +}; + +use anyhow::bail; +use csv::DeserializeRecordsIter; +use rmp_serde::{Deserializer, Serializer}; +use serde::{de::DeserializeOwned, Serialize}; + +pub trait RecordSerializer { + fn serialize(&mut self, record: impl Serialize) -> anyhow::Result<()>; +} + +impl RecordSerializer for csv::Writer { + fn serialize(&mut self, record: impl Serialize) -> anyhow::Result<()> { + self.serialize(record)?; + Ok(()) + } +} + +impl RecordSerializer for Serializer { + fn serialize(&mut self, record: impl Serialize) -> anyhow::Result<()> { + record.serialize(self)?; + Ok(()) + } +} + +// TODO: Want to be able to deserialise with serde over a reader like we currently do with the writer +// pub trait RecordDeserializer>> { +// fn deserialize(&mut self) -> I; +// } + +// impl>> +// RecordDeserializer for csv::Reader +// { +// fn deserialize(&mut self) -> I { +// self.deserialize().into_iter().map(|r| match r { +// Ok(ok) => Ok(ok), +// Err(err) => bail!(err), +// }) +// } +// } + +// impl RecordDeserializer for Deserializer { +// fn deserialize(&mut self) -> I +// where +// D: DeserializeOwned, +// I: Iterator, +// { +// todo!() +// } +// } diff --git a/src/lib.rs b/src/lib.rs index 50bd3d6..b39646d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,8 @@ pub mod link; pub mod filter; +mod io; + #[no_mangle] pub extern "C" fn move_money_from_text( rules: *const c_char, diff --git a/src/overhead_allocation.rs b/src/overhead_allocation.rs index 4ec3209..129b003 100644 --- a/src/overhead_allocation.rs +++ b/src/overhead_allocation.rs @@ -1,16 +1,14 @@ use std::{ collections::{HashMap, HashSet}, - io::{Read, Write}, + io::Read, }; -use csv::Writer; use itertools::Itertools; use nalgebra::{DMatrix, Dynamic, LU}; use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; -use rmp_serde::Serializer; use serde::{Deserialize, Serialize}; -use crate::{CsvAccount, CsvCost}; +use crate::{io::RecordSerializer, CsvAccount, CsvCost}; #[derive(Debug, PartialEq, Eq)] pub enum DepartmentType { @@ -769,24 +767,6 @@ fn solve_reciprocal_with_from( Ok(()) } -pub trait RecordSerializer { - fn serialize(&mut self, record: impl Serialize) -> anyhow::Result<()>; -} - -impl RecordSerializer for csv::Writer { - fn serialize(&mut self, record: impl Serialize) -> anyhow::Result<()> { - self.serialize(record)?; - Ok(()) - } -} - -impl RecordSerializer for Serializer { - fn serialize(&mut self, record: impl Serialize) -> anyhow::Result<()> { - record.serialize(self)?; - Ok(()) - } -} - #[cfg(test)] mod tests {