1use std::collections::HashMap;
4use std::sync::Arc;
5
6use crate::error::{ToolError, codes};
7use crate::metadata::CapabilityFlag;
8use crate::tool::{DynTool, SharedTool, ToolDefinition};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
12#[serde(rename_all = "snake_case")]
13#[non_exhaustive]
14pub enum CapabilityMode {
15 #[default]
17 Full,
18 ReadOnly,
20 Plan,
22}
23
24impl CapabilityMode {
25 #[must_use]
27 pub fn parse(s: &str) -> Option<Self> {
28 match s.trim().to_ascii_lowercase().as_str() {
29 "full" => Some(Self::Full),
30 "read_only" | "read-only" | "readonly" => Some(Self::ReadOnly),
31 "plan" => Some(Self::Plan),
32 _ => None,
33 }
34 }
35
36 #[must_use]
38 pub const fn intersect(self, other: Self) -> Self {
39 use CapabilityMode::{Full, Plan, ReadOnly};
40 match (self, other) {
41 (ReadOnly, _) | (_, ReadOnly) => ReadOnly,
42 (Plan, _) | (_, Plan) => Plan,
43 (Full, Full) => Full,
44 }
45 }
46}
47
48#[derive(Clone, Default)]
50pub struct ToolRegistry {
51 tools: Arc<HashMap<String, SharedTool>>,
52}
53
54impl std::fmt::Debug for ToolRegistry {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 let mut names: Vec<_> = self.tools.keys().map(String::as_str).collect();
57 names.sort_unstable();
58 f.debug_struct("ToolRegistry")
59 .field("tools", &names)
60 .finish()
61 }
62}
63
64impl ToolRegistry {
65 #[must_use]
67 pub fn new() -> Self {
68 Self::default()
69 }
70
71 #[must_use]
73 pub fn from_tools(tools: Vec<SharedTool>) -> Self {
74 let mut map = HashMap::new();
75 for tool in tools {
76 map.insert(tool.name().to_owned(), tool);
77 }
78 Self {
79 tools: Arc::new(map),
80 }
81 }
82
83 #[must_use]
85 pub fn get(&self, name: &str) -> Option<SharedTool> {
86 self.tools.get(name).cloned()
87 }
88
89 pub fn require(&self, name: &str) -> Result<SharedTool, ToolError> {
95 self.get(name).ok_or_else(|| codes::not_found(name))
96 }
97
98 #[must_use]
100 pub fn definitions(&self, mode: CapabilityMode) -> Vec<ToolDefinition> {
101 let mut defs: Vec<_> = self
102 .tools
103 .values()
104 .filter(|t| self.allows(t.as_ref(), mode))
105 .map(|t| t.definition())
106 .collect();
107 defs.sort_by(|a, b| a.name.cmp(&b.name));
108 defs
109 }
110
111 #[must_use]
113 pub fn allows(&self, tool: &dyn DynTool, mode: CapabilityMode) -> bool {
114 let _ = self;
115 let meta = tool.metadata();
116 match mode {
117 CapabilityMode::Full => true,
118 CapabilityMode::ReadOnly => meta.allowed_in_read_only(),
119 CapabilityMode::Plan => !meta
120 .capabilities
121 .iter()
122 .any(|c| matches!(c, CapabilityFlag::Execute | CapabilityFlag::Spawn)),
123 }
124 }
125
126 #[must_use]
128 pub fn len(&self) -> usize {
129 self.tools.len()
130 }
131
132 #[must_use]
134 pub fn is_empty(&self) -> bool {
135 self.tools.is_empty()
136 }
137
138 #[must_use]
140 pub fn merge(&self, other: &Self) -> Self {
141 let mut map = (*self.tools).clone();
142 for (k, v) in other.tools.iter() {
143 map.insert(k.clone(), Arc::clone(v));
144 }
145 Self {
146 tools: Arc::new(map),
147 }
148 }
149
150 #[must_use]
152 pub fn names(&self) -> Vec<String> {
153 let mut n: Vec<_> = self.tools.keys().cloned().collect();
154 n.sort_unstable();
155 n
156 }
157}