tekhsi_rs 0.1.1

High-performance client for Tektronix TekHSI enabled oscilloscopes
Documentation
use std::error::Error;
use std::fs;
use std::path::{Path, PathBuf};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("cargo:rerun-if-changed=build.rs");

    // Build protos
    println!("cargo:rerun-if-changed=proto/TekHighspeedServer.proto");
    tonic_prost_build::configure()
        .build_server(false)
        .type_attribute(".", "#[allow(clippy::all)]")
        .compile_protos(&["proto/TekHighspeedServer.proto"], &["proto"])?;

    generate_waveform_tests()?;

    Ok(())
}

fn generate_waveform_tests() -> Result<(), Box<dyn Error>> {
    // Waveform test generation
    let data_dir = Path::new("tests").join("data");
    println!("cargo:rerun-if-changed={}", data_dir.display());

    let mut symbols = Vec::new();
    if let Ok(entries) = fs::read_dir(&data_dir) {
        for entry in entries.flatten() {
            let path = entry.path();
            let ext = path.extension().and_then(|ext| ext.to_str());
            if ext != Some("wfm") {
                continue;
            }
            if let Some(stem) = path.file_stem().and_then(|name| name.to_str()) {
                let csv_path = data_dir.join(format!("{stem}.csv"));
                let mat_path = data_dir.join(format!("{stem}.mat"));
                let is_iq = stem.ends_with("_iq");
                let has_csv = csv_path.exists();
                let has_mat = mat_path.exists();
                if (is_iq && has_mat) || (!is_iq && has_csv) {
                    symbols.push(stem.to_string());
                }
            }
            println!("cargo:rerun-if-changed={}", path.display());
        }
    }
    symbols.sort();

    let out_dir = std::env::var("OUT_DIR")?;
    let output_path = PathBuf::from(out_dir).join("generated_waveform_tests.rs");
    let mut output = String::new();
    output.push_str("// @generated by build.rs\n");
    output.push_str("// This file is overwritten during build.\n\n");

    for symbol in symbols {
        let ident = sanitize_symbol(&symbol);
        output.push_str("#[tokio::test]\n");
        output.push_str(&format!(
            "async fn validate_{ident}() -> Result<(), Box<dyn std::error::Error>> {{\n"
        ));
        output.push_str(&format!(
            "    validate_symbol_waveform(\"{symbol}\").await\n"
        ));
        output.push_str("}\n\n");
    }

    fs::write(&output_path, output)?;
    Ok(())
}

fn sanitize_symbol(symbol: &str) -> String {
    let mut out = String::from("symbol_");
    for ch in symbol.chars() {
        if ch.is_ascii_alphanumeric() {
            out.push(ch.to_ascii_lowercase());
        } else {
            out.push('_');
        }
    }
    out
}