#[cfg(coverage)]
use std::sync::PoisonError;
use std::sync::{
Arc,
LazyLock,
RwLock,
RwLockReadGuard,
RwLockWriteGuard,
};
use qubit_spi::{
ProviderRegistry,
ProviderSelection,
ServiceProvider,
};
use crate::{
MediaStreamClassifier,
MimeConfig,
MimeError,
MimeResult,
};
use super::{
FfprobeCommandMediaStreamClassifierProvider,
MediaStreamClassifierProvider,
MediaStreamClassifierSpec,
};
#[derive(Debug, Clone, Default)]
pub struct MediaStreamClassifierRegistry {
providers: ProviderRegistry<MediaStreamClassifierSpec>,
}
static DEFAULT_MEDIA_STREAM_CLASSIFIER_REGISTRY: LazyLock<RwLock<MediaStreamClassifierRegistry>> =
LazyLock::new(|| RwLock::new(MediaStreamClassifierRegistry::builtin()));
#[cfg(not(coverage))]
const BACKEND: &str = "media-stream-classifier-registry";
#[cfg(not(coverage))]
const LOCK_ERR: &str = "lock poisoned";
impl MediaStreamClassifierRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn builtin() -> Self {
let mut registry = Self::new();
registry
.register(FfprobeCommandMediaStreamClassifierProvider)
.expect("built-in FFprobe classifier provider should register");
registry
}
pub fn default_registry() -> MimeResult<Self> {
let registry = read_default_registry()?;
Ok(registry.clone())
}
pub fn register_default<P>(provider: P) -> MimeResult<()>
where
P: MediaStreamClassifierProvider + 'static,
{
let mut registry = write_default_registry()?;
registry.register(provider)
}
pub fn register<P>(&mut self, provider: P) -> MimeResult<()>
where
P: MediaStreamClassifierProvider + 'static,
{
self.providers
.register(provider)
.map_err(MimeError::classifier_registry_error)
}
pub fn register_shared<P>(&mut self, provider: Arc<P>) -> MimeResult<()>
where
P: MediaStreamClassifierProvider + 'static,
{
self.providers
.register_shared(provider)
.map_err(MimeError::classifier_registry_error)
}
pub fn register_arc<P>(&mut self, provider: Arc<P>) -> MimeResult<()>
where
P: MediaStreamClassifierProvider + 'static,
{
self.register_shared(provider)
}
pub fn provider_names(&self) -> Vec<&str> {
self.providers.provider_names()
}
pub fn find_provider(
&self,
name: &str,
) -> Option<&dyn ServiceProvider<MediaStreamClassifierSpec>> {
self.resolve_provider(name).ok()
}
pub fn resolve_provider(
&self,
name: &str,
) -> MimeResult<&dyn ServiceProvider<MediaStreamClassifierSpec>> {
self.providers
.resolve_provider(name)
.map_err(MimeError::classifier_registry_error)
}
pub fn create_box(
&self,
name: &str,
config: &MimeConfig,
) -> MimeResult<Box<dyn MediaStreamClassifier>> {
self.providers
.create_box(name, config)
.map_err(MimeError::classifier_registry_error)
}
pub fn create_arc(
&self,
name: &str,
config: &MimeConfig,
) -> MimeResult<Arc<dyn MediaStreamClassifier>> {
self.providers
.create_arc(name, config)
.map_err(MimeError::classifier_registry_error)
}
pub fn create_default_box(
&self,
config: &MimeConfig,
) -> MimeResult<Box<dyn MediaStreamClassifier>> {
let selection = provider_selection_from_config(config)?;
self.providers
.create_selected_box(&selection, config)
.map_err(MimeError::classifier_registry_error)
}
pub fn create_default_arc(
&self,
config: &MimeConfig,
) -> MimeResult<Arc<dyn MediaStreamClassifier>> {
let selection = provider_selection_from_config(config)?;
self.providers
.create_selected_arc(&selection, config)
.map_err(MimeError::classifier_registry_error)
}
}
fn provider_selection_from_config(config: &MimeConfig) -> MimeResult<ProviderSelection> {
let configured = config.media_stream_classifier_default().trim();
if configured.is_empty() || configured.eq_ignore_ascii_case("auto") {
return Ok(ProviderSelection::Auto);
}
ProviderSelection::named(configured).map_err(MimeError::classifier_registry_error)
}
#[cfg(not(coverage))]
fn read_default_registry() -> MimeResult<RwLockReadGuard<'static, MediaStreamClassifierRegistry>> {
match DEFAULT_MEDIA_STREAM_CLASSIFIER_REGISTRY.read() {
Ok(registry) => Ok(registry),
Err(_) => Err(MimeError::ClassifierBackend {
backend: BACKEND.into(),
reason: LOCK_ERR.into(),
}),
}
}
#[cfg(coverage)]
fn read_default_registry() -> MimeResult<RwLockReadGuard<'static, MediaStreamClassifierRegistry>> {
Ok(DEFAULT_MEDIA_STREAM_CLASSIFIER_REGISTRY
.read()
.unwrap_or_else(PoisonError::into_inner))
}
#[cfg(not(coverage))]
fn write_default_registry() -> MimeResult<RwLockWriteGuard<'static, MediaStreamClassifierRegistry>>
{
match DEFAULT_MEDIA_STREAM_CLASSIFIER_REGISTRY.write() {
Ok(registry) => Ok(registry),
Err(_) => Err(MimeError::ClassifierBackend {
backend: BACKEND.into(),
reason: LOCK_ERR.into(),
}),
}
}
#[cfg(coverage)]
fn write_default_registry() -> MimeResult<RwLockWriteGuard<'static, MediaStreamClassifierRegistry>>
{
Ok(DEFAULT_MEDIA_STREAM_CLASSIFIER_REGISTRY
.write()
.unwrap_or_else(PoisonError::into_inner))
}