pub use wincode::ReadError;
use {
crate::{backend, stream_name::StreamName},
std::{marker::PhantomData, path::PathBuf, time::Duration},
wincode::Deserialize,
wincode_dynamic::{Decoder, Fields, RootSchema},
};
#[derive(Debug)]
pub struct StreamExplorer(backend::StreamExplorer);
impl StreamExplorer {
pub fn new(event_system_directory: PathBuf) -> Self {
Self(backend::StreamExplorer::new(event_system_directory))
}
pub fn available_streams(&self) -> impl Iterator<Item = AvailableStream> + '_ {
self.0.available_streams().map(AvailableStream)
}
}
#[derive(Debug)]
pub struct Dynamic;
pub struct Typed<T> {
_marker: PhantomData<fn() -> T>,
}
impl<T> std::fmt::Debug for Typed<T> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("Typed")
.field("_marker", &self._marker)
.finish()
}
}
pub struct Subscriber<Mode> {
backend: backend::Subscriber,
mode: PhantomData<Mode>,
}
impl<Mode> std::fmt::Debug for Subscriber<Mode> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("Subscriber")
.field("mode", &std::any::type_name::<Mode>())
.field("backend", &self.backend)
.finish()
}
}
impl<Mode> Subscriber<Mode> {
pub fn stream_name(&self) -> &StreamName {
self.backend.stream_name()
}
pub fn type_name(&self) -> &str {
self.backend.type_name()
}
pub fn try_recv(&mut self) -> Result<StreamMessage<'_, Mode>, TryRecvError> {
self.backend.try_recv().map(StreamMessage::new)
}
pub fn recv_timeout(
&mut self,
timeout: Duration,
) -> Result<StreamMessage<'_, Mode>, RecvTimeoutError> {
self.backend.recv_timeout(timeout).map(StreamMessage::new)
}
pub fn lanes_metadata(&self) -> LanesMetadata {
LanesMetadata(self.backend.lanes_metadata())
}
fn new(backend: backend::Subscriber) -> Self {
Self {
backend,
mode: PhantomData,
}
}
}
pub struct StreamMessage<'a, Mode> {
backend: backend::StreamMessage<'a>,
mode: PhantomData<Mode>,
}
impl<Mode> std::fmt::Debug for StreamMessage<'_, Mode> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("StreamMessage")
.field("mode", &std::any::type_name::<Mode>())
.field("backend", &self.backend)
.finish()
}
}
impl<'a, Mode> StreamMessage<'a, Mode> {
pub fn lane_metadata(&self) -> LaneMetadata<'_> {
LaneMetadata(self.backend.lane_metadata())
}
fn new(backend: backend::StreamMessage<'a>) -> Self {
Self {
backend,
mode: PhantomData,
}
}
}
impl<T> StreamMessage<'_, Typed<T>>
where
T: for<'de> Deserialize<'de, Dst = T>,
{
pub fn decode(&self) -> Result<T, ReadError> {
wincode::deserialize(self.backend.payload())
}
}
impl<'a> StreamMessage<'a, Dynamic> {
pub fn decode<'de>(&'de self) -> Result<DecodedMessage<'a, 'de>, ReadError> {
Ok(match Decoder::new(self.backend.schema()) {
Decoder::Struct(schema_decoder) => DecodedMessage::Struct {
fields: schema_decoder.fields(self.backend.payload()),
},
Decoder::Enum(enum_decoder) => {
let variant_decoder = enum_decoder.decode_variant(self.backend.payload())?;
DecodedMessage::Enum {
variant_name: variant_decoder.variant_name(),
fields: variant_decoder.fields(),
}
}
})
}
}
#[derive(Debug)]
pub struct LanesMetadata(backend::LanesMetadata);
impl LanesMetadata {
pub fn iter(&self) -> impl Iterator<Item = LaneMetadata<'_>> + '_ {
self.0.iter().map(LaneMetadata)
}
}
#[derive(Clone, Copy, Debug)]
pub struct LaneMetadata<'a>(backend::LaneMetadata<'a>);
impl LaneMetadata<'_> {
pub fn lane(&self) -> usize {
self.0.lane()
}
pub fn rejected_items(&self) -> u64 {
self.0.rejected_items()
}
}
#[derive(Debug)]
pub struct AvailableStream(backend::AvailableStream);
impl AvailableStream {
pub fn stream_name(&self) -> &StreamName {
self.0.stream_name()
}
pub fn type_name(&self) -> &str {
self.0.type_name()
}
pub fn try_connect_dynamic(self) -> Result<Subscriber<Dynamic>, TryConnectError> {
self.0.try_connect().map(Subscriber::<Dynamic>::new)
}
pub fn try_connect_typed<T: wincode_dynamic::SchemaDynamic>(
self,
) -> Result<Subscriber<Typed<T>>, TryConnectTypedError> {
let expected_schema = T::schema();
let actual_schema = self.0.stream_schema();
if expected_schema != *actual_schema {
return Err(TryConnectTypedError::SchemaMismatch(Box::new(
SchemaMismatch {
expected: expected_schema,
actual: actual_schema.clone(),
},
)));
}
self.0
.try_connect()
.map(Subscriber::<Typed<T>>::new)
.map_err(TryConnectTypedError::Connection)
}
}
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
pub enum TryRecvError {
#[error("the stream has no new message")]
Empty,
}
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
#[error("the stream had no new message within the timeout")]
pub struct RecvTimeoutError;
#[derive(Debug, thiserror::Error)]
pub enum TryConnectTypedError {
#[error("stream schema does not match the requested type")]
SchemaMismatch(Box<SchemaMismatch>),
#[error(transparent)]
Connection(#[from] TryConnectError),
}
#[derive(Debug)]
pub struct SchemaMismatch {
pub expected: RootSchema,
pub actual: RootSchema,
}
#[derive(Debug, thiserror::Error)]
pub enum TryConnectError {
#[error("the stream has no available subscriber slots")]
SubscriberSlotsExhausted,
}
#[derive(Debug)]
pub enum DecodedMessage<'a, 'de> {
Struct {
fields: Fields<'a, 'de, &'de [u8]>,
},
Enum {
fields: Fields<'a, 'de, &'de [u8]>,
variant_name: &'a str,
},
}