1use crate::config::Config;
2use std::path::PathBuf;
3use which::which;
4
5#[derive(Debug, Clone)]
7pub struct Capabilities {
8 pub graphviz: Option<PathBuf>,
9 pub mermaid: Option<PathBuf>,
10 pub vega: Option<PathBuf>, pub vegalite: Option<PathBuf>, 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 }
19
20impl Capabilities {
21 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 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 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}