oxios_kernel/persona/
store.rs1use anyhow::{Result, anyhow};
10use parking_lot::RwLock;
11use std::collections::HashMap;
12use std::sync::Arc;
13
14use super::Persona;
15
16fn persona_priority(id: &str) -> usize {
19 const PRIORITY: &[&str] = &[
20 "dev",
21 "review",
22 "research",
23 "architect",
24 "mentor",
25 "ops",
26 "security",
27 "writer",
28 "planner",
29 ];
30 PRIORITY.iter().position(|&p| p == id).unwrap_or(usize::MAX)
31}
32
33#[derive(Debug, Default)]
35pub struct PersonaStore {
36 personas: RwLock<HashMap<String, Persona>>,
37}
38
39impl PersonaStore {
40 pub fn new() -> Self {
42 Self {
43 personas: RwLock::new(HashMap::new()),
44 }
45 }
46
47 pub fn register(&self, persona: Persona) {
49 let mut personas = self.personas.write();
50 personas.insert(persona.id.clone(), persona);
51 }
52
53 pub fn get(&self, id: &str) -> Option<Persona> {
55 let personas = self.personas.read();
56 personas.get(id).cloned()
57 }
58
59 pub fn list_enabled(&self) -> Vec<Persona> {
61 let personas = self.personas.read();
62 let mut result: Vec<Persona> = personas.values().filter(|p| p.enabled).cloned().collect();
63 result.sort_by_key(|p| persona_priority(&p.id));
64 result
65 }
66
67 pub fn list_all(&self) -> Vec<Persona> {
69 let personas = self.personas.read();
70 personas.values().cloned().collect()
71 }
72
73 pub fn set_enabled(&self, id: &str, enabled: bool) -> Result<()> {
75 let mut personas = self.personas.write();
76 match personas.get_mut(id) {
77 Some(p) => {
78 p.enabled = enabled;
79 Ok(())
80 }
81 None => Err(anyhow!("Persona '{id}' not found")),
82 }
83 }
84
85 pub fn delete(&self, id: &str) -> Result<()> {
87 let mut personas = self.personas.write();
88 if personas.remove(id).is_some() {
89 Ok(())
90 } else {
91 Err(anyhow!("Persona '{id}' not found"))
92 }
93 }
94
95 pub fn update(&self, id: &str, updated: Persona) -> Result<()> {
97 let mut personas = self.personas.write();
98 if personas.contains_key(id) {
99 personas.insert(id.to_string(), updated);
100 Ok(())
101 } else {
102 Err(anyhow!("Persona '{id}' not found"))
103 }
104 }
105
106 pub fn len(&self) -> usize {
108 let personas = self.personas.read();
109 personas.len()
110 }
111
112 pub fn is_empty(&self) -> bool {
114 let personas = self.personas.read();
115 personas.is_empty()
116 }
117
118 pub fn load_from_slice(&self, personas: &[Persona]) {
120 let mut store = self.personas.write();
121 for p in personas {
122 store.insert(p.id.clone(), p.clone());
123 }
124 }
125}
126
127#[derive(Clone)]
129pub struct PersonaStoreHandle {
130 store: Arc<PersonaStore>,
131}
132
133impl PersonaStoreHandle {
134 pub fn from_store(store: Arc<PersonaStore>) -> Self {
136 Self { store }
137 }
138
139 pub fn inner(&self) -> Arc<PersonaStore> {
141 Arc::clone(&self.store)
142 }
143}
144
145impl Default for PersonaStoreHandle {
146 fn default() -> Self {
147 Self {
148 store: Arc::new(PersonaStore::new()),
149 }
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn test_register_and_get() {
159 let store = PersonaStore::new();
160 let persona = Persona::new("Test", "assistant", "A test persona", "You are a test.");
161 store.register(persona.clone());
162 assert_eq!(store.get(&persona.id).unwrap().name, "Test");
163 }
164
165 #[test]
166 fn test_list_enabled() {
167 let store = PersonaStore::new();
168 let mut p1 = Persona::new("A", "dev", "Desc A", "Prompt A");
169 p1.enabled = true;
170 let mut p2 = Persona::new("B", "dev", "Desc B", "Prompt B");
171 p2.enabled = false;
172 store.register(p1);
173 store.register(p2);
174
175 let enabled = store.list_enabled();
176 assert_eq!(enabled.len(), 1);
177 assert_eq!(enabled[0].name, "A");
178 }
179
180 #[test]
181 fn test_set_enabled() {
182 let store = PersonaStore::new();
183 let persona = Persona::new("Test", "dev", "Desc", "Prompt");
184 store.register(persona.clone());
185 store.set_enabled(&persona.id, false).unwrap();
186 assert!(store.list_enabled().is_empty());
187
188 store.set_enabled(&persona.id, true).unwrap();
189 assert_eq!(store.list_enabled().len(), 1);
190 }
191
192 #[test]
193 fn test_delete() {
194 let store = PersonaStore::new();
195 let persona = Persona::new("Test", "dev", "Desc", "Prompt");
196 let id = persona.id.clone();
197 store.register(persona);
198 store.delete(&id).unwrap();
199 assert!(store.get(&id).is_none());
200 }
201}