1use serde_json::{Map, Value};
28use std::collections::HashMap;
29
30#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct ToolNotRegisteredError(pub String);
34
35impl std::fmt::Display for ToolNotRegisteredError {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 write!(f, "tool {:?} is not registered", self.0)
38 }
39}
40
41impl std::error::Error for ToolNotRegisteredError {}
42
43#[derive(Debug, Default, Clone)]
52pub struct ToolDefaults {
53 defaults: HashMap<String, Map<String, Value>>,
54}
55
56impl ToolDefaults {
57 pub fn new() -> Self {
58 Self::default()
59 }
60
61 pub fn register(&mut self, tool_name: impl Into<String>, defaults: Value) {
67 let obj = match defaults {
68 Value::Object(m) => m,
69 other => panic!("defaults must be a JSON object, got {other:?}"),
70 };
71 self.defaults.insert(tool_name.into(), obj);
72 }
73
74 pub fn register_many(&mut self, map: Value) {
76 if let Value::Object(outer) = map {
77 for (name, val) in outer {
78 self.register(name, val);
79 }
80 }
81 }
82
83 pub fn update(&mut self, tool_name: impl Into<String>, extra: Value) {
85 let name = tool_name.into();
86 let target = self.defaults.entry(name).or_default();
87 if let Value::Object(m) = extra {
88 target.extend(m);
89 }
90 }
91
92 pub fn unregister(&mut self, tool_name: &str) -> bool {
94 self.defaults.remove(tool_name).is_some()
95 }
96
97 pub fn tool_names(&self) -> Vec<&str> {
101 let mut names: Vec<&str> = self.defaults.keys().map(|s| s.as_str()).collect();
102 names.sort_unstable();
103 names
104 }
105
106 pub fn defaults_for(&self, tool_name: &str) -> Option<Map<String, Value>> {
108 self.defaults.get(tool_name).cloned()
109 }
110
111 pub fn contains(&self, tool_name: &str) -> bool {
112 self.defaults.contains_key(tool_name)
113 }
114
115 pub fn apply(
123 &self,
124 tool_name: &str,
125 args: &Value,
126 strict: bool,
127 ) -> Result<Value, ToolNotRegisteredError> {
128 let supplied: Map<String, Value> = match args {
129 Value::Object(m) => m.clone(),
130 Value::Null => Map::new(),
131 _ => Map::new(),
132 };
133 let Some(tool_defaults) = self.defaults.get(tool_name) else {
134 if strict {
135 return Err(ToolNotRegisteredError(tool_name.to_owned()));
136 }
137 return Ok(Value::Object(supplied));
138 };
139 let mut merged = tool_defaults.clone();
141 for (k, v) in supplied {
142 merged.insert(k, v);
143 }
144 Ok(Value::Object(merged))
145 }
146}
147
148#[cfg(test)]
151mod tests {
152 use super::*;
153 use serde_json::json;
154
155 #[test]
156 fn fill_missing_args() {
157 let mut d = ToolDefaults::new();
158 d.register("search", json!({"timeout": 30, "max_results": 10}));
159 let out = d.apply("search", &json!({"q": "hello"}), false).unwrap();
160 assert_eq!(out["q"], json!("hello"));
161 assert_eq!(out["timeout"], json!(30));
162 assert_eq!(out["max_results"], json!(10));
163 }
164
165 #[test]
166 fn caller_wins_on_conflict() {
167 let mut d = ToolDefaults::new();
168 d.register("search", json!({"timeout": 30}));
169 let out = d
170 .apply("search", &json!({"timeout": 5, "q": "x"}), false)
171 .unwrap();
172 assert_eq!(out["timeout"], json!(5));
173 }
174
175 #[test]
176 fn null_is_real_value() {
177 let mut d = ToolDefaults::new();
178 d.register("fetch", json!({"follow_redirects": true}));
179 let out = d
180 .apply("fetch", &json!({"follow_redirects": null}), false)
181 .unwrap();
182 assert_eq!(out["follow_redirects"], Value::Null);
183 }
184
185 #[test]
186 fn unknown_tool_non_strict() {
187 let d = ToolDefaults::new();
188 let out = d.apply("ghost", &json!({"x": 1}), false).unwrap();
189 assert_eq!(out["x"], json!(1));
190 }
191
192 #[test]
193 fn unknown_tool_strict() {
194 let d = ToolDefaults::new();
195 let err = d.apply("ghost", &json!({}), true).unwrap_err();
196 assert_eq!(err.0, "ghost");
197 }
198
199 #[test]
200 fn empty_args_fills_all_defaults() {
201 let mut d = ToolDefaults::new();
202 d.register("tool", json!({"a": 1, "b": 2}));
203 let out = d.apply("tool", &json!({}), false).unwrap();
204 assert_eq!(out["a"], json!(1));
205 assert_eq!(out["b"], json!(2));
206 }
207
208 #[test]
209 fn null_args_treated_as_empty() {
210 let mut d = ToolDefaults::new();
211 d.register("tool", json!({"x": 99}));
212 let out = d.apply("tool", &Value::Null, false).unwrap();
213 assert_eq!(out["x"], json!(99));
214 }
215
216 #[test]
217 fn register_overwrites() {
218 let mut d = ToolDefaults::new();
219 d.register("t", json!({"a": 1}));
220 d.register("t", json!({"a": 99, "b": 2}));
221 let out = d.apply("t", &json!({}), false).unwrap();
222 assert_eq!(out["a"], json!(99));
223 assert_eq!(out["b"], json!(2));
224 }
225
226 #[test]
227 fn update_merges() {
228 let mut d = ToolDefaults::new();
229 d.register("t", json!({"a": 1}));
230 d.update("t", json!({"b": 2}));
231 let out = d.apply("t", &json!({}), false).unwrap();
232 assert_eq!(out["a"], json!(1));
233 assert_eq!(out["b"], json!(2));
234 }
235
236 #[test]
237 fn update_creates_if_absent() {
238 let mut d = ToolDefaults::new();
239 d.update("new_tool", json!({"x": 42}));
240 let out = d.apply("new_tool", &json!({}), false).unwrap();
241 assert_eq!(out["x"], json!(42));
242 }
243
244 #[test]
245 fn unregister_returns_true_if_was_registered() {
246 let mut d = ToolDefaults::new();
247 d.register("t", json!({"a": 1}));
248 assert!(d.unregister("t"));
249 assert!(!d.unregister("t")); }
251
252 #[test]
253 fn unregister_then_non_strict() {
254 let mut d = ToolDefaults::new();
255 d.register("t", json!({"a": 1}));
256 d.unregister("t");
257 let out = d.apply("t", &json!({"x": 7}), false).unwrap();
258 assert_eq!(out["x"], json!(7));
259 assert!(!out.as_object().unwrap().contains_key("a"));
260 }
261
262 #[test]
263 fn tool_names_sorted() {
264 let mut d = ToolDefaults::new();
265 d.register("zz", json!({}));
266 d.register("aa", json!({}));
267 d.register("mm", json!({}));
268 assert_eq!(d.tool_names(), vec!["aa", "mm", "zz"]);
269 }
270
271 #[test]
272 fn defaults_for_some() {
273 let mut d = ToolDefaults::new();
274 d.register("t", json!({"x": 1}));
275 let m = d.defaults_for("t").unwrap();
276 assert_eq!(m["x"], json!(1));
277 }
278
279 #[test]
280 fn defaults_for_none() {
281 let d = ToolDefaults::new();
282 assert!(d.defaults_for("missing").is_none());
283 }
284
285 #[test]
286 fn contains_check() {
287 let mut d = ToolDefaults::new();
288 assert!(!d.contains("t"));
289 d.register("t", json!({}));
290 assert!(d.contains("t"));
291 d.unregister("t");
292 assert!(!d.contains("t"));
293 }
294
295 #[test]
296 fn register_many() {
297 let mut d = ToolDefaults::new();
298 d.register_many(json!({
299 "a": {"x": 1},
300 "b": {"y": 2}
301 }));
302 assert_eq!(d.apply("a", &json!({}), false).unwrap()["x"], json!(1));
303 assert_eq!(d.apply("b", &json!({}), false).unwrap()["y"], json!(2));
304 }
305
306 #[test]
307 fn apply_does_not_mutate_args() {
308 let mut d = ToolDefaults::new();
309 d.register("t", json!({"a": 1}));
310 let args = json!({"b": 2});
311 let _ = d.apply("t", &args, false).unwrap();
312 assert!(!args.as_object().unwrap().contains_key("a"));
313 }
314
315 #[test]
316 fn nested_value_preserved() {
317 let mut d = ToolDefaults::new();
318 d.register("t", json!({"opts": {"retries": 3}}));
319 let out = d.apply("t", &json!({}), false).unwrap();
320 assert_eq!(out["opts"]["retries"], json!(3));
321 }
322
323 #[test]
324 fn bool_int_string_value_types() {
325 let mut d = ToolDefaults::new();
326 d.register(
327 "t",
328 json!({"flag": true, "count": 42, "label": "hi"}),
329 );
330 let out = d.apply("t", &json!({}), false).unwrap();
331 assert_eq!(out["flag"], json!(true));
332 assert_eq!(out["count"], json!(42));
333 assert_eq!(out["label"], json!("hi"));
334 }
335
336 #[test]
337 fn multiple_tools_independent() {
338 let mut d = ToolDefaults::new();
339 d.register("a", json!({"x": 1}));
340 d.register("b", json!({"y": 2}));
341 let a = d.apply("a", &json!({}), false).unwrap();
342 let b = d.apply("b", &json!({}), false).unwrap();
343 assert!(!a.as_object().unwrap().contains_key("y"));
344 assert!(!b.as_object().unwrap().contains_key("x"));
345 }
346}