1use anyhow::Result;
2use serde_json::Value;
3
4use crate::{ExecConfig, ExecError, ExecRequest, exec};
5
6#[derive(Debug)]
7pub enum Maybe<T> {
8 Data(T),
9 Unsupported,
10}
11
12#[derive(Debug)]
13pub struct ToolDescribe {
14 pub capabilities: Maybe<Vec<String>>,
15 pub secrets: Maybe<Value>,
16 pub config_schema: Maybe<Value>,
17}
18
19pub fn describe_tool(name: &str, cfg: &ExecConfig) -> Result<ToolDescribe> {
20 fn try_action(name: &str, action: &str, cfg: &ExecConfig) -> Result<Maybe<Value>> {
21 let req = ExecRequest {
22 component: name.to_string(),
23 action: action.to_string(),
24 args: Value::Object(Default::default()),
25 tenant: None,
26 };
27
28 match exec(req, cfg) {
29 Ok(v) => Ok(Maybe::Data(v)),
30 Err(ExecError::NotFound { .. }) => Ok(Maybe::Unsupported),
31 Err(ExecError::Tool { code, payload, .. }) if code == "iface-error.not-found" => {
32 let _ = payload;
33 Ok(Maybe::Unsupported)
34 }
35 Err(e) => Err(e.into()),
36 }
37 }
38
39 let capabilities_value = try_action(name, "capabilities", cfg)?;
40 let secrets = try_action(name, "list_secrets", cfg)?;
41 let config_schema = try_action(name, "config_schema", cfg)?;
42
43 let capabilities = match capabilities_value {
44 Maybe::Data(value) => {
45 if let Some(arr) = value.as_array() {
46 let list = arr
47 .iter()
48 .filter_map(|v| v.as_str().map(|s| s.to_string()))
49 .collect::<Vec<_>>();
50 Maybe::Data(list)
51 } else {
52 Maybe::Data(Vec::new())
53 }
54 }
55 Maybe::Unsupported => Maybe::Unsupported,
56 };
57
58 Ok(ToolDescribe {
59 capabilities,
60 secrets,
61 config_schema,
62 })
63}