-
-
Save hynek/7e1ccf3d8dfc8b93da73f8c900398bce to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::collections::HashMap; | |
use std::fmt; | |
use failure::Error; | |
pub struct PhoneBook { | |
/// Name → dict of service → contact string. | |
book: HashMap<String, HashMap<String, String>>, | |
notifiers: Vec<Box<dyn Notifier>>, | |
} | |
impl PhoneBook { | |
/// Iterator of notifiers for name, sorted by preferability. | |
fn notifiers_iter(&self, name: &str) -> impl Iterator<Item = LePoke> { | |
let contacts = self.book.get(name).expect("missing contact").clone(); | |
self.notifiers.iter().filter_map(move |notifier| { | |
if let Some(contact) = contacts.get(¬ifier.get_name()) { | |
Some(LePoke { | |
notifier: notifier, | |
contact: contact.clone(), | |
}) | |
} else { | |
None | |
} | |
}) | |
} | |
} | |
pub trait Notifier: Send + Sync { | |
/// Name of the notifier. | |
fn get_name(&self) -> String; | |
/// Send *text* to *name*. | |
fn notify(&self, name: &str, text: &str) -> Result<(), Error>; | |
} | |
impl fmt::Debug for dyn Notifier { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
write!(f, "Notifier: {}", self.get_name()) | |
} | |
} | |
pub struct Notifiers { | |
phone_book: PhoneBook, | |
} | |
/// A Tuple of a notifier and the contact its supposed to poke. | |
#[derive(Debug)] | |
struct LePoke<'a> { | |
notifier: &'a Box<dyn Notifier>, | |
contact: String, | |
} | |
impl<'a> LePoke<'a> { | |
fn poke(&self, text: &str) -> Result<(), Error> { | |
self.notifier.notify(&self.contact, text) | |
} | |
} | |
impl Notifiers { | |
pub fn new( | |
phone_book: HashMap<String, HashMap<String, String>>, | |
notifiers: Vec<Box<dyn Notifier>>, | |
) -> Self { | |
Notifiers { | |
phone_book: PhoneBook { | |
book: phone_book, | |
notifiers: notifiers, | |
}, | |
} | |
} | |
/// Find the best notifier for *whom* and poke them. | |
pub fn notify(&self, whom: &str, text: &str) -> Result<(), Error> { | |
let mut notified = false; | |
for le_poke in self.phone_book.notifiers_iter(whom) { | |
if let Ok(_) = le_poke.poke(text) { | |
notified = true; | |
break; | |
} | |
} | |
if !notified { | |
panic!("No working notifier found for {}.", whom); | |
} | |
Ok(()) | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use std::cell::RefCell; | |
use std::sync::Mutex; | |
use super::*; | |
type Notifies = Vec<(String, String)>; | |
#[derive(Debug)] | |
struct InMemoryNotifier<'a> { | |
name: String, | |
notifies: Mutex<RefCell<&'a mut Notifies>>, | |
} | |
impl<'a> InMemoryNotifier<'a> { | |
fn new(name: &str, notifies: &'a mut Notifies) -> Self { | |
InMemoryNotifier { | |
name: name.to_string(), | |
notifies: Mutex::new(RefCell::new(notifies)), | |
} | |
} | |
} | |
impl<'a> Notifier for InMemoryNotifier<'a> { | |
fn get_name(&self) -> String { | |
self.name.clone() | |
} | |
fn notify(&self, whom: &str, text: &str) -> Result<(), Error> { | |
let lock = self.notifies.lock().unwrap(); | |
let mut notifies = lock.borrow_mut(); | |
notifies.push((whom.to_string(), text.to_string())); | |
Ok(()) | |
} | |
} | |
#[test] | |
fn smoke() { | |
let mut notifies = Notifies::new(); | |
{ | |
let n = InMemoryNotifier::new("notifier", &mut notifies); | |
n.notify("abc", "yolo!").unwrap(); | |
n.notify("xyz", "carpe diem").unwrap(); | |
} | |
assert_eq!( | |
vec![ | |
("abc".to_string(), "yolo!".to_string()), | |
("xyz".to_string(), "carpe diem".to_string()), | |
], | |
notifies | |
) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment