diff --git a/Cargo.toml b/Cargo.toml index cdfe1c3..eae6d83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ maintenance = { status = "actively-developed" } [[example]] name = "calc" -required-features = ["structs"] +required-features = ["structs", "aggs"] [[example]] name = "dotenv" diff --git a/examples/calc.rs b/examples/calc.rs index 2799242..cdd7292 100644 --- a/examples/calc.rs +++ b/examples/calc.rs @@ -1,14 +1,12 @@ -use estring::{EString, SepVec}; +use estring::{Aggregate, EString, Product, SepVec, Sum}; type PlusVec = SepVec; type MulVec = SepVec; fn main() -> estring::Result<()> { let res = EString::from("10+5*2+3") - .parse::>>()? - .iter() - .map(|m| m.iter().product::()) - .sum::(); + .parse::>>>>()? + .agg(); assert_eq!(res, 23.0); Ok(()) diff --git a/src/agg.rs b/src/agg.rs index ec7c695..36f0072 100644 --- a/src/agg.rs +++ b/src/agg.rs @@ -1,6 +1,8 @@ //! This module will contain aggregate functions (Sum, Product, etc) //! +mod product; mod sum; +pub use product::*; pub use sum::*; diff --git a/src/agg/product.rs b/src/agg/product.rs new file mode 100644 index 0000000..0ac49e1 --- /dev/null +++ b/src/agg/product.rs @@ -0,0 +1,92 @@ +use crate::{Aggregatable, Aggregate, EString, ParseFragment}; + +/// Aggregate struct, that can multiply inner aggregatable [items](Aggregatable::items) if +/// [``Aggregatable::Item``] implements [``std::iter::Product``](std::iter::Product) +/// +/// # Examples +/// +/// ```rust +/// use estring::{Aggregate, EString, SepVec, Product}; +/// let res = EString::from("1*2*3*4") +/// .parse::>>() +/// .unwrap() +/// .agg(); +/// assert_eq!(res, 24); +/// ``` +#[derive(Debug, PartialEq, Eq)] +pub struct Product(pub T); + +impl ParseFragment for Product +where + T: ParseFragment, +{ + fn parse_frag(es: EString) -> crate::Result { + T::parse_frag(es).map(Self) + } +} + +impl Aggregate for Product +where + R: std::iter::Product, + T: Aggregatable, +{ + type Target = R; + + fn agg(self) -> Self::Target { + self.0.items().into_iter().product() + } +} + +impl Aggregatable for Product +where + R: std::iter::Product, + T: Aggregatable, +{ + type Item = R; + + fn items(self) -> Vec { + vec![self.agg()] + } +} + +#[cfg(test)] +mod tests { + use crate::SepVec; + + use super::*; + + type CommaVec = SepVec; + type MulVec = SepVec; + + #[test] + fn should_parse_vec() { + let es = EString::from("1,2,3"); + match es.parse::>>() { + Ok(res) => assert_eq!(res, Product(CommaVec::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_vector() { + let es = EString::from("1*2,2,3"); + let expr = es.parse::>>>().unwrap(); + assert_eq!(expr.agg(), 12); + } + + #[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(), 12); + } +} diff --git a/src/agg/sum.rs b/src/agg/sum.rs index f7f0dea..aab4b0d 100644 --- a/src/agg/sum.rs +++ b/src/agg/sum.rs @@ -1,26 +1,31 @@ -use std::marker::PhantomData; - use crate::{Aggregatable, Aggregate, EString, ParseFragment}; +/// Aggregate struct, that can sum inner aggregatable [items](Aggregatable::items) if +/// [``Aggregatable::Item``] implements [``std::iter::Sum``](std::iter::Sum) +/// +/// # Examples +/// +/// ```rust +/// use estring::{Aggregate, EString, SepVec, Sum}; +/// let res = EString::from("1+2+3+4") +/// .parse::>>() +/// .unwrap() +/// .agg(); +/// assert_eq!(res, 10); +/// ``` #[derive(Debug, PartialEq, Eq)] -struct Sum(T, PhantomData); +pub struct Sum(pub T); -impl Sum { - fn new(inner: T) -> Self { - Self(inner, PhantomData::default()) - } -} - -impl ParseFragment for Sum +impl ParseFragment for Sum where T: ParseFragment, { fn parse_frag(es: EString) -> crate::Result { - T::parse_frag(es).map(Self::new) + T::parse_frag(es).map(Self) } } -impl Aggregate for Sum +impl Aggregate for Sum where R: std::iter::Sum, T: Aggregatable, @@ -32,7 +37,7 @@ where } } -impl Aggregatable for Sum +impl Aggregatable for Sum where R: std::iter::Sum, T: Aggregatable, @@ -56,8 +61,8 @@ mod tests { #[test] fn should_parse_vec() { let es = EString::from("1,2,3"); - match es.parse::>>() { - Ok(res) => assert_eq!(res, Sum::new(CommaVec::from(vec![1, 2, 3]))), + match es.parse::>>() { + Ok(res) => assert_eq!(res, Sum(CommaVec::from(vec![1, 2, 3]))), _ => unreachable!(), } } @@ -65,23 +70,21 @@ mod tests { #[test] fn should_aggregate_vector() { let es = EString::from("1,2,3"); - let expr = es.parse::>>().unwrap(); + let expr = es.parse::>>().unwrap(); assert_eq!(expr.agg(), 6); } #[test] fn should_aggregate_vector_with_inner_vector() { let es = EString::from("1+2,2,3"); - let expr = es.parse::>>>().unwrap(); + let expr = es.parse::>>>().unwrap(); assert_eq!(expr.agg(), 8); } #[test] fn should_aggregate_vector_with_inner_aggregation() { let es = EString::from("1+2,2,3"); - let expr = es - .parse::>>>>() - .unwrap(); + let expr = es.parse::>>>>().unwrap(); assert_eq!(expr.agg(), 8); } }