use std::collections::HashMap;
use std::sync::Arc;
pub(crate) trait RegistryEntry {
fn name(&self) -> &str;
fn priority(&self) -> i32;
}
#[derive(Clone)]
pub(crate) struct SortedRegistry<T: RegistryEntry> {
storage: Option<Arc<RegistryStorage<T>>>,
}
#[derive(Clone)]
struct RegistryStorage<T: RegistryEntry> {
entries: HashMap<String, T>,
sorted_keys: Vec<String>,
}
impl<T: RegistryEntry> SortedRegistry<T> {
pub(crate) fn new() -> Self {
Self { storage: None }
}
pub(crate) fn register(&mut self, entry: T) -> Result<(), String>
where
T: Clone,
{
let name = entry.name();
if self
.storage
.as_ref()
.is_some_and(|storage| storage.entries.contains_key(name))
{
return Err(format!("{name} already exists"));
}
let name = name.to_string();
let storage = self.storage.get_or_insert_with(|| {
Arc::new(RegistryStorage {
entries: HashMap::new(),
sorted_keys: Vec::new(),
})
});
let storage = Arc::make_mut(storage);
storage.entries.insert(name, entry);
storage.resort();
Ok(())
}
pub(crate) fn deregister(&mut self, name: &str) -> bool
where
T: Clone,
{
let Some(storage) = self.storage.as_mut() else {
return false;
};
if !storage.entries.contains_key(name) {
return false;
}
let storage = Arc::make_mut(storage);
storage.entries.remove(name);
storage.resort();
true
}
pub(crate) fn sorted_values(&self) -> impl Iterator<Item = &T> {
self.storage.iter().flat_map(|storage| {
storage
.sorted_keys
.iter()
.filter_map(|key| storage.entries.get(key))
})
}
pub(crate) fn is_empty(&self) -> bool {
self.storage
.as_ref()
.is_none_or(|storage| storage.entries.is_empty())
}
pub(crate) fn values(&self) -> impl Iterator<Item = &T> {
self.storage
.iter()
.flat_map(|storage| storage.entries.values())
}
}
impl<T: RegistryEntry> RegistryStorage<T> {
fn resort(&mut self) {
let entries = &self.entries;
let mut keys: Vec<String> = entries.keys().cloned().collect();
keys.sort_by(|left, right| {
let left_entry = entries.get(left).unwrap();
let right_entry = entries.get(right).unwrap();
left_entry
.priority()
.cmp(&right_entry.priority())
.then_with(|| left.cmp(right))
});
self.sorted_keys = keys;
}
}
impl<T: RegistryEntry> Default for SortedRegistry<T> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[path = "../tests/unit/registry_tests.rs"]
mod tests;