use bytes::{Buf, BufMut};
use crate::protocol::{BinaryMessage, BinaryPayload, ParseError};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PlaybackCommand {
Play = 0,
Pause = 1,
}
impl TryFrom<u8> for PlaybackCommand {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Play),
1 => Ok(Self::Pause),
_ => Err(value),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PlaybackControlRequest {
pub playback_command: PlaybackCommand,
pub playback_speed: f32,
pub seek_time: Option<u64>,
pub request_id: String,
}
impl<'a> BinaryPayload<'a> for PlaybackControlRequest {
fn parse_payload(mut data: &'a [u8]) -> Result<Self, ParseError> {
if data.len() < 1 + 4 + 1 + 8 + 4 {
return Err(ParseError::BufferTooShort);
}
let state_byte = data.get_u8();
let playback_command = PlaybackCommand::try_from(state_byte)
.map_err(|_| ParseError::InvalidPlaybackCommand(state_byte))?;
let playback_speed = data.get_f32_le();
let had_seek = data.get_u8() != 0;
let seek_time = if had_seek {
Some(data.get_u64_le())
} else {
data.advance(8);
None
};
let request_id_len = data.get_u32_le() as usize;
if data.len() < request_id_len {
return Err(ParseError::BufferTooShort);
}
let request_id_bytes = &data[..request_id_len];
let request_id = std::str::from_utf8(request_id_bytes)?.to_string();
Ok(Self {
playback_command,
playback_speed,
seek_time,
request_id,
})
}
fn payload_size(&self) -> usize {
1 + 4 + 1 + 8 + 4 + self.request_id.len()
}
fn write_payload(&self, buf: &mut impl BufMut) {
buf.put_u8(self.playback_command as u8);
buf.put_f32_le(self.playback_speed);
buf.put_u8(if self.seek_time.is_some() { 1 } else { 0 });
buf.put_u64_le(self.seek_time.unwrap_or(0));
buf.put_u32_le(self.request_id.len() as u32);
buf.put_slice(self.request_id.as_bytes());
}
}
impl BinaryMessage<'_> for PlaybackControlRequest {
const OPCODE: u8 = 3;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_roundtrip_play_with_seek_time() {
let orig = PlaybackControlRequest {
playback_command: PlaybackCommand::Play,
playback_speed: 1.0,
seek_time: Some(100_500_000_000),
request_id: "some-id".to_string(),
};
let mut buf = Vec::new();
BinaryPayload::write_payload(&orig, &mut buf);
let parsed = PlaybackControlRequest::parse_payload(&buf).unwrap();
assert_eq!(parsed, orig);
}
#[test]
fn test_roundtrip_play_without_seek_time() {
let orig = PlaybackControlRequest {
playback_command: PlaybackCommand::Play,
playback_speed: 1.0,
seek_time: None,
request_id: "some-id".to_string(),
};
let mut buf = Vec::new();
BinaryPayload::write_payload(&orig, &mut buf);
let parsed = PlaybackControlRequest::parse_payload(&buf).unwrap();
assert_eq!(parsed, orig);
}
#[test]
fn test_parse_payload_with_seek_time() {
let request_id = "some-id".to_string();
let mut data = Vec::new();
data.put_u8(PlaybackCommand::Play as u8); data.put_f32_le(1.5); data.put_u8(1); data.put_u64_le(100_500_000_000); data.put_u32_le(request_id.len() as u32);
data.put_slice(request_id.as_bytes());
let parsed = PlaybackControlRequest::parse_payload(&data).unwrap();
assert_eq!(parsed.playback_command, PlaybackCommand::Play);
assert_eq!(parsed.playback_speed, 1.5);
assert_eq!(parsed.seek_time, Some(100_500_000_000));
assert_eq!(parsed.request_id, "some-id".to_string());
}
#[test]
fn test_parse_payload_without_seek_time() {
let request_id = "some-id".to_string();
let mut data = Vec::new();
data.put_u8(PlaybackCommand::Play as u8); data.put_f32_le(2.0); data.put_u8(0); data.put_u64_le(0); data.put_u32_le(request_id.len() as u32);
data.put_slice(request_id.as_bytes());
let parsed = PlaybackControlRequest::parse_payload(&data).unwrap();
assert_eq!(parsed.playback_command, PlaybackCommand::Play);
assert_eq!(parsed.playback_speed, 2.0);
assert_eq!(parsed.seek_time, None);
assert_eq!(parsed.request_id, "some-id".to_string());
}
}