1use std::collections::{HashMap, HashSet};
28use std::sync::Arc;
29
30use crate::registry::prompts::Prompt;
31use crate::registry::resources::Resource;
32use crate::registry::tools::Tool;
33
34#[derive(Default)]
42pub struct SessionProfile {
43 pub name: String,
45
46 pub tool_extras: Vec<Arc<dyn Tool>>,
49 pub tool_overrides: HashMap<String, Arc<dyn Tool>>,
51 pub tool_hidden: HashSet<String>,
53 pub tool_aliases: HashMap<String, String>,
55
56 pub resource_extras: Vec<Arc<dyn Resource>>,
59 pub resource_overrides: HashMap<String, Arc<dyn Resource>>,
61 pub resource_hidden: HashSet<String>,
63
64 pub prompt_extras: Vec<Arc<dyn Prompt>>,
67 pub prompt_overrides: HashMap<String, Arc<dyn Prompt>>,
69 pub prompt_hidden: HashSet<String>,
71}
72
73impl SessionProfile {
74 pub fn new(name: impl Into<String>) -> Self {
76 Self {
77 name: name.into(),
78 ..Default::default()
79 }
80 }
81
82 pub fn admin() -> Self {
84 Self::new("admin")
85 }
86
87 pub fn vip() -> Self {
89 Self::new("vip")
90 }
91
92 pub fn readonly() -> Self {
94 Self::new("readonly")
95 }
96
97 pub fn guest() -> Self {
99 Self::new("guest")
100 }
101
102 pub fn add_tool(&mut self, tool: Arc<dyn Tool>) -> &mut Self {
106 self.tool_extras.push(tool);
107 self
108 }
109
110 pub fn override_tool(&mut self, name: impl Into<String>, tool: Arc<dyn Tool>) -> &mut Self {
112 self.tool_overrides.insert(name.into(), tool);
113 self
114 }
115
116 pub fn hide_tool(&mut self, name: impl Into<String>) -> &mut Self {
118 self.tool_hidden.insert(name.into());
119 self
120 }
121
122 pub fn hide_tools(&mut self, names: impl IntoIterator<Item = impl Into<String>>) -> &mut Self {
124 for name in names {
125 self.tool_hidden.insert(name.into());
126 }
127 self
128 }
129
130 pub fn alias_tool(&mut self, alias: impl Into<String>, target: impl Into<String>) -> &mut Self {
132 self.tool_aliases.insert(alias.into(), target.into());
133 self
134 }
135
136 pub fn add_resource(&mut self, resource: Arc<dyn Resource>) -> &mut Self {
140 self.resource_extras.push(resource);
141 self
142 }
143
144 pub fn override_resource(
146 &mut self,
147 uri: impl Into<String>,
148 resource: Arc<dyn Resource>,
149 ) -> &mut Self {
150 self.resource_overrides.insert(uri.into(), resource);
151 self
152 }
153
154 pub fn hide_resource(&mut self, uri: impl Into<String>) -> &mut Self {
156 self.resource_hidden.insert(uri.into());
157 self
158 }
159
160 pub fn hide_resources(
162 &mut self,
163 uris: impl IntoIterator<Item = impl Into<String>>,
164 ) -> &mut Self {
165 for uri in uris {
166 self.resource_hidden.insert(uri.into());
167 }
168 self
169 }
170
171 pub fn add_prompt(&mut self, prompt: Arc<dyn Prompt>) -> &mut Self {
175 self.prompt_extras.push(prompt);
176 self
177 }
178
179 pub fn override_prompt(
181 &mut self,
182 name: impl Into<String>,
183 prompt: Arc<dyn Prompt>,
184 ) -> &mut Self {
185 self.prompt_overrides.insert(name.into(), prompt);
186 self
187 }
188
189 pub fn hide_prompt(&mut self, name: impl Into<String>) -> &mut Self {
191 self.prompt_hidden.insert(name.into());
192 self
193 }
194
195 pub fn hide_prompts(
197 &mut self,
198 names: impl IntoIterator<Item = impl Into<String>>,
199 ) -> &mut Self {
200 for name in names {
201 self.prompt_hidden.insert(name.into());
202 }
203 self
204 }
205
206 pub fn merge(&mut self, other: &SessionProfile) -> &mut Self {
210 self.tool_extras.extend(other.tool_extras.iter().cloned());
212 self.tool_overrides.extend(
213 other
214 .tool_overrides
215 .iter()
216 .map(|(k, v)| (k.clone(), v.clone())),
217 );
218 self.tool_hidden.extend(other.tool_hidden.iter().cloned());
219 self.tool_aliases.extend(
220 other
221 .tool_aliases
222 .iter()
223 .map(|(k, v)| (k.clone(), v.clone())),
224 );
225
226 self.resource_extras
228 .extend(other.resource_extras.iter().cloned());
229 self.resource_overrides.extend(
230 other
231 .resource_overrides
232 .iter()
233 .map(|(k, v)| (k.clone(), v.clone())),
234 );
235 self.resource_hidden
236 .extend(other.resource_hidden.iter().cloned());
237
238 self.prompt_extras
240 .extend(other.prompt_extras.iter().cloned());
241 self.prompt_overrides.extend(
242 other
243 .prompt_overrides
244 .iter()
245 .map(|(k, v)| (k.clone(), v.clone())),
246 );
247 self.prompt_hidden
248 .extend(other.prompt_hidden.iter().cloned());
249
250 self
251 }
252}
253
254impl std::fmt::Debug for SessionProfile {
255 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256 f.debug_struct("SessionProfile")
257 .field("name", &self.name)
258 .field("tool_extras_count", &self.tool_extras.len())
259 .field(
260 "tool_overrides",
261 &self.tool_overrides.keys().collect::<Vec<_>>(),
262 )
263 .field("tool_hidden", &self.tool_hidden)
264 .field("tool_aliases", &self.tool_aliases)
265 .field("resource_extras_count", &self.resource_extras.len())
266 .field(
267 "resource_overrides",
268 &self.resource_overrides.keys().collect::<Vec<_>>(),
269 )
270 .field("resource_hidden", &self.resource_hidden)
271 .field("prompt_extras_count", &self.prompt_extras.len())
272 .field(
273 "prompt_overrides",
274 &self.prompt_overrides.keys().collect::<Vec<_>>(),
275 )
276 .field("prompt_hidden", &self.prompt_hidden)
277 .finish()
278 }
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284
285 #[test]
286 fn test_profile_creation() {
287 let profile = SessionProfile::new("test");
288 assert_eq!(profile.name, "test");
289 assert!(profile.tool_extras.is_empty());
290 assert!(profile.tool_overrides.is_empty());
291 assert!(profile.tool_hidden.is_empty());
292 }
293
294 #[test]
295 fn test_profile_presets() {
296 let admin = SessionProfile::admin();
297 assert_eq!(admin.name, "admin");
298
299 let vip = SessionProfile::vip();
300 assert_eq!(vip.name, "vip");
301
302 let readonly = SessionProfile::readonly();
303 assert_eq!(readonly.name, "readonly");
304
305 let guest = SessionProfile::guest();
306 assert_eq!(guest.name, "guest");
307 }
308
309 #[test]
310 fn test_hide_tools() {
311 let mut profile = SessionProfile::new("test");
312 profile.hide_tool("delete");
313 profile.hide_tools(["admin_panel", "system_reboot"]);
314
315 assert!(profile.tool_hidden.contains("delete"));
316 assert!(profile.tool_hidden.contains("admin_panel"));
317 assert!(profile.tool_hidden.contains("system_reboot"));
318 assert_eq!(profile.tool_hidden.len(), 3);
319 }
320
321 #[test]
322 fn test_tool_aliases() {
323 let mut profile = SessionProfile::new("test");
324 profile.alias_tool("save", "git_commit");
325 profile.alias_tool("deploy", "kubernetes_apply");
326
327 assert_eq!(
328 profile.tool_aliases.get("save"),
329 Some(&"git_commit".to_string())
330 );
331 assert_eq!(
332 profile.tool_aliases.get("deploy"),
333 Some(&"kubernetes_apply".to_string())
334 );
335 }
336
337 #[test]
338 fn test_profile_merge() {
339 let mut base = SessionProfile::new("base");
340 base.hide_tool("tool1");
341 base.alias_tool("a", "b");
342
343 let mut extra = SessionProfile::new("extra");
344 extra.hide_tool("tool2");
345 extra.alias_tool("c", "d");
346
347 base.merge(&extra);
348
349 assert!(base.tool_hidden.contains("tool1"));
350 assert!(base.tool_hidden.contains("tool2"));
351 assert_eq!(base.tool_aliases.get("a"), Some(&"b".to_string()));
352 assert_eq!(base.tool_aliases.get("c"), Some(&"d".to_string()));
353 }
354
355 #[test]
356 fn test_builder_pattern() {
357 let mut profile = SessionProfile::new("test");
358 profile
359 .hide_tool("dangerous")
360 .alias_tool("save", "commit")
361 .hide_resource("secret://data");
362
363 assert!(profile.tool_hidden.contains("dangerous"));
364 assert_eq!(
365 profile.tool_aliases.get("save"),
366 Some(&"commit".to_string())
367 );
368 assert!(profile.resource_hidden.contains("secret://data"));
369 }
370}