Skip to main content

aimdb_core/buffer/
reader.rs

1//! Consumer-facing reader handles.
2//!
3//! The [`BufferReader`] / [`JsonBufferReader`] SPIs are object-safe and
4//! poll-based so adapters can implement them without a per-message
5//! `Pin<Box<dyn Future>>` allocation. These handles restore the ergonomic
6//! `async fn recv().await` surface for consumers by wrapping the erased
7//! reader's `poll_*` method in [`core::future::poll_fn`] — which is `core`-only
8//! (no_std-clean), allocation-free, and `unsafe`-free.
9//!
10//! The wrapped future is `Send` because the boxed reader is `Send`.
11
12use alloc::boxed::Box;
13#[cfg(feature = "remote")]
14use alloc::vec::Vec;
15use core::future::poll_fn;
16
17use crate::buffer::BufferReader;
18use crate::DbError;
19
20#[cfg(feature = "remote")]
21use crate::buffer::JsonBufferReader;
22
23/// Owned, ergonomic handle over an erased [`BufferReader`].
24///
25/// Returned by `Consumer::subscribe`. This is the "boxed lane": one indirect
26/// call per `recv`, zero AimDB-added heap allocations per message. (The generic
27/// monomorphized `Reader<T, B>` fast lane remains dormant.)
28pub struct Reader<T: Clone + Send> {
29    inner: Box<dyn BufferReader<T> + Send>,
30}
31
32impl<T: Clone + Send> Reader<T> {
33    /// Wrap an erased reader in an ergonomic handle.
34    pub fn new(inner: Box<dyn BufferReader<T> + Send>) -> Self {
35        Self { inner }
36    }
37
38    /// Receive the next value, awaiting until one is available.
39    ///
40    /// Allocation-free: wraps the erased reader's
41    /// [`poll_recv`](BufferReader::poll_recv) via `core::future::poll_fn`.
42    ///
43    /// # Behavior by Buffer Type
44    /// - **SPMC Ring**: Returns next value, or `Lagged(n)` if fell behind
45    /// - **SingleLatest**: Waits for value change, returns most recent
46    /// - **Mailbox**: Waits for slot value, takes and clears it
47    pub async fn recv(&mut self) -> Result<T, DbError> {
48        poll_fn(|cx| self.inner.poll_recv(cx)).await
49    }
50
51    /// Non-blocking receive — returns immediately.
52    ///
53    /// Returns `Err(DbError::BufferEmpty)` if no pending values.
54    pub fn try_recv(&mut self) -> Result<T, DbError> {
55        self.inner.try_recv()
56    }
57}
58
59/// Owned, ergonomic handle over an erased [`JsonBufferReader`].
60///
61/// Returned by `subscribe_json`. Awaiting `recv_json` or
62/// `recv_json_bytes` wraps the corresponding poll method via
63/// `core::future::poll_fn` — no extra per-message future box.
64#[cfg(feature = "remote")]
65pub struct JsonReader {
66    inner: Box<dyn JsonBufferReader + Send>,
67}
68
69#[cfg(feature = "remote")]
70impl JsonReader {
71    /// Wrap an erased JSON reader in an ergonomic handle.
72    pub fn new(inner: Box<dyn JsonBufferReader + Send>) -> Self {
73        Self { inner }
74    }
75
76    /// Receive the next value as JSON, awaiting until one is available.
77    pub async fn recv_json(&mut self) -> Result<serde_json::Value, DbError> {
78        poll_fn(|cx| self.inner.poll_recv_json(cx)).await
79    }
80
81    /// Receive the next value as owned JSON bytes.
82    pub async fn recv_json_bytes(&mut self) -> Result<Vec<u8>, DbError> {
83        poll_fn(|cx| self.inner.poll_recv_json_bytes(cx)).await
84    }
85
86    /// Non-blocking receive as JSON — returns immediately.
87    ///
88    /// Returns `Err(DbError::BufferEmpty)` if no pending values.
89    pub fn try_recv_json(&mut self) -> Result<serde_json::Value, DbError> {
90        self.inner.try_recv_json()
91    }
92
93    /// Non-blocking receive as owned JSON bytes — returns immediately.
94    ///
95    /// Returns `Err(DbError::BufferEmpty)` if no pending values.
96    pub fn try_recv_json_bytes(&mut self) -> Result<Vec<u8>, DbError> {
97        self.inner.try_recv_json_bytes()
98    }
99}