use std::time::Duration;
use serde::{Deserialize, Serialize};
use crate::errors::Error;
use crate::messages::{IncomingMessages, Notice, OutgoingMessages, ResponseMessage};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum SubscriptionItem<T> {
Data(T),
Notice(Notice),
}
impl<T> SubscriptionItem<T> {
pub fn into_data(self) -> Option<T> {
match self {
SubscriptionItem::Data(t) => Some(t),
SubscriptionItem::Notice(_) => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Drained {
Ended,
Rejected(Notice),
Unconfirmed,
}
pub(crate) fn drain_outcome<T>(item: Result<SubscriptionItem<T>, Error>) -> Option<Result<Drained, Error>> {
match item {
Ok(_) => None,
Err(Error::Notice(notice)) => Some(Ok(Drained::Rejected(notice))),
Err(e @ (Error::ConnectionReset | Error::Shutdown)) => Some(Err(e)),
Err(_) => Some(Ok(Drained::Unconfirmed)),
}
}
pub(crate) fn filter_notice<T>(item: Result<SubscriptionItem<T>, Error>) -> Option<Result<T, Error>> {
match item {
Ok(SubscriptionItem::Data(t)) => Some(Ok(t)),
Ok(SubscriptionItem::Notice(n)) => {
log::warn!("ib notice on subscription: {n}");
None
}
Err(e) => Some(Err(e)),
}
}
#[derive(Debug)]
pub(crate) enum CollectStop {
EndMarker,
Closed,
Deadline,
Stopped,
Error(Error),
}
impl CollectStop {
pub(crate) fn into_result<T>(self, rows: Vec<T>) -> Result<Vec<T>, Error> {
match self {
CollectStop::EndMarker | CollectStop::Stopped => Ok(rows),
CollectStop::Closed => Err(Error::UnexpectedEndOfStream),
CollectStop::Deadline => Err(Error::Timeout),
CollectStop::Error(e) => Err(e),
}
}
pub(crate) fn into_batch<T>(self, rows: Vec<T>) -> Option<Result<Vec<T>, Error>> {
match self {
CollectStop::Error(e) => Some(Err(e)),
_ => (!rows.is_empty()).then_some(Ok(rows)),
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum Bound<I> {
None,
At(I),
Idle(Duration),
}
pub(crate) fn collect_step<T: StreamDecoder<T>>(
rows: &mut Vec<T>,
item: Result<SubscriptionItem<T>, Error>,
stop: &mut impl FnMut(&[T]) -> bool,
) -> Option<CollectStop> {
match filter_notice(item)? {
Ok(value) if value.is_snapshot_end() => Some(CollectStop::Stopped),
Ok(value) => {
rows.push(value);
stop(rows).then_some(CollectStop::Stopped)
}
Err(e) => Some(CollectStop::Error(e)),
}
}
#[derive(Debug, Clone)]
pub(crate) enum RoutedItem {
Response(ResponseMessage),
Notice(Notice),
Error(Error),
}
impl From<ResponseMessage> for RoutedItem {
fn from(message: ResponseMessage) -> Self {
RoutedItem::Response(message)
}
}
impl From<Error> for RoutedItem {
fn from(error: Error) -> Self {
RoutedItem::Error(error)
}
}
impl RoutedItem {
pub(crate) fn into_legacy(self) -> Option<Result<ResponseMessage, Error>> {
match self {
RoutedItem::Response(message) => Some(Ok(message)),
RoutedItem::Error(error) => Some(Err(error)),
RoutedItem::Notice(_) => None,
}
}
}
pub(crate) fn is_undeclared(ids: &[IncomingMessages], message: &ResponseMessage) -> bool {
!ids.contains(&message.message_type())
}
pub(crate) fn notice_item<T: StreamDecoder<T>>(notice: Notice) -> SubscriptionItem<T> {
match T::data_from_notice(¬ice) {
Some(data) => SubscriptionItem::Data(data),
None => SubscriptionItem::Notice(notice),
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct DecoderContext {
pub server_version: i32,
pub request_type: Option<OutgoingMessages>,
pub is_smart_depth: bool,
}
impl DecoderContext {
pub fn new(server_version: i32) -> Self {
Self {
server_version,
request_type: None,
is_smart_depth: false,
}
}
#[allow(dead_code)]
pub fn with_request_type(mut self, request_type: OutgoingMessages) -> Self {
self.request_type = Some(request_type);
self
}
pub fn with_smart_depth(mut self, is_smart_depth: bool) -> Self {
self.is_smart_depth = is_smart_depth;
self
}
}
pub(crate) trait StreamDecoder<T> {
const RESPONSE_MESSAGE_IDS: &'static [IncomingMessages];
fn decode(context: &DecoderContext, message: &ResponseMessage) -> Result<T, Error>;
const END_MESSAGES: &'static [IncomingMessages] = &[];
fn is_nonterminal_notice(_notice: &Notice) -> bool {
false
}
fn data_from_notice(_notice: &Notice) -> Option<T> {
None
}
fn cancel_message(_server_version: i32, _request_id: Option<i32>, _context: Option<&DecoderContext>) -> Result<Vec<u8>, Error> {
Err(Error::NotImplemented)
}
#[allow(unused)]
fn is_snapshot_end(&self) -> bool {
false
}
fn is_batch_end(&self) -> bool {
false
}
}
pub(crate) fn debug_assert_request_id_routable<T, D: StreamDecoder<T>>(request_id: Option<i32>) {
if !cfg!(debug_assertions) || request_id.is_none() {
return;
}
if let Some(kind) = crate::transport::routing::first_unroutable_by_request_id(D::RESPONSE_MESSAGE_IDS) {
panic!(
"{} declares {kind:?} in RESPONSE_MESSAGE_IDS, but {kind:?} has no `text_request_id_field` entry \
in src/messages.rs — a request_id-keyed subscription can never receive it. \
See docs/rules/wire/proto-aware-accessors.md",
std::any::type_name::<D>()
);
}
}
#[cfg(test)]
#[path = "common_tests.rs"]
mod tests;
#[cfg(test)]
#[path = "response_message_ids_tests.rs"]
mod response_message_ids_tests;