use serde::{Deserialize, Serialize};
pub const MOQ_EPOCH_UNIX_MILLIS: u64 = 1_577_836_800_000;
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct Archive {
pub track: String,
#[serde(
default = "Archive::default_timescale",
deserialize_with = "super::deserialize_timescale_or_default"
)]
pub timescale: u32,
pub duration_max: Option<u64>,
#[serde(default)]
pub replay: Option<moq_net::path::RelativeOwned>,
#[serde(default)]
pub store: Option<url::Url>,
#[serde(default)]
pub version: Option<u32>,
}
impl Archive {
pub const VERSION: u32 = 1;
pub const fn default_timescale() -> u32 {
1000
}
pub fn new(track: impl Into<String>) -> Self {
Self {
track: track.into(),
timescale: Self::default_timescale(),
duration_max: None,
replay: None,
store: None,
version: None,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn live_publisher_is_timeline_fields_alone() {
let json = serde_json::to_string(&Archive::new("timeline.z")).unwrap();
assert_eq!(json, r#"{"track":"timeline.z","timescale":1000}"#);
assert_eq!(
serde_json::from_str::<Archive>(&json).unwrap(),
Archive::new("timeline.z")
);
}
#[test]
fn recording_roundtrips_replay_store_and_version() {
let mut archive = Archive::new("timeline.z");
archive.replay = Some(moq_net::path::RelativeOwned::new("./recordings/clip"));
archive.store = Some("https://objects.example/rec/".parse().unwrap());
archive.version = Some(Archive::VERSION);
let json = serde_json::to_string(&archive).unwrap();
assert_eq!(
json,
r#"{"track":"timeline.z","timescale":1000,"replay":"recordings/clip","store":"https://objects.example/rec/","version":1}"#
);
assert_eq!(serde_json::from_str::<Archive>(&json).unwrap(), archive);
}
#[test]
fn zero_timescale_is_refused() {
serde_json::from_str::<Archive>(r#"{"track":"timeline.z","timescale":0}"#)
.expect_err("a zero timescale must not decode");
}
#[test]
fn invalid_store_url_is_refused() {
serde_json::from_str::<Archive>(r#"{"track":"timeline.z","store":"not a url"}"#)
.expect_err("an invalid store URL must not decode");
}
}