use std::{borrow::Cow, time::Duration};
use crate::{
Path, Timescale,
coding::{Decode, DecodeError, Encode, EncodeError},
};
use super::{Message, Version};
#[derive(Clone, Debug)]
pub struct Track<'a> {
pub broadcast: Path<'a>,
pub track: Cow<'a, str>,
}
impl Message for Track<'_> {
fn decode_msg<R: bytes::Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError> {
if !version.has_track_stream() {
return Err(DecodeError::Version);
}
let broadcast = Path::decode(r, version)?;
let track = Cow::<str>::decode(r, version)?;
Ok(Self { broadcast, track })
}
fn encode_msg<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
if !version.has_track_stream() {
return Err(EncodeError::Version);
}
self.broadcast.encode(w, version)?;
self.track.encode(w, version)?;
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct TrackInfo {
pub priority: u8,
pub ordered: bool,
pub latency_max: Duration,
pub timescale: Timescale,
}
impl Message for TrackInfo {
fn decode_msg<R: bytes::Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError> {
if !version.has_track_stream() {
return Err(DecodeError::Version);
}
let priority = u8::decode(r, version)?;
let ordered = u8::decode(r, version)? != 0;
let latency_max = Duration::decode(r, version)?;
let timescale = Timescale::new(u64::decode(r, version)?).map_err(|_| DecodeError::InvalidValue)?;
Ok(Self {
priority,
ordered,
latency_max,
timescale,
})
}
fn encode_msg<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
if !version.has_track_stream() {
return Err(EncodeError::Version);
}
self.priority.encode(w, version)?;
(self.ordered as u8).encode(w, version)?;
self.latency_max.encode(w, version)?;
u64::from(self.timescale).encode(w, version)?;
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
fn info_sample() -> TrackInfo {
TrackInfo {
priority: 7,
ordered: false,
latency_max: Duration::from_millis(2000),
timescale: Timescale::MICRO,
}
}
fn info_roundtrip(version: Version, info: &TrackInfo) -> TrackInfo {
let mut buf = Vec::new();
info.encode_msg(&mut buf, version).unwrap();
let mut slice = buf.as_slice();
TrackInfo::decode_msg(&mut slice, version).unwrap()
}
#[test]
fn track_info_roundtrips_on_lite05() {
let got = info_roundtrip(Version::Lite05, &info_sample());
assert_eq!(got.priority, 7);
assert!(!got.ordered);
assert_eq!(got.latency_max, Duration::from_millis(2000));
assert_eq!(got.timescale, Timescale::MICRO);
}
#[test]
fn track_info_default_timescale_roundtrips() {
let mut info = info_sample();
info.timescale = Timescale::default();
assert_eq!(info_roundtrip(Version::Lite05, &info).timescale, Timescale::MILLI);
}
#[test]
fn track_info_defaults_match_cross_language_wire_bytes() {
let info = crate::track::Info::default();
let info = TrackInfo {
priority: info.priority,
ordered: info.ordered,
latency_max: info.latency_max,
timescale: info.timescale,
};
let mut buf = Vec::new();
info.encode(&mut buf, Version::Lite05).unwrap();
assert_eq!(buf, [0x06, 0x00, 0x00, 0x53, 0x88, 0x43, 0xe8]);
}
#[test]
fn track_info_errors_before_lite05() {
let mut buf = Vec::new();
assert!(info_sample().encode_msg(&mut buf, Version::Lite04).is_err());
}
#[test]
fn track_request_roundtrips_on_lite05() {
let msg = Track {
broadcast: Path::new("room").to_owned(),
track: Cow::Borrowed("video"),
};
let mut buf = Vec::new();
msg.encode_msg(&mut buf, Version::Lite05).unwrap();
let mut slice = buf.as_slice();
let got = Track::decode_msg(&mut slice, Version::Lite05).unwrap();
assert_eq!(got.broadcast, Path::new("room"));
assert_eq!(got.track, "video");
}
#[test]
fn track_request_errors_before_lite05() {
let msg = Track {
broadcast: Path::new("room").to_owned(),
track: Cow::Borrowed("video"),
};
let mut buf = Vec::new();
assert!(msg.encode_msg(&mut buf, Version::Lite04).is_err());
}
}