use std::collections::VecDeque;
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Waker};
use serde_json::{Map, Value};
#[derive(Debug, Default)]
pub struct Unsolicited {
inner: Arc<Mutex<Inner>>,
}
#[derive(Debug, Default)]
struct Inner {
frames: VecDeque<Map<String, Value>>,
wakers: Vec<Waker>,
}
#[derive(Debug)]
struct Pending {
inner: Arc<Mutex<Inner>>,
}
impl Future for Pending {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
let Ok(mut inner) = self.inner.lock() else {
return Poll::Ready(());
};
if !inner.frames.is_empty() {
return Poll::Ready(());
}
if !inner.wakers.iter().any(|held| held.will_wake(cx.waker())) {
inner.wakers.push(cx.waker().clone());
}
Poll::Pending
}
}
impl Unsolicited {
#[must_use]
pub fn new() -> Self {
Self {
inner: Arc::new(Mutex::new(Inner::default())),
}
}
pub fn hold(&self, frame: Map<String, Value>) {
let mut wakers = Vec::new();
{
let Ok(mut inner) = self.inner.lock() else {
return;
};
inner.frames.push_back(frame);
wakers.append(&mut inner.wakers);
}
for waker in wakers {
waker.wake();
}
}
#[must_use]
pub fn take(&self) -> Vec<Map<String, Value>> {
self.inner
.lock()
.map_or_else(|_| Vec::new(), |mut inner| inner.frames.drain(..).collect())
}
pub fn wait(&self) -> impl Future<Output = ()> + Send + 'static {
Pending {
inner: Arc::clone(&self.inner),
}
}
#[must_use]
pub fn len(&self) -> usize {
self.inner.lock().map_or(0, |inner| inner.frames.len())
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[cfg(test)]
mod tests {
use serde_json::{Map, Value, json};
use super::Unsolicited;
fn frame(id: i64) -> Map<String, Value> {
json!({"jsonrpc": "2.0", "id": id})
.as_object()
.cloned()
.unwrap_or_default()
}
#[test]
fn a_held_frame_comes_back_exactly_once() {
let held = Unsolicited::new();
held.hold(frame(1));
held.hold(frame(2));
assert_eq!(held.len(), 2);
let first = held.take();
assert_eq!(first.len(), 2);
assert_eq!(first.first().and_then(|f| f.get("id")), Some(&json!(1)));
assert_eq!(first.get(1).and_then(|f| f.get("id")), Some(&json!(2)));
assert!(held.take().is_empty());
assert!(held.is_empty());
}
#[test]
fn a_fresh_queue_is_empty() {
let held = Unsolicited::new();
assert!(held.is_empty());
assert_eq!(held.len(), 0);
assert!(held.take().is_empty());
}
}