1use std::path::{Path, PathBuf};
4use std::process::Command;
5
6pub fn resolve_vcd2fst() -> Result<PathBuf, FstError> {
8 resolve_vcd2fst_from(std::env::var_os("RHDL_VCD2FST"), std::env::var_os("PATH"))
9}
10
11pub(crate) fn resolve_vcd2fst_from(
12 override_bin: Option<std::ffi::OsString>,
13 path: Option<std::ffi::OsString>,
14) -> Result<PathBuf, FstError> {
15 if let Some(p) = override_bin {
16 let pb = PathBuf::from(&p);
17 if pb.is_file() {
18 return Ok(pb);
19 }
20 return Err(FstError::Message(format!(
21 "RHDL_VCD2FST={p:?} is not a file; install gtkwave's vcd2fst or point this env at it"
22 )));
23 }
24 which_in("vcd2fst", path).ok_or_else(|| {
25 FstError::Message(
26 "FST requested but vcd2fst not found; install gtkwave (vcd2fst) or set RHDL_VCD2FST \
27 (AD-24; Verilator --trace-fst is for Verilated C++ models, not native tick)"
28 .into(),
29 )
30 })
31}
32
33fn which_in(name: &str, path: Option<std::ffi::OsString>) -> Option<PathBuf> {
34 let path = path?;
35 for dir in std::env::split_paths(&path) {
36 let cand = dir.join(name);
37 if cand.is_file() {
38 return Some(cand);
39 }
40 }
41 None
42}
43
44#[derive(Debug)]
45pub enum FstError {
46 Message(String),
47 Io(std::io::Error),
48}
49
50impl std::fmt::Display for FstError {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 match self {
53 FstError::Message(m) => write!(f, "{m}"),
54 FstError::Io(e) => write!(f, "{e}"),
55 }
56 }
57}
58
59impl std::error::Error for FstError {}
60
61impl From<std::io::Error> for FstError {
62 fn from(e: std::io::Error) -> Self {
63 FstError::Io(e)
64 }
65}
66
67pub(crate) fn convert_vcd_to_fst(converter: &Path, vcd: &Path, fst: &Path) -> Result<(), FstError> {
68 let status = Command::new(converter)
69 .arg(vcd)
70 .arg(fst)
71 .status()
72 .map_err(|e| FstError::Message(format!("spawn vcd2fst: {e}")))?;
73 if !status.success() {
74 return Err(FstError::Message(format!(
75 "vcd2fst failed converting {vcd:?} -> {fst:?}"
76 )));
77 }
78 if !fst.is_file() {
79 return Err(FstError::Message(format!("vcd2fst did not write {fst:?}")));
80 }
81 Ok(())
82}