use bytes::{Buf, BufMut};
use crate::coding::{Decode, DecodeError, Encode, EncodeError, Sizer};
use super::Version;
const TRACK_STATUS_OK_14: u64 = 0x0E;
const TRACK_STATUS_ERROR_14: u64 = 0x0F;
pub(crate) fn control(id: u64, version: Version) -> crate::stats::Control {
use super::*;
use crate::stats::Control;
if version == Version::Draft14 && (id == TRACK_STATUS_OK_14 || id == TRACK_STATUS_ERROR_14) {
return Control::TrackInfo;
}
if matches!(version, Version::Draft14 | Version::Draft15) && id == SubscribeUpdate::ID {
return Control::Subscribe;
}
if version == Version::Draft14 && id == SubscribeError::ID {
return Control::Subscribe;
}
match id {
PublishNamespace::ID
| PublishNamespaceError::ID
| PublishNamespaceDone::ID
| PublishNamespaceCancel::ID
| SubscribeNamespace::ID
| SubscribeNamespaceLegacy::ID
| SubscribeNamespaceOk::ID
| SubscribeNamespaceError::ID
| UnsubscribeNamespace::ID
| NamespaceDone::ID => Control::Announce,
TrackStatus::ID | SUBSCRIBE_TRACKS_ID | PublishBlocked::ID => Control::TrackInfo,
Subscribe::ID
| SubscribeOk::ID
| Unsubscribe::ID
| Publish::ID
| PublishOk::ID
| PublishError::ID
| PublishDone::ID => Control::Subscribe,
Fetch::ID | FetchOk::ID | FetchError::ID | FetchCancel::ID => Control::Fetch,
GoAway::ID => Control::Goaway,
_ => Control::Other,
}
}
pub trait Message: Sized + std::fmt::Debug {
const ID: u64;
fn encode_msg<W: BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError>;
fn decode_msg<B: Buf>(buf: &mut B, version: Version) -> Result<Self, DecodeError>;
}
impl<T: Message> Encode<Version> for T {
fn encode<W: BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
tracing::trace!(?self, "encoding");
let mut sizer = Sizer::default();
self.encode_msg(&mut sizer, version)?;
let size: u16 = sizer.size.try_into().map_err(|_| EncodeError::TooLarge)?;
size.encode(w, version)?;
self.encode_msg(w, version)
}
fn control(&self, version: Version) -> Option<crate::stats::Control> {
Some(control(T::ID, version))
}
}
impl<T: Message> Decode<Version> for T {
fn control(&self, version: Version) -> Option<crate::stats::Control> {
Some(control(T::ID, version))
}
fn decode<B: Buf>(buf: &mut B, version: Version) -> Result<Self, DecodeError> {
let size = u16::decode(buf, version)? as usize;
if tracing::enabled!(tracing::Level::TRACE) {
if buf.remaining() < size {
return Err(DecodeError::Short);
}
let raw = buf.copy_to_bytes(size);
let mut slice = &raw[..];
match Self::decode_msg(&mut slice, version).map_err(DecodeError::complete) {
Ok(result) => {
if slice.remaining() > 0 {
return Err(DecodeError::Long);
}
tracing::trace!(?result, "decoded");
Ok(result)
}
Err(e) => {
tracing::warn!(%e, ?raw, "decode failed");
Err(e)
}
}
} else {
if buf.remaining() < size {
return Err(DecodeError::Short);
}
let mut limited = buf.take(size);
match Self::decode_msg(&mut limited, version).map_err(DecodeError::complete) {
Ok(result) => {
if limited.remaining() > 0 {
return Err(DecodeError::Long);
}
Ok(result)
}
Err(e) => {
tracing::warn!(%e, "decode failed");
Err(e)
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::stats::Control;
#[test]
fn draft14_track_status_ids_are_track_info() {
for id in [TRACK_STATUS_OK_14, TRACK_STATUS_ERROR_14] {
assert_eq!(control(id, Version::Draft14), Control::TrackInfo);
}
assert_eq!(
control(super::super::NamespaceDone::ID, Version::Draft16),
Control::Announce
);
assert_eq!(
control(super::super::SubscribeUpdate::ID, Version::Draft15),
Control::Subscribe
);
for id in [super::super::RequestOk::ID, super::super::SubscribeUpdate::ID] {
assert_eq!(control(id, Version::Draft16), Control::Other);
}
}
#[test]
fn draft14_subscribe_error_is_subscribe() {
let id = super::super::SubscribeError::ID;
assert_eq!(control(id, Version::Draft14), Control::Subscribe);
assert_eq!(control(id, Version::Draft15), Control::Other);
}
}