use serde::{Deserialize, Serialize};
use crate::Result;
pub trait RecordExt: serde::Serialize + serde::de::DeserializeOwned + Default + Clone + Send + Unpin + 'static {}
impl RecordExt for () {}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(
rename_all = "camelCase",
bound(serialize = "E: serde::Serialize", deserialize = "E: serde::de::DeserializeOwned")
)]
pub struct Record<E: RecordExt = ()> {
pub group: u64,
pub pts: u64,
#[serde(flatten)]
pub ext: E,
}
impl<E: RecordExt> Record<E> {
pub fn new(group: u64, pts: u64) -> Self {
Self {
group,
pts,
ext: E::default(),
}
}
pub fn from_slice(v: &[u8]) -> Result<Self> {
Ok(serde_json::from_slice(v)?)
}
pub fn to_vec(&self) -> Result<Vec<u8>> {
Ok(serde_json::to_vec(self)?)
}
}
pub fn track_name(rendition: &str) -> String {
format!("{rendition}.timeline.z")
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn roundtrip() {
let record = Record::<()>::new(42, 84_000);
let json = record.to_vec().unwrap();
assert_eq!(std::str::from_utf8(&json).unwrap(), r#"{"group":42,"pts":84000}"#);
assert_eq!(Record::<()>::from_slice(&json).unwrap(), record);
}
#[test]
fn typed_extension_flattens() {
#[derive(serde::Serialize, serde::Deserialize, Default, Clone, PartialEq, Debug)]
struct Ext {
#[serde(skip_serializing_if = "std::ops::Not::not", default)]
discontinuity: bool,
}
impl RecordExt for Ext {}
let record = Record {
group: 7,
pts: 14_000,
ext: Ext { discontinuity: true },
};
let json = record.to_vec().unwrap();
assert_eq!(
std::str::from_utf8(&json).unwrap(),
r#"{"group":7,"pts":14000,"discontinuity":true}"#
);
assert_eq!(Record::<Ext>::from_slice(&json).unwrap(), record);
}
#[test]
fn conventional_track_name() {
assert_eq!(track_name("video0"), "video0.timeline.z");
}
}