kroki_rs/diagrams/providers/
wavedrom.rs

1use crate::diagrams::{DiagramError, DiagramProvider, DiagramResult};
2use async_trait::async_trait;
3use std::io::Write;
4use tempfile::NamedTempFile;
5use tokio::process::Command;
6
7crate::diagrams::define_provider!(WavedromProvider);
8
9#[async_trait]
10impl DiagramProvider for WavedromProvider {
11    fn validate(&self, source: &str) -> DiagramResult<()> {
12        if source.trim().is_empty() {
13            return Err(DiagramError::ValidationFailed(
14                "Diagram source is empty".into(),
15            ));
16        }
17        Ok(())
18    }
19
20    async fn generate(&self, source: &str, format: &str) -> DiagramResult<Vec<u8>> {
21        let mut input_file = NamedTempFile::new().map_err(|e| {
22            DiagramError::Internal(format!("Failed to create temporary input file: {}", e))
23        })?;
24        input_file.write_all(source.as_bytes()).map_err(|e| {
25            DiagramError::Internal(format!("Failed to write source to temp file: {}", e))
26        })?;
27
28        let output_file = NamedTempFile::new().map_err(|e| {
29            DiagramError::Internal(format!("Failed to create temporary output file: {}", e))
30        })?;
31        let output_path = output_file.path().to_path_buf();
32
33        let mut cmd = Command::new(&self.bin_path);
34        cmd.arg("-i").arg(input_file.path());
35        cmd.stdout(std::process::Stdio::piped())
36            .stderr(std::process::Stdio::piped());
37
38        match format {
39            "svg" => {
40                cmd.arg("-s").arg(&output_path);
41            }
42            "png" => {
43                cmd.arg("-p").arg(&output_path);
44            }
45            _ => {
46                return Err(DiagramError::UnsupportedFormat {
47                    format: format.into(),
48                    provider: "Wavedrom".into(),
49                })
50            }
51        }
52
53        let output = crate::diagrams::run_process_with_timeout(
54            "wavedrom-cli",
55            cmd,
56            None,
57            self.timeout_ms,
58            source.len(),
59        )
60        .await?;
61
62        if !output.status.success() {
63            let stderr = String::from_utf8_lossy(&output.stderr);
64            return Err(DiagramError::ProcessFailed(format!(
65                "Wavedrom conversion failed: {}",
66                stderr
67            )));
68        }
69
70        let result = tokio::fs::read(&output_path).await.map_err(|e| {
71            DiagramError::Internal(format!("Failed to read Wavedrom output file: {}", e))
72        })?;
73
74        Ok(result)
75    }
76}