use std::sync::mpsc::Receiver;
use std::sync::mpsc::TryRecvError;
use anyhow::Context as _;
use crate::reminder::Reminder;
use crate::request::Request;
#[derive(Debug)]
pub(crate) struct Reminders {
request_recv: Receiver<Request>,
reminders: Vec<Reminder>,
}
impl Reminders {
pub fn new(request_recv: Receiver<Request>, reminder: Reminder) -> Self {
Self {
request_recv,
reminders: vec![reminder],
}
}
pub fn try_recv(&mut self) -> Result<(), ()> {
let result = self.request_recv.try_recv();
let request = match result {
Ok(request) => request,
Err(TryRecvError::Empty) => {
return Ok(())
},
Err(TryRecvError::Disconnected) => {
return Err(())
},
};
let reminder = match request {
Request::Remind(reminder) => reminder,
Request::List(response) => {
let () = response
.write(&self.reminders)
.context("failed to send Reminder list")
.unwrap();
return Ok(())
},
};
let result = self
.reminders
.binary_search_by(|r| r.cmp(&reminder).reverse());
match result {
Ok(..) => {
},
Err(idx) => {
let () = self.reminders.insert(idx, reminder);
},
}
Ok(())
}
#[inline]
pub fn next_reminder(&self) -> Option<&Reminder> {
self.reminders.last()
}
#[inline]
pub fn remove_next(&mut self) {
let _reminder = self.reminders.pop();
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::mpsc::channel;
use std::time::Duration;
use std::time::UNIX_EPOCH;
use crate::channel::Channel;
fn reminder(secs: u64, msg: &str) -> Reminder {
Reminder {
time: UNIX_EPOCH + Duration::from_secs(secs),
message: msg.to_string(),
}
}
#[test]
fn initial_reminder_retrieval() {
let (_tx, rx) = channel();
let r = reminder(10, "initial");
let reminders = Reminders::new(rx, r.clone());
assert_eq!(reminders.next_reminder(), Some(&r));
}
#[test]
fn no_reminder_receive() {
let (_tx, rx) = channel();
let mut reminders = Reminders::new(rx, reminder(10, "initial"));
assert_eq!(reminders.try_recv(), Ok(()));
assert_eq!(reminders.next_reminder().unwrap().message, "initial");
}
#[test]
fn disconnected_channel_receive() {
let (tx, rx) = channel::<Request>();
let mut reminders = Reminders::new(rx, reminder(10, "initial"));
drop(tx);
assert_eq!(reminders.try_recv(), Err(()));
}
#[test]
fn reminder_ordering() {
let (tx, rx) = channel();
let initial = reminder(20, "20");
let mut reminders = Reminders::new(rx, initial);
let () = tx.send(Request::Remind(reminder(10, "10"))).unwrap();
let () = reminders.try_recv().unwrap();
let () = tx.send(Request::Remind(reminder(30, "30"))).unwrap();
let () = reminders.try_recv().unwrap();
assert_eq!(reminders.next_reminder().unwrap().message, "10");
let () = reminders.remove_next();
assert_eq!(reminders.next_reminder().unwrap().message, "20");
let () = reminders.remove_next();
assert_eq!(reminders.next_reminder().unwrap().message, "30");
}
#[test]
fn duplicate_reminder_ignoring() {
let (tx, rx) = channel();
let r = reminder(10, "duplicate");
let mut reminders = Reminders::new(rx, r.clone());
let () = tx.send(Request::Remind(r)).unwrap();
let () = reminders.try_recv().unwrap();
let () = reminders.remove_next();
assert_eq!(reminders.next_reminder(), None);
}
#[test]
fn remove_next_on_empty_container_is_safe() {
let (_tx, rx) = channel();
let mut reminders = Reminders::new(rx, reminder(10, "10"));
let () = reminders.remove_next();
let () = reminders.remove_next();
assert_eq!(reminders.next_reminder(), None);
}
#[test]
fn reminder_listing() {
let (tx, rx) = channel();
let initial = reminder(20, "20");
let mut reminders = Reminders::new(rx, initial);
let () = tx.send(Request::Remind(reminder(10, "10"))).unwrap();
let () = reminders.try_recv().unwrap();
let () = tx.send(Request::Remind(reminder(30, "30"))).unwrap();
let () = reminders.try_recv().unwrap();
let (r, w) = Channel::oneshot().unwrap();
let () = tx.send(Request::List(w)).unwrap();
let () = reminders.try_recv().unwrap();
let list = r.read().unwrap();
let expected = vec![reminder(30, "30"), reminder(20, "20"), reminder(10, "10")];
assert_eq!(list, expected);
}
}