use std::collections::HashMap;
use std::sync::{Arc, OnceLock, RwLock};
use crate::errors;
use crate::spec::Entry;
pub struct Registry {
entries: RwLock<HashMap<String, Arc<Entry>>>,
}
impl Default for Registry {
fn default() -> Self {
Registry {
entries: RwLock::new(HashMap::new()),
}
}
}
impl Registry {
pub fn new() -> Self {
Registry {
entries: RwLock::new(HashMap::new()),
}
}
#[allow(clippy::should_implement_trait)]
pub fn default() -> &'static Registry {
static DEFAULT: OnceLock<Registry> = OnceLock::new();
DEFAULT.get_or_init(Registry::new)
}
pub fn add(&self, e: Arc<Entry>) -> errors::Result<Arc<Entry>> {
if e.name.is_empty() {
return Err(errors::Error::new(
errors::Kind::Internal,
"registry: entry has empty name".to_string(),
));
}
let mut entries = self.entries.write().unwrap();
if let Some(old) = entries.get(&e.name) {
return Err(errors::Error::new(
errors::Kind::Internal,
format!(
"registry: name {:?} already registered (existing summary {:?})",
e.name, old.summary
),
));
}
entries.insert(e.name.clone(), Arc::clone(&e));
Ok(e)
}
pub fn get(&self, name: &str) -> Option<Arc<Entry>> {
self.entries.read().unwrap().get(name).cloned()
}
pub fn names(&self) -> Vec<String> {
let mut names: Vec<String> = self.entries.read().unwrap().keys().cloned().collect();
names.sort();
names
}
pub fn all(&self) -> Vec<Arc<Entry>> {
let mut out: Vec<Arc<Entry>> = self.entries.read().unwrap().values().cloned().collect();
out.sort_by(|a, b| a.name.cmp(&b.name));
out
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Ctx;
use crate::spec::command::Command;
fn entry(name: &str) -> Arc<Entry> {
#[derive(crate::XyzArgs)]
struct A {
#[xyz(desc = "s")]
s: String,
}
fn h(_: &Ctx, _: &A) -> crate::errors::Result<String> {
Ok(String::new())
}
Arc::new(Command::new(name, h).entry().unwrap())
}
#[test]
fn add_dup_is_error() {
let r = Registry::new();
let e = entry("a.b");
r.add(Arc::clone(&e)).unwrap();
let err = r.add(Arc::clone(&e)).unwrap_err();
assert!(err.to_string().contains("already registered"), "{err}");
}
#[test]
fn names_and_all_sorted() {
let r = Registry::new();
for n in ["c.x", "a.y", "b.z"] {
r.add(entry(n)).unwrap();
}
assert_eq!(r.names(), vec!["a.y", "b.z", "c.x"]);
let all = r.all();
assert_eq!(
all.iter().map(|e| e.name.as_str()).collect::<Vec<_>>(),
vec!["a.y", "b.z", "c.x"]
);
}
#[test]
fn get_hit_and_miss() {
let r = Registry::new();
r.add(entry("x.y")).unwrap();
assert!(r.get("x.y").is_some());
assert!(r.get("nope").is_none());
}
}