diff --git a/Cargo.lock b/Cargo.lock index 0356f2f..a4efb43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "estring" -version = "0.1.2" - +version = "0.2.1" diff --git a/src/agg.rs b/src/agg.rs index 2d6b12f..ec7c695 100644 --- a/src/agg.rs +++ b/src/agg.rs @@ -1,2 +1,6 @@ //! This module will contain aggregate functions (Sum, Product, etc) //! + +mod sum; + +pub use sum::*; diff --git a/src/agg/sum.rs b/src/agg/sum.rs new file mode 100644 index 0000000..8ca61b4 --- /dev/null +++ b/src/agg/sum.rs @@ -0,0 +1,73 @@ +use std::marker::PhantomData; + +use crate::{Aggregate, EString, ParseFragment}; + +#[derive(Debug, PartialEq, Eq)] +struct Sum(T, PhantomData) +where + R: Copy + std::iter::Sum, + T: ParseFragment + std::ops::Deref>; + +impl Sum +where + R: Copy + std::iter::Sum, + T: ParseFragment + std::ops::Deref>, +{ + fn new(inner: T) -> Self { + Self(inner, PhantomData::default()) + } +} + +impl ParseFragment for Sum +where + R: Copy + std::iter::Sum, + T: ParseFragment + std::ops::Deref>, +{ + fn parse_frag(es: EString) -> crate::Result { + T::parse_frag(es).map(Self::new) + } +} + +impl Aggregate for Sum +where + R: Copy + std::iter::Sum, + T: ParseFragment + std::ops::Deref>, +{ + type Target = R; + + fn agg(&self) -> Self::Target { + self.0.iter().copied().sum() + } +} + +#[cfg(test)] +mod tests { + use crate::SepVec; + + use super::*; + + #[test] + fn should_parse_vec() { + let es = EString::from("1,2,3"); + match es.parse::>>() { + Ok(res) => assert_eq!(res, Sum::new(SepVec::from(vec![1, 2, 3]))), + _ => unreachable!(), + } + } + + #[test] + fn should_aggregate_vector() { + let es = EString::from("1,2,3"); + let expr = es.parse::>>().unwrap(); + assert_eq!(expr.agg(), 6); + } + + #[test] + fn should_aggregate_vector_with_inner_aggregation() { + let es = EString::from("1+2,2,3"); + let expr = es + .parse::>, ','>>>() + .unwrap(); + assert_eq!(expr.agg(), 6); + } +} diff --git a/src/core.rs b/src/core.rs index d47fa93..7fbdef0 100644 --- a/src/core.rs +++ b/src/core.rs @@ -110,6 +110,19 @@ pub trait ParseFragment: Sized { fn parse_frag(es: EString) -> crate::Result; } +// TODO: add example +/// Trait to represent structures that can act as aggregators. +/// +/// The `agg` method works with data that has already been parsed. For this reason, **this trait +/// should never fail**. +pub trait Aggregate { + /// The resulting type after aggregation. + type Target: ?Sized; + + /// Aggregates the value. + fn agg(&self) -> Self::Target; +} + /// Wrapper under ``String`` type. /// /// # Examples