Skip to content

Instantly share code, notes, and snippets.

@hynek
Created June 30, 2018 08:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hynek/7e1ccf3d8dfc8b93da73f8c900398bce to your computer and use it in GitHub Desktop.
Save hynek/7e1ccf3d8dfc8b93da73f8c900398bce to your computer and use it in GitHub Desktop.
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(&notifier.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