kroki_rs/
capabilities.rs

1use crate::config::Config;
2use std::path::PathBuf;
3use which::which;
4
5/// Holds the paths to discovered diagram generation tools.
6#[derive(Debug, Clone)]
7pub struct Capabilities {
8    pub graphviz: Option<PathBuf>,
9    pub mermaid: Option<PathBuf>,
10    pub vega: Option<PathBuf>,     // vg2svg
11    pub vegalite: Option<PathBuf>, // vl2vg
12    pub wavedrom: Option<PathBuf>,
13    pub bpmn: Option<PathBuf>,
14    pub d2: Option<PathBuf>,
15    pub ditaa: Option<PathBuf>,
16    pub excalidraw: Option<PathBuf>,
17    // Add other tools
18}
19
20impl Capabilities {
21    /// Discovers available tools based on configuration and system PATH.
22    pub fn discover(config: &Config) -> Self {
23        #[cfg(feature = "native-browser")]
24        let (mermaid, bpmn) = (Some(PathBuf::from("native")), Some(PathBuf::from("native")));
25
26        #[cfg(not(feature = "native-browser"))]
27        let (mermaid, bpmn) = (
28            Self::find_tool(&None, "node"),
29            Self::find_tool(&None, "node"),
30        );
31
32        let caps = Self {
33            graphviz: Self::find_tool(&config.graphviz.bin_path, "dot"),
34            mermaid,
35            vega: Self::find_tool(&config.vega.bin_path, "vg2svg"),
36            vegalite: Self::find_tool(&config.vegalite.bin_path, "vl2vg"),
37            wavedrom: Self::find_tool(&config.wavedrom.bin_path, "wavedrom-cli"),
38            bpmn,
39            d2: Self::find_tool(&config.d2.bin_path, "d2"),
40            ditaa: Self::find_tool(&config.ditaa.bin_path, "ditaa"),
41            excalidraw: Self::find_tool(&config.excalidraw.bin_path, "excalidraw-to-svg"),
42        };
43        // Log summary of discovered capabilities
44        let found: Vec<&str> = [
45            caps.graphviz.as_ref().map(|_| "graphviz"),
46            caps.mermaid.as_ref().map(|_| "mermaid"),
47            caps.vega.as_ref().map(|_| "vega"),
48            caps.vegalite.as_ref().map(|_| "vegalite"),
49            caps.wavedrom.as_ref().map(|_| "wavedrom"),
50            caps.bpmn.as_ref().map(|_| "bpmn"),
51            caps.d2.as_ref().map(|_| "d2"),
52            caps.ditaa.as_ref().map(|_| "ditaa"),
53            caps.excalidraw.as_ref().map(|_| "excalidraw"),
54        ]
55        .into_iter()
56        .flatten()
57        .collect();
58        tracing::info!("Discovered {} tools: [{}]", found.len(), found.join(", "));
59        caps
60    }
61
62    /// Finds a tool by checking the configured path, local node_modules, and system PATH.
63    fn find_tool(configured_path: &Option<String>, default_name: &str) -> Option<PathBuf> {
64        if let Some(path_str) = configured_path {
65            let path = PathBuf::from(path_str);
66            if path.exists() {
67                return Some(path);
68            } else if let Ok(p) = which(path_str) {
69                return Some(p);
70            }
71        }
72
73        let local_node_bin = PathBuf::from("node_modules/.bin").join(default_name);
74        if local_node_bin.exists() {
75            return Some(local_node_bin);
76        }
77
78        which(default_name).ok()
79    }
80}