kroki_rs/diagrams/providers/
cmd.rs1use crate::diagrams::{DiagramError, DiagramProvider, DiagramResult};
2use async_trait::async_trait;
3use std::process::Stdio;
4use tokio::process::Command;
5
6crate::diagrams::define_provider!(CommandProvider);
7
8#[async_trait]
9impl DiagramProvider for CommandProvider {
10 fn validate(&self, source: &str) -> DiagramResult<()> {
11 if source.trim().is_empty() {
12 return Err(DiagramError::ValidationFailed(
13 "Diagram source is empty".into(),
14 ));
15 }
16 Ok(())
17 }
18
19 async fn generate(&self, source: &str, _format: &str) -> DiagramResult<Vec<u8>> {
20 let mut cmd = Command::new(&self.bin_path);
21 cmd.arg(format!("-T{}", _format))
22 .stdin(Stdio::piped())
23 .stdout(Stdio::piped())
24 .stderr(Stdio::piped());
25
26 let output = crate::diagrams::run_process_with_timeout(
27 "dot",
28 cmd,
29 Some(source.as_bytes()),
30 self.timeout_ms,
31 source.len(),
32 )
33 .await?;
34
35 if output.status.success() {
36 if output.stdout.is_empty() {
37 return Err(DiagramError::ProcessFailed(
38 "Command succeeded but returned empty output".into(),
39 ));
40 }
41 Ok(output.stdout)
42 } else {
43 let stderr = String::from_utf8_lossy(&output.stderr);
44 Err(DiagramError::ProcessFailed(format!(
45 "Command failed: {}",
46 stderr
47 )))
48 }
49 }
50}