use std::collections;
use std::hash::BuildHasher;
use trailer::{Trailer, TrailerValue};
#[derive(Clone)]
pub enum AccumulationPolicy {
Latest,
List,
}
#[derive(Clone)]
pub enum ValueAccumulator {
Latest(Option<TrailerValue>),
List(Vec<TrailerValue>),
}
impl ValueAccumulator {
pub fn process(&mut self, new_value: TrailerValue) {
match self {
&mut ValueAccumulator::Latest(ref mut value) => if value.is_none() {
*value = Some(new_value);
},
&mut ValueAccumulator::List(ref mut values) => values.push(new_value),
}
}
}
impl From<AccumulationPolicy> for ValueAccumulator {
fn from(policy: AccumulationPolicy) -> Self {
match policy {
AccumulationPolicy::Latest => ValueAccumulator::Latest(None),
AccumulationPolicy::List => ValueAccumulator::List(Vec::new()),
}
}
}
impl IntoIterator for ValueAccumulator {
type Item = TrailerValue;
type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
fn into_iter(self) -> Self::IntoIter {
match self {
ValueAccumulator::Latest(value) => Box::new(value.into_iter()),
ValueAccumulator::List(values) => Box::new(values.into_iter()),
}
}
}
impl Default for ValueAccumulator {
fn default() -> Self {
ValueAccumulator::Latest(None)
}
}
pub trait Accumulator {
fn process(&mut self, trailer: Trailer);
fn process_all<I>(&mut self, iter: I)
where I: IntoIterator<Item = Trailer>
{
for trailer in iter.into_iter() {
self.process(trailer);
}
}
}
pub trait MultiAccumulator {
fn get(&self, key: &str) -> Option<&ValueAccumulator>;
fn get_mut(&mut self, key: &str) -> Option<&mut ValueAccumulator>;
}
impl<M> Accumulator for M
where M: MultiAccumulator
{
fn process(&mut self, trailer: Trailer) {
let (key, value) = trailer.into();
self.get_mut(key.as_ref())
.map(|ref mut acc| acc.process(value));
}
}
impl<S> MultiAccumulator for collections::HashMap<String, ValueAccumulator, S>
where S: BuildHasher
{
fn get(&self, key: &str) -> Option<&ValueAccumulator> {
collections::HashMap::get(self, key)
}
fn get_mut(&mut self, key: &str) -> Option<&mut ValueAccumulator> {
collections::HashMap::get_mut(self, key)
}
}
impl MultiAccumulator for collections::BTreeMap<String, ValueAccumulator> {
fn get(&self, key: &str) -> Option<&ValueAccumulator> {
collections::BTreeMap::get(self, key)
}
fn get_mut(&mut self, key: &str) -> Option<&mut ValueAccumulator> {
collections::BTreeMap::get_mut(self, key)
}
}
pub struct SingleAccumulator {
key: String,
acc: ValueAccumulator,
}
impl SingleAccumulator {
pub fn new(key: String, policy: AccumulationPolicy) -> Self {
SingleAccumulator { key: key, acc: ValueAccumulator::from(policy) }
}
pub fn into_values(self) -> <ValueAccumulator as IntoIterator>::IntoIter {
self.acc.into_iter()
}
}
impl Accumulator for SingleAccumulator {
fn process(&mut self, trailer: Trailer) {
let (key, value) = trailer.into();
if *key.as_ref() == self.key {
self.acc.process(value);
}
}
}
impl Into<(String, ValueAccumulator)> for SingleAccumulator {
fn into(self) -> (String, ValueAccumulator) {
(self.key, self.acc)
}
}
impl Into<ValueAccumulator> for SingleAccumulator {
fn into(self) -> ValueAccumulator {
self.acc
}
}
pub struct SingleKeyTrailerAssemblyIterator<I>
where I: Iterator<Item = TrailerValue>
{
key: String,
inner: I,
}
impl<I> SingleKeyTrailerAssemblyIterator<I>
where I: Iterator<Item = TrailerValue>
{
fn new(key: String, inner: I) -> Self {
SingleKeyTrailerAssemblyIterator { key: key, inner: inner }
}
}
impl<I> Iterator for SingleKeyTrailerAssemblyIterator<I>
where I: Iterator<Item = TrailerValue>
{
type Item = (String, TrailerValue);
fn next(&mut self) -> Option<Self::Item> {
self.inner
.next()
.map(|value| (self.key.clone(), value))
}
}
impl IntoIterator for SingleAccumulator {
type Item = (String, TrailerValue);
type IntoIter = SingleKeyTrailerAssemblyIterator<<ValueAccumulator as IntoIterator>::IntoIter>;
fn into_iter(self) -> Self::IntoIter {
SingleKeyTrailerAssemblyIterator::new(self.key, self.acc.into_iter())
}
}
#[cfg(test)]
mod tests {
use super::*;
use trailer::{Trailer, TrailerValue};
#[test]
fn accumulate_latest() {
let mut acc = ValueAccumulator::from(AccumulationPolicy::Latest);
acc.process(TrailerValue::from_slice("foo-bar"));
acc.process(TrailerValue::from_slice("baz"));
let mut values = acc.into_iter();
assert_eq!(values.next().expect("Could not retrieve value").to_string(), "foo-bar");
assert_eq!(values.next(), None);
}
#[test]
fn accumulate_list() {
let mut acc = ValueAccumulator::from(AccumulationPolicy::List);
acc.process(TrailerValue::from_slice("foo-bar"));
acc.process(TrailerValue::from_slice("baz"));
let mut values = acc.into_iter();
assert_eq!(values.next().expect("Could not retrieve value").to_string(), "foo-bar");
assert_eq!(values.next().expect("Could not retrieve value").to_string(), "baz");
assert_eq!(values.next(), None);
}
#[test]
fn btree_map_accumulator() {
use std::iter::FromIterator;
let val_accs = vec![
(String::from("Assignee"), AccumulationPolicy::Latest),
(String::from("Foo-bar"), AccumulationPolicy::List),
]
.into_iter()
.map(|(k, v)| (k, ValueAccumulator::from(v)));
let mut acc = ::std::collections::BTreeMap::from_iter(val_accs);
acc.process(Trailer::new("Foo-bar", "baz"));
acc.process(Trailer::new("Assignee", "Foo Bar <foo.bar@example.com>"));
acc.process(Trailer::new("Status", "Red alert"));
acc.process(Trailer::new("Foo-bar", "bam"));
acc.process(Trailer::new("Assignee", "Mee Seeks <meeseeks@rm.com>"));
{
let mut vals = acc
.remove(&String::from("Assignee"))
.expect("Could not retrieve value from map")
.into_iter();
assert_eq!(
vals.next().expect("Could not retrieve value from iterator").to_string(),
"Foo Bar <foo.bar@example.com>"
);
assert_eq!(vals.next(), None);
}
{
let mut vals = acc
.remove(&String::from("Foo-bar"))
.expect("Could not retrieve value from map")
.into_iter();
assert_eq!(
vals.next().expect("Could not retrieve value from iterator").to_string(),
"baz"
);
assert_eq!(
vals.next().expect("Could not retrieve value from iterator").to_string(),
"bam"
);
assert_eq!(vals.next(), None);
}
assert!(acc.is_empty());
}
#[test]
fn single_accumulator_latest() {
let mut acc = SingleAccumulator::new(String::from("Foo-bar"), AccumulationPolicy::Latest);
acc.process(Trailer::new("Foo-bar", "baz"));
acc.process(Trailer::new("Assignee", "Foo Bar <foo.bar@example.com>"));
acc.process(Trailer::new("Foo-bar", "bam"));
acc.process(Trailer::new("Assignee", "Mee Seeks <meeseeks@rm.com>"));
let mut vals = acc.into_iter();
assert_eq!(vals.next().expect("Could not retrieve value").1.to_string(), "baz");
assert_eq!(vals.next(), None);
}
#[test]
fn single_accumulator_list() {
let mut acc = SingleAccumulator::new(String::from("Foo-bar"), AccumulationPolicy::List);
acc.process(Trailer::new("Foo-bar", "baz"));
acc.process(Trailer::new("Assignee", "Foo Bar <foo.bar@example.com>"));
acc.process(Trailer::new("Foo-bar", "bam"));
acc.process(Trailer::new("Assignee", "Mee Seeks <meeseeks@rm.com>"));
let mut vals = acc.into_iter();
assert_eq!(vals.next().expect("Could not retrieve value").1.to_string(), "baz");
assert_eq!(vals.next().expect("Could not retrieve value").1.to_string(), "bam");
assert_eq!(vals.next(), None);
}
}