#[cfg(coverage)]
use std::sync::PoisonError;
use std::sync::{
Arc,
LazyLock,
RwLock,
RwLockReadGuard,
RwLockWriteGuard,
};
use qubit_spi::{
ProviderRegistry,
ProviderSelection,
ServiceProvider,
};
use crate::{
MimeConfig,
MimeDetector,
MimeError,
MimeResult,
};
use super::{
FileCommandMimeDetectorProvider,
MimeDetectorProvider,
MimeDetectorSpec,
RepositoryMimeDetectorProvider,
};
#[derive(Debug, Clone, Default)]
pub struct MimeDetectorRegistry {
providers: ProviderRegistry<MimeDetectorSpec>,
}
static DEFAULT_MIME_DETECTOR_REGISTRY: LazyLock<RwLock<MimeDetectorRegistry>> =
LazyLock::new(|| RwLock::new(MimeDetectorRegistry::builtin()));
#[cfg(not(coverage))]
const BACKEND: &str = "mime-detector-registry";
#[cfg(not(coverage))]
const LOCK_ERR: &str = "lock poisoned";
impl MimeDetectorRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn builtin() -> Self {
let mut registry = Self::new();
registry
.register(RepositoryMimeDetectorProvider)
.expect("built-in repository MIME provider should register");
registry
.register(FileCommandMimeDetectorProvider)
.expect("built-in file MIME 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: MimeDetectorProvider + 'static,
{
let mut registry = write_default_registry()?;
registry.register(provider)
}
pub fn register<P>(&mut self, provider: P) -> MimeResult<()>
where
P: MimeDetectorProvider + 'static,
{
self.providers.register(provider).map_err(MimeError::from)
}
pub fn register_shared<P>(&mut self, provider: Arc<P>) -> MimeResult<()>
where
P: MimeDetectorProvider + 'static,
{
self.providers
.register_shared(provider)
.map_err(MimeError::from)
}
pub fn provider_names(&self) -> Vec<&str> {
self.providers.provider_names()
}
pub fn find_provider(&self, name: &str) -> Option<&dyn ServiceProvider<MimeDetectorSpec>> {
self.resolve_provider(name).ok()
}
pub fn resolve_provider(
&self,
name: &str,
) -> MimeResult<&dyn ServiceProvider<MimeDetectorSpec>> {
self.providers
.resolve_provider(name)
.map_err(MimeError::from)
}
pub fn create_box(&self, name: &str, config: &MimeConfig) -> MimeResult<Box<dyn MimeDetector>> {
self.providers
.create_box(name, config)
.map_err(MimeError::from)
}
pub fn create_arc(&self, name: &str, config: &MimeConfig) -> MimeResult<Arc<dyn MimeDetector>> {
self.providers
.create_arc(name, config)
.map_err(MimeError::from)
}
pub fn create_default_box(&self, config: &MimeConfig) -> MimeResult<Box<dyn MimeDetector>> {
let selection = provider_selection_from_config(config)?;
self.providers
.create_selected_box(&selection, config)
.map_err(MimeError::from)
}
pub fn create_default_arc(&self, config: &MimeConfig) -> MimeResult<Arc<dyn MimeDetector>> {
let selection = provider_selection_from_config(config)?;
self.providers
.create_selected_arc(&selection, config)
.map_err(MimeError::from)
}
}
fn provider_selection_from_config(config: &MimeConfig) -> MimeResult<ProviderSelection> {
let configured = config.mime_detector_default().trim();
if configured.is_empty() || configured.eq_ignore_ascii_case("auto") {
return Ok(ProviderSelection::Auto);
}
ProviderSelection::from_owned_names(configured, config.mime_detector_fallbacks())
.map_err(MimeError::from)
}
#[cfg(not(coverage))]
fn read_default_registry() -> MimeResult<RwLockReadGuard<'static, MimeDetectorRegistry>> {
match DEFAULT_MIME_DETECTOR_REGISTRY.read() {
Ok(registry) => Ok(registry),
Err(_) => Err(MimeError::DetectorBackend {
backend: BACKEND.into(),
reason: LOCK_ERR.into(),
}),
}
}
#[cfg(coverage)]
fn read_default_registry() -> MimeResult<RwLockReadGuard<'static, MimeDetectorRegistry>> {
Ok(DEFAULT_MIME_DETECTOR_REGISTRY
.read()
.unwrap_or_else(PoisonError::into_inner))
}
#[cfg(not(coverage))]
fn write_default_registry() -> MimeResult<RwLockWriteGuard<'static, MimeDetectorRegistry>> {
match DEFAULT_MIME_DETECTOR_REGISTRY.write() {
Ok(registry) => Ok(registry),
Err(_) => Err(MimeError::DetectorBackend {
backend: BACKEND.into(),
reason: LOCK_ERR.into(),
}),
}
}
#[cfg(coverage)]
fn write_default_registry() -> MimeResult<RwLockWriteGuard<'static, MimeDetectorRegistry>> {
Ok(DEFAULT_MIME_DETECTOR_REGISTRY
.write()
.unwrap_or_else(PoisonError::into_inner))
}