honzo_chunks/extra/
mod.rs1use honzo_core::HonzoError;
2use std::any::Any;
3use std::collections::HashMap;
4use std::sync::{Arc, OnceLock, RwLock};
5
6pub const ANNO_NAMESPACE: &str = "org.nisoku.anno";
7pub const DRM_NAMESPACE: &str = "org.nisoku.drm";
8pub const SYNC_NAMESPACE: &str = "org.nisoku.sync";
9
10pub mod anno;
11pub mod drm;
12pub mod sync;
13
14pub type RegisteredExtraValue = Box<dyn Any + Send + Sync>;
15pub type RegisteredExtraParser =
16 dyn Fn(&[u8]) -> Result<RegisteredExtraValue, HonzoError> + Send + Sync + 'static;
17
18static EXTRA_REGISTRY: OnceLock<RwLock<HashMap<String, Arc<RegisteredExtraParser>>>> =
19 OnceLock::new();
20
21fn registry() -> &'static RwLock<HashMap<String, Arc<RegisteredExtraParser>>> {
22 EXTRA_REGISTRY.get_or_init(|| RwLock::new(HashMap::new()))
23}
24
25pub fn boxed_extra<T: Any + Send + Sync>(value: T) -> RegisteredExtraValue {
26 Box::new(value)
27}
28
29pub struct RegisteredExtra {
30 namespace: String,
31 value: RegisteredExtraValue,
32}
33
34impl core::fmt::Debug for RegisteredExtra {
35 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
36 f.debug_struct("RegisteredExtra")
37 .field("namespace", &self.namespace)
38 .field("value_type", &std::any::type_name_of_val(&*self.value))
39 .finish()
40 }
41}
42
43impl RegisteredExtra {
44 pub fn namespace(&self) -> &str {
45 &self.namespace
46 }
47
48 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
49 self.value.downcast_ref()
50 }
51
52 pub fn into_downcast<T: Any>(self) -> Result<Box<T>, Self> {
53 match self.value.downcast::<T>() {
54 Ok(value) => Ok(value),
55 Err(value) => Err(Self {
56 namespace: self.namespace,
57 value,
58 }),
59 }
60 }
61}
62
63#[derive(Debug)]
64pub enum ParsedExtra {
65 Known(KnownExtra),
66 Registered(RegisteredExtra),
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub enum KnownExtra {
71 Anno(Vec<anno::Annotation>),
72 Drm(drm::DrmEnvelope),
73 Sync(Vec<sync::SyncCue>),
74}
75
76impl KnownExtra {
77 pub fn namespace(&self) -> &'static str {
78 match self {
79 Self::Anno(_) => ANNO_NAMESPACE,
80 Self::Drm(_) => DRM_NAMESPACE,
81 Self::Sync(_) => SYNC_NAMESPACE,
82 }
83 }
84}
85
86pub fn is_known_namespace(namespace: &str) -> bool {
87 if matches!(namespace, ANNO_NAMESPACE | DRM_NAMESPACE | SYNC_NAMESPACE) {
88 return true;
89 }
90 registry()
91 .read()
92 .expect("extra registry poisoned")
93 .contains_key(namespace)
94}
95
96pub fn parse_known(namespace: &str, body: &[u8]) -> Option<Result<KnownExtra, HonzoError>> {
97 match namespace {
98 ANNO_NAMESPACE => Some(anno::parse_anno(body).map(KnownExtra::Anno)),
99 DRM_NAMESPACE => Some(drm::parse_drm(body).map(KnownExtra::Drm)),
100 SYNC_NAMESPACE => Some(sync::parse_sync(body).map(KnownExtra::Sync)),
101 _ => None,
102 }
103}
104
105pub fn register_extra_parser(
106 namespace: impl Into<String>,
107 parser: impl Fn(&[u8]) -> Result<RegisteredExtraValue, HonzoError> + Send + Sync + 'static,
108) -> Option<Arc<RegisteredExtraParser>> {
109 registry()
110 .write()
111 .expect("extra registry poisoned")
112 .insert(namespace.into(), Arc::new(parser))
113}
114
115pub fn parse_registered(
116 namespace: &str,
117 body: &[u8],
118) -> Option<Result<RegisteredExtra, HonzoError>> {
119 let parser = registry()
120 .read()
121 .expect("extra registry poisoned")
122 .get(namespace)
123 .cloned()?;
124
125 Some(parser(body).map(|value| RegisteredExtra {
126 namespace: namespace.to_string(),
127 value,
128 }))
129}
130
131pub fn parse_extra(namespace: &str, body: &[u8]) -> Option<Result<ParsedExtra, HonzoError>> {
132 if let Some(parsed) = parse_known(namespace, body) {
133 return Some(parsed.map(ParsedExtra::Known));
134 }
135
136 parse_registered(namespace, body).map(|parsed| parsed.map(ParsedExtra::Registered))
137}