use std::{ any::Any, collections::{HashMap, HashSet}, mem, sync::{Arc, Mutex, MutexGuard}, }; use web_push::{PartialVapidSignatureBuilder, SubscriptionInfo, WebPushError}; use crate::{error::failed::Failed, push::Publish}; #[derive(Clone)] pub struct Client(Arc>); #[derive(Default)] struct ClientInner { sent: Vec, planned_failures: HashMap, } impl Client { pub fn new() -> Self { Self(Arc::default()) } fn inner(&self) -> MutexGuard<'_, ClientInner> { self.0.lock().unwrap() } // Clears the list of sent messages (for all clones of this Client) when called, because we // can't clone `Publications`s, so we either need to move them or try to reconstruct them. pub fn sent(&self) -> Vec { let sent = &mut self.inner().sent; mem::take(sent) } pub fn fail_next(&self, subscription_info: &SubscriptionInfo, err: WebPushError) { let planned_failures = &mut self.inner().planned_failures; planned_failures.insert(subscription_info.clone(), err); } } #[async_trait::async_trait] impl Publish for Client { async fn publish<'s, M>( &self, message: M, _: &PartialVapidSignatureBuilder, subscriptions: impl IntoIterator + Send, ) -> Result, Failed> where M: Send + 'static, { let mut inner = self.inner(); let message: Box = Box::new(message); let mut recipients = HashSet::new(); let mut failures = Vec::new(); for subscription in subscriptions { recipients.insert(subscription.clone()); if let Some(err) = inner.planned_failures.remove(subscription) { failures.push((subscription, err)); } } let publication = Publication { message, recipients, }; inner.sent.push(publication); Ok(failures) } } #[derive(Debug)] pub struct Publication { pub message: Box, pub recipients: HashSet, } impl Publication { pub fn message_eq(&self, candidate: &M) -> bool where M: PartialEq + 'static, { match self.message.downcast_ref::() { None => false, Some(message) => message == candidate, } } }