use std::collections::HashMap;
use std::sync::{Arc, Mutex, Weak};
use tokio::sync::mpsc;
use crate::ilink::types::WeixinMessage;
const APPROVAL_INBOX_CAPACITY: usize = 8;
pub(crate) struct ApprovalBroker {
inboxes: Mutex<HashMap<String, mpsc::Sender<WeixinMessage>>>,
}
impl ApprovalBroker {
pub(crate) fn new() -> Arc<Self> {
Arc::new(Self {
inboxes: Mutex::new(HashMap::new()),
})
}
pub(crate) fn deliver(&self, key: &str, msg: &WeixinMessage) -> bool {
let tx = self
.inboxes
.lock()
.unwrap_or_else(|e| e.into_inner())
.get(key)
.cloned();
match tx {
Some(tx) => tx.try_send(msg.clone()).is_ok(),
None => false,
}
}
pub(crate) fn register(
self: &Arc<Self>,
key: String,
) -> (mpsc::Receiver<WeixinMessage>, ApprovalGuard) {
let (tx, rx) = mpsc::channel(APPROVAL_INBOX_CAPACITY);
self.inboxes
.lock()
.unwrap_or_else(|e| e.into_inner())
.insert(key.clone(), tx);
(
rx,
ApprovalGuard {
broker: Arc::downgrade(self),
key,
},
)
}
}
pub(crate) struct ApprovalGuard {
broker: Weak<ApprovalBroker>,
key: String,
}
impl ApprovalGuard {
#[allow(dead_code)]
pub fn forget(self) {
std::mem::forget(self);
}
}
impl Drop for ApprovalGuard {
fn drop(&mut self) {
if let Some(broker) = self.broker.upgrade() {
let mut map = broker.inboxes.lock().unwrap_or_else(|e| e.into_inner());
if map.get(&self.key).is_some() {
map.remove(&self.key);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ilink::types::{MessageItem, TextItem, WeixinMessage};
fn msg_with(text: &str) -> WeixinMessage {
WeixinMessage {
context_token: Some("ctx".to_string()),
ilink_hub_ext: Some(crate::ilink::types::HubExt {
session_name: Some("s".to_string()),
..Default::default()
}),
item_list: Some(std::sync::Arc::new(vec![MessageItem {
item_type: Some(1),
text_item: Some(TextItem {
text: Some(text.to_string()),
}),
..Default::default()
}])),
..Default::default()
}
}
#[tokio::test]
async fn deliver_returns_false_when_no_inbox() {
let broker = ApprovalBroker::new();
assert!(!broker.deliver("missing", &msg_with("允许")));
}
#[tokio::test]
async fn register_then_deliver_routes_to_receiver() {
let broker = ApprovalBroker::new();
let (mut rx, _guard) = broker.register("k".to_string());
assert!(broker.deliver("k", &msg_with("允许")));
let got = rx.recv().await.expect("msg delivered");
assert_eq!(got.text(), Some("允许"));
}
#[tokio::test]
async fn guard_drop_removes_inbox() {
let broker = ApprovalBroker::new();
{
let (_rx, _guard) = broker.register("k".to_string());
assert!(broker.deliver("k", &msg_with("允许")));
}
assert!(!broker.deliver("k", &msg_with("拒绝")));
}
#[tokio::test]
async fn second_register_replaces_first() {
let broker = ApprovalBroker::new();
let (_rx1, _guard1) = broker.register("k".to_string());
let (mut rx2, _guard2) = broker.register("k".to_string());
assert!(broker.deliver("k", &msg_with("拒绝")));
assert_eq!(rx2.recv().await.unwrap().text(), Some("拒绝"));
}
}