pubsub.rs 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. use rand::{rngs::OsRng, Rng};
  2. use std::{
  3. collections::HashMap,
  4. sync::{Arc, Mutex},
  5. };
  6. use crate::error::{Error, Result};
  7. pub type SubscriptionId = usize;
  8. // Waiting for trait aliases
  9. pub trait Piped: Clone + Send + 'static {}
  10. impl<T> Piped for T where T: Clone + Send + 'static {}
  11. #[derive(Debug)]
  12. /// Subscription to the Publisher. Created using `publisher.subscribe().await`.
  13. pub struct Subscription<T: Piped> {
  14. id: SubscriptionId,
  15. recv_queue: smol::channel::Receiver<T>,
  16. parent: Arc<Publisher<T>>,
  17. }
  18. impl<T: Piped> Subscription<T> {
  19. pub fn get_id(&self) -> SubscriptionId {
  20. self.id
  21. }
  22. /// Receive message.
  23. pub async fn receive(&self) -> Result<T> {
  24. let msg_result = self.recv_queue.recv().await;
  25. msg_result.or(Err(Error::PublisherDestroyed))
  26. }
  27. }
  28. impl<T: Piped> Drop for Subscription<T> {
  29. fn drop(&mut self) {
  30. self.parent.unsubscribe(self.id)
  31. }
  32. }
  33. pub type PublisherPtr<T> = Arc<Publisher<T>>;
  34. #[derive(Debug)]
  35. pub struct Publisher<T> {
  36. subs: Mutex<HashMap<SubscriptionId, smol::channel::Sender<T>>>,
  37. }
  38. impl<T: Piped> Publisher<T> {
  39. pub fn new() -> Arc<Self> {
  40. Arc::new(Self { subs: Mutex::new(HashMap::new()) })
  41. }
  42. pub fn subscribe(self: Arc<Self>) -> Subscription<T> {
  43. let (sendr, recvr) = smol::channel::unbounded();
  44. let sub_id = OsRng.gen();
  45. // Optional to check whether this ID already exists.
  46. // It is nearly impossible to ever happen.
  47. self.subs.lock().unwrap().insert(sub_id, sendr);
  48. Subscription { id: sub_id, recv_queue: recvr, parent: self.clone() }
  49. }
  50. fn unsubscribe(&self, sub_id: SubscriptionId) {
  51. self.subs.lock().unwrap().remove(&sub_id);
  52. }
  53. /// Publish a message to subscriptions in the include list
  54. pub async fn notify_with_include(&self, message_result: T, include_list: &[SubscriptionId]) {
  55. // Maybe we should just provide a method to get all IDs
  56. // Then people can call notify_with_exclude() instead.
  57. // TODO: just collect and clone directly into a Vec
  58. let subs = self.subs.lock().unwrap().clone();
  59. for (id, sub) in subs.into_iter() {
  60. if !include_list.contains(&id) {
  61. continue
  62. }
  63. if let Err(e) = sub.send(message_result.clone()).await {
  64. panic!("[system::publisher] Error returned sending message in notify_with_include() call! {}", e);
  65. }
  66. }
  67. }
  68. /// Publish a message to all listening subscriptions.
  69. pub fn notify(&self, msg: T) {
  70. let subs = self.subs.lock().unwrap().clone();
  71. for (id, sub) in subs {
  72. if let Err(e) = sub.try_send(msg.clone()) {
  73. // This should never happen since Drop calls unsubscribe()
  74. panic!("Error in notify() call for sub={}! {}", id, e);
  75. }
  76. }
  77. }
  78. }