use crate::*;
use std::collections::HashMap;
pub struct MailBox<M> {
store: Store<M>,
init: bool,
id_factory: MessageIdFactory,
cases: Vec<Box<dyn CaseHandler<M>>>,
}
impl<M> Default for MailBox<M> {
fn default() -> Self {
MailBox {
store: HashMap::new(),
init: false,
id_factory: MessageIdFactory::new(),
cases: Vec::new(),
}
}
}
impl<M> MailBox<M> {
pub fn is_initialized(&self) -> bool {
self.init
}
pub fn is_modified(&self) -> bool {
!self.cases.is_empty() || !self.store.is_empty()
}
pub fn init(&mut self) {
self.init = true;
}
pub fn add_case(&mut self, case: Box<dyn CaseHandler<M>>) {
if !self.init {
self.cases.push(case);
} else {
panic!("Mailbox must not be modifed");
}
}
fn remove_message_ids_from_cases(&mut self, messages: &MatchedIds) {
for case in &mut self.cases {
case.remove(messages, &self.store);
}
}
pub fn process(&mut self, message: M) -> Option<MatchedMessages<M>> {
let message_id = self.id_factory.next();
self.store.insert(message_id, message);
let matches: Vec<CaseMatch> = self
.cases
.iter_mut()
.map(|case| case.consume(message_id, &self.store))
.enumerate()
.filter_map(|e| match e {
(id, Some(matched_ids)) => Some(CaseMatch::new(CaseId(id), matched_ids)),
_ => None,
})
.collect();
let fairest_match = CaseMatch::get_fairest(matches)?;
self.remove_message_ids_from_cases(&fairest_match.messages);
Some(fairest_match.to_messages(&mut self.store))
}
pub fn unmatched_messages(&self) -> Vec<MessageId> {
self.store.keys().cloned().collect()
}
pub fn extract(&mut self) -> Vec<M> {
let all_ids = MatchedIds(self.unmatched_messages());
self.remove_message_ids_from_cases(&all_ids);
let mut messages: Vec<(MessageId, M)> = self.store.drain().collect();
messages.sort_by_key(|e| e.0);
messages.into_iter().map(|(_, v)| v).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
type TestMessage = String;
struct MockCase0;
impl CaseHandler<TestMessage> for MockCase0 {
fn consume(&mut self, id: MessageId, _store: &Store<TestMessage>) -> Option<MatchedIds> {
if id == id!(6) {
Some(MatchedIds::new(vec![id!(5), id!(6), id!(2)]))
} else {
None
}
}
fn remove(&mut self, _messages: &MatchedIds, _store: &Store<TestMessage>) {}
fn is_empty(&self) -> bool {
unimplemented!();
}
}
struct MockCase1;
impl CaseHandler<TestMessage> for MockCase1 {
fn consume(&mut self, id: MessageId, _store: &Store<TestMessage>) -> Option<MatchedIds> {
if id == id!(6) {
Some(MatchedIds::new(vec![id!(6), id!(4), id!(2), id!(5)]))
} else {
None
}
}
fn remove(&mut self, _messages: &MatchedIds, _store: &Store<TestMessage>) {}
fn is_empty(&self) -> bool {
unimplemented!();
}
}
#[test]
fn test_mailbox_init() {
let mut mailbox = MailBox::<TestMessage>::default();
assert!(!mailbox.is_initialized());
assert!(!mailbox.is_modified());
mailbox.add_case(Box::new(MockCase0));
assert!(mailbox.is_modified());
mailbox.add_case(Box::new(MockCase1));
mailbox.init();
assert!(mailbox.is_initialized());
}
#[test]
fn test_mailbox_process() {
let mut mailbox = MailBox::<TestMessage>::default();
mailbox.add_case(Box::new(MockCase0));
mailbox.add_case(Box::new(MockCase1));
mailbox.init();
for msg in ["1", "2", "3", "4", "5"] {
assert!(mailbox.process(msg.into()).is_none());
}
let result = mailbox.process("6".into()).unwrap();
assert_eq!(&CaseId(1), result.case_id());
assert_eq!(
(
String::from("6"),
String::from("4"),
String::from("2"),
String::from("5")
),
result.into_4()
);
let remaining = mailbox.unmatched_messages();
assert_eq!(remaining.len(), 2);
assert!(remaining.contains(&id!(1)));
assert!(!remaining.contains(&id!(2)));
assert!(remaining.contains(&id!(3)));
assert!(!remaining.contains(&id!(4)));
assert!(!remaining.contains(&id!(5)));
assert!(!remaining.contains(&id!(6)));
}
}