use std::collections::HashMap;
use std::sync::Arc;
use crate::cognitive::CognitiveState;
use crate::lobe::{Lobe, LobeContext};
pub struct LobeRegistry {
lobes: HashMap<String, Arc<dyn Lobe>>,
}
const _: () = {
fn _assert_send_sync<T: Send + Sync>() {}
fn _check() {
_assert_send_sync::<LobeRegistry>();
}
};
impl LobeRegistry {
pub fn new() -> Self {
Self {
lobes: HashMap::new(),
}
}
pub fn register(&mut self, lobe: Arc<dyn Lobe>) -> &mut Self {
self.lobes.insert(lobe.name().to_string(), lobe);
self
}
pub fn unregister(&mut self, name: &str) -> Option<Arc<dyn Lobe>> {
self.lobes.remove(name)
}
pub fn get(&self, name: &str) -> Option<&Arc<dyn Lobe>> {
self.lobes.get(name)
}
pub fn contains(&self, name: &str) -> bool {
self.lobes.contains_key(name)
}
pub fn len(&self) -> usize {
self.lobes.len()
}
pub fn is_empty(&self) -> bool {
self.lobes.is_empty()
}
pub fn all(&self) -> Vec<&Arc<dyn Lobe>> {
self.lobes.values().collect()
}
pub fn sorted_by_priority(&self) -> Vec<&Arc<dyn Lobe>> {
let mut lobes: Vec<_> = self.lobes.values().collect();
lobes.sort_by_key(|l| l.priority());
lobes
}
pub fn active_lobes(&self, state: &CognitiveState) -> Vec<&Arc<dyn Lobe>> {
let context = LobeContext::from_cognitive_state(state);
self.active_lobes_for_context(&context)
}
pub fn active_lobes_for_context(&self, context: &LobeContext) -> Vec<&Arc<dyn Lobe>> {
let mut active: Vec<_> = self
.lobes
.values()
.filter(|l| l.should_activate(context))
.collect();
active.sort_by_key(|l| l.priority());
active
}
pub fn into_lobes(self) -> Vec<(String, Arc<dyn Lobe>)> {
self.lobes.into_iter().collect()
}
}
impl Default for LobeRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lobe::{LobeBudget, LobeFuture, LobeInput, LobeOutput, LobeOutputFormat};
struct TestLobe {
name: &'static str,
priority: u32,
active: bool,
}
impl Lobe for TestLobe {
fn name(&self) -> &str {
self.name
}
fn should_activate(&self, _: &LobeContext) -> bool {
self.active
}
fn priority(&self) -> u32 {
self.priority
}
fn budget(&self) -> LobeBudget {
LobeBudget::default()
}
fn output_format(&self) -> LobeOutputFormat {
LobeOutputFormat::FreeText
}
fn process(&self, _: &LobeInput) -> LobeFuture {
Box::pin(async { Ok(LobeOutput::new("test", 0.5)) })
}
}
#[test]
fn test_register_and_get() {
let mut reg = LobeRegistry::new();
reg.register(Arc::new(TestLobe {
name: "analyst",
priority: 10,
active: true,
}));
assert_eq!(reg.len(), 1);
assert!(reg.contains("analyst"));
assert!(reg.get("analyst").is_some());
assert!(reg.get("nonexistent").is_none());
}
#[test]
fn test_register_replaces_duplicate() {
let mut reg = LobeRegistry::new();
reg.register(Arc::new(TestLobe {
name: "critic",
priority: 10,
active: true,
}));
reg.register(Arc::new(TestLobe {
name: "critic",
priority: 20,
active: true,
}));
assert_eq!(reg.len(), 1);
assert_eq!(reg.get("critic").unwrap().priority(), 20);
}
#[test]
fn test_unregister() {
let mut reg = LobeRegistry::new();
reg.register(Arc::new(TestLobe {
name: "analyst",
priority: 10,
active: true,
}));
let removed = reg.unregister("analyst");
assert!(removed.is_some());
assert!(reg.is_empty());
assert!(reg.unregister("analyst").is_none());
}
#[test]
fn test_sorted_by_priority() {
let mut reg = LobeRegistry::new();
reg.register(Arc::new(TestLobe {
name: "low",
priority: 30,
active: true,
}));
reg.register(Arc::new(TestLobe {
name: "high",
priority: 5,
active: true,
}));
reg.register(Arc::new(TestLobe {
name: "mid",
priority: 15,
active: true,
}));
let sorted = reg.sorted_by_priority();
assert_eq!(sorted[0].name(), "high");
assert_eq!(sorted[1].name(), "mid");
assert_eq!(sorted[2].name(), "low");
}
#[test]
fn test_active_lobes_filters_inactive() {
let mut reg = LobeRegistry::new();
reg.register(Arc::new(TestLobe {
name: "always_on",
priority: 10,
active: true,
}));
reg.register(Arc::new(TestLobe {
name: "dormant",
priority: 5,
active: false,
}));
let state = CognitiveState::default();
let active = reg.active_lobes(&state);
assert_eq!(active.len(), 1);
assert_eq!(active[0].name(), "always_on");
}
#[test]
fn test_active_lobes_sorted_by_priority() {
let mut reg = LobeRegistry::new();
reg.register(Arc::new(TestLobe {
name: "second",
priority: 20,
active: true,
}));
reg.register(Arc::new(TestLobe {
name: "first",
priority: 5,
active: true,
}));
let state = CognitiveState::default();
let active = reg.active_lobes(&state);
assert_eq!(active[0].name(), "first");
assert_eq!(active[1].name(), "second");
}
#[test]
fn test_empty_registry() {
let reg = LobeRegistry::new();
assert!(reg.is_empty());
assert_eq!(reg.len(), 0);
assert!(reg.all().is_empty());
assert!(reg.sorted_by_priority().is_empty());
}
#[test]
fn test_into_lobes() {
let mut reg = LobeRegistry::new();
reg.register(Arc::new(TestLobe {
name: "a",
priority: 10,
active: true,
}));
reg.register(Arc::new(TestLobe {
name: "b",
priority: 20,
active: true,
}));
let pairs = reg.into_lobes();
assert_eq!(pairs.len(), 2);
}
}