use crate::{types::LanguageId, DynAdapter, LanguageAdapter};
use ahash::AHashMap;
use parking_lot::RwLock;
use std::sync::Arc;
#[derive(Default)]
pub struct LanguageRegistry {
inner: RwLock<Inner>,
}
#[derive(Default)]
struct Inner {
by_id: AHashMap<LanguageId, DynAdapter>,
by_ext: AHashMap<String, Vec<DynAdapter>>,
}
impl std::fmt::Debug for LanguageRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let inner = self.inner.read();
f.debug_struct("LanguageRegistry")
.field("adapters", &inner.by_id.len())
.finish()
}
}
pub type AdapterArc = Arc<dyn LanguageAdapter>;
impl LanguageRegistry {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn register(&self, adapter: AdapterArc) {
let mut inner = self.inner.write();
for ext in adapter.file_extensions() {
let candidates = inner.by_ext.entry((*ext).to_ascii_lowercase()).or_default();
if !candidates
.iter()
.any(|candidate| candidate.language_id() == adapter.language_id())
{
candidates.push(adapter.clone());
}
}
inner.by_id.insert(adapter.language_id(), adapter);
}
pub fn adapter_for_extension(&self, ext: &str) -> Option<AdapterArc> {
self.inner
.read()
.by_ext
.get(&ext.to_ascii_lowercase())
.and_then(|candidates| candidates.first())
.cloned()
}
pub fn adapters_for_extension(&self, ext: &str) -> Vec<AdapterArc> {
self.inner
.read()
.by_ext
.get(&ext.to_ascii_lowercase())
.cloned()
.unwrap_or_default()
}
pub fn adapter(&self, id: LanguageId) -> Option<AdapterArc> {
self.inner.read().by_id.get(&id).cloned()
}
pub fn all(&self) -> Vec<AdapterArc> {
self.inner.read().by_id.values().cloned().collect()
}
#[must_use]
pub fn len(&self) -> usize {
self.inner.read().by_id.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.inner.read().by_id.is_empty()
}
}