From c4b68acb5448854e83df318c0b150a34f693b23f Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Thu, 28 Jul 2022 12:19:56 +0300 Subject: [PATCH] agg: add aggregatable trait --- src/agg/sum.rs | 56 ++++++++++++++++++++++++++---------------- src/core.rs | 32 +++++++++++++++++++++++- src/std/bool.rs | 10 +++++++- src/std/number.rs | 10 +++++++- src/std/option.rs | 13 +++++++++- src/structs/sep_vec.rs | 34 ++++++++++++++++++++----- 6 files changed, 124 insertions(+), 31 deletions(-) diff --git a/src/agg/sum.rs b/src/agg/sum.rs index 8ca61b4..0318665 100644 --- a/src/agg/sum.rs +++ b/src/agg/sum.rs @@ -1,18 +1,11 @@ use std::marker::PhantomData; -use crate::{Aggregate, EString, ParseFragment}; +use crate::{Aggregate, Aggregateble, EString, ParseFragment}; #[derive(Debug, PartialEq, Eq)] -struct Sum(T, PhantomData) -where - R: Copy + std::iter::Sum, - T: ParseFragment + std::ops::Deref>; +struct Sum(T, PhantomData); -impl Sum -where - R: Copy + std::iter::Sum, - T: ParseFragment + std::ops::Deref>, -{ +impl Sum { fn new(inner: T) -> Self { Self(inner, PhantomData::default()) } @@ -20,8 +13,7 @@ where impl ParseFragment for Sum where - R: Copy + std::iter::Sum, - T: ParseFragment + std::ops::Deref>, + T: ParseFragment, { fn parse_frag(es: EString) -> crate::Result { T::parse_frag(es).map(Self::new) @@ -30,13 +22,25 @@ where impl Aggregate for Sum where - R: Copy + std::iter::Sum, - T: ParseFragment + std::ops::Deref>, + R: std::iter::Sum, + T: Aggregateble, { type Target = R; - fn agg(&self) -> Self::Target { - self.0.iter().copied().sum() + fn agg(self) -> Self::Target { + self.0.items().into_iter().sum() + } +} + +impl Aggregateble for Sum +where + R: std::iter::Sum, + T: Aggregateble, +{ + type Item = R; + + fn items(self) -> Vec { + vec![self.agg()] } } @@ -46,11 +50,14 @@ mod tests { use super::*; + type CommaVec = SepVec; + type PlusVec = SepVec; + #[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]))), + match es.parse::>>() { + Ok(res) => assert_eq!(res, Sum::new(CommaVec::from(vec![1, 2, 3]))), _ => unreachable!(), } } @@ -58,16 +65,23 @@ 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(); + 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::>, ','>>>() + .parse::>>>>() .unwrap(); - assert_eq!(expr.agg(), 6); + assert_eq!(expr.agg(), 8); } } diff --git a/src/core.rs b/src/core.rs index 7fbdef0..189b6c4 100644 --- a/src/core.rs +++ b/src/core.rs @@ -120,7 +120,13 @@ pub trait Aggregate { type Target: ?Sized; /// Aggregates the value. - fn agg(&self) -> Self::Target; + fn agg(self) -> Self::Target; +} + +pub trait Aggregateble { + type Item; + + fn items(self) -> Vec; } /// Wrapper under ``String`` type. @@ -230,6 +236,14 @@ impl ParseFragment for EString { } } +impl Aggregateble for EString { + type Item = Self; + + fn items(self) -> Vec { + vec![self] + } +} + impl ParseFragment for String { #[inline] fn parse_frag(es: EString) -> crate::Result { @@ -244,6 +258,14 @@ impl ToEString for String { } } +impl Aggregateble for String { + type Item = Self; + + fn items(self) -> Vec { + vec![self] + } +} + impl ParseFragment for &'static str { #[inline] fn parse_frag(es: EString) -> crate::Result { @@ -258,6 +280,14 @@ impl<'a> ToEString for &'a str { } } +impl<'a> Aggregateble for &'a str { + type Item = Self; + + fn items(self) -> Vec { + vec![self] + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/std/bool.rs b/src/std/bool.rs index b041acd..fe02c7f 100644 --- a/src/std/bool.rs +++ b/src/std/bool.rs @@ -1,4 +1,4 @@ -use crate::core::{EString, ParseFragment, ToEString}; +use crate::core::{Aggregateble, EString, ParseFragment, ToEString}; use crate::error::{Error, Reason}; impl ParseFragment for bool { @@ -19,6 +19,14 @@ impl ToEString for bool { } } +impl Aggregateble for bool { + type Item = Self; + + fn items(self) -> Vec { + vec![self] + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/std/number.rs b/src/std/number.rs index 44d2f0e..4c6a982 100644 --- a/src/std/number.rs +++ b/src/std/number.rs @@ -1,4 +1,4 @@ -use crate::core::{EString, ParseFragment, ToEString}; +use crate::core::{Aggregateble, EString, ParseFragment, ToEString}; use crate::error::{Error, Reason}; #[doc(hidden)] @@ -18,6 +18,14 @@ macro_rules! from_env_string_numbers_impl { EString(self.to_string()) } } + + impl Aggregateble for $ty { + type Item = Self; + + fn items(self) -> Vec { + vec![self] + } + } )+ }; } diff --git a/src/std/option.rs b/src/std/option.rs index 191ad39..bea98b5 100644 --- a/src/std/option.rs +++ b/src/std/option.rs @@ -1,4 +1,4 @@ -use crate::core::{EString, ParseFragment, ToEString}; +use crate::core::{Aggregateble, EString, ParseFragment, ToEString}; impl ToEString for Option where @@ -25,6 +25,17 @@ where } } +impl Aggregateble for Option +where + T: Aggregateble, +{ + type Item = T::Item; + + fn items(self) -> Vec { + self.map(T::items).unwrap_or_default() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/structs/sep_vec.rs b/src/structs/sep_vec.rs index 1208642..59c0eb7 100644 --- a/src/structs/sep_vec.rs +++ b/src/structs/sep_vec.rs @@ -1,7 +1,7 @@ //! Contains the implementations to vec type //! -use crate::core::{EString, ParseFragment, ToEString}; +use crate::core::{Aggregateble, EString, ParseFragment, ToEString}; use std::fmt::Write; /// Wrapper for ``Vec`` to split string by a separator (`SEP`). @@ -92,17 +92,25 @@ where } } +impl Aggregateble for SepVec +where + T: Aggregateble, +{ + type Item = T::Item; + + fn items(self) -> Vec { + self.0.into_iter().flat_map(T::items).collect() + } +} + #[cfg(test)] mod tests { use super::*; use crate::Pair; use crate::{Error, Reason}; - const COMMA: char = ','; - const SEMI: char = ';'; - - type CommaVec = SepVec; - type SemiVec = SepVec; + type CommaVec = SepVec; + type SemiVec = SepVec; #[test] fn should_parse_into_vec() { @@ -171,4 +179,18 @@ d,e"; let vec = SepVec::<_, ','>::from(vec![PlusPair::from((1, 2)), PlusPair::from((3, 4))]); assert_eq!(vec.to_estring(), EString(String::from("1+2,3+4"))); } + + #[test] + fn should_returns_aggregatable_items() { + let estr = EString::from("1,2,3,4,5"); + let res = estr.parse::>().unwrap(); + assert_eq!(res.items(), vec![1, 2, 3, 4, 5]); + } + + #[test] + fn should_returns_flatten_aggregatable_items() { + let estr = EString::from("1,2; 3,4,5; 6,7"); + let res = estr.parse::>>().unwrap(); + assert_eq!(res.items(), vec![1, 2, 3, 4, 5, 6, 7]); + } }