wfdb 0.1.6

A library for decoding and encoding Waveform Database format files.
Documentation
use std::env;
use wfdb::Record;

fn main() -> wfdb::Result<()> {
    let args: Vec<String> = env::args().collect();

    if args.len() < 2 {
        eprintln!("Usage: {} <record_path>", args[0]);
        eprintln!("\nExample:");
        eprintln!("  {} ./references/wfdb-master/data/100s", args[0]);
        std::process::exit(1);
    }

    let record_path = &args[1];
    let record = Record::open(record_path)?;

    println!("=== Record Information ===");
    println!("Record: {}", record.metadata().name());
    println!(
        "Sampling frequency: {} Hz",
        record.metadata().sampling_frequency()
    );

    if let Some(num_samples) = record.metadata().num_samples {
        println!("Total samples: {num_samples}");
    }

    if record.is_multi_segment() {
        demonstrate_multi_segment_seeking(&record)?;
    } else {
        demonstrate_single_segment_seeking(&record)?;
    }

    Ok(())
}

#[allow(clippy::unwrap_used)]
fn demonstrate_single_segment_seeking(record: &Record) -> wfdb::Result<()> {
    println!("\n=== Single-Segment Record ===");
    println!("Signals: {}", record.signal_count());

    if record.signal_count() == 0 {
        println!("No signals to read");
        return Ok(());
    }

    let signal_idx = 0;
    let signal_info = &record.signal_info().unwrap()[signal_idx];
    println!(
        "\nDemonstrating with Signal {}: {}",
        signal_idx,
        signal_info
            .description
            .as_deref()
            .unwrap_or("(no description)")
    );

    println!("\n--- SignalReader: Sample-based Seeking ---");
    let mut reader = record.signal_reader(signal_idx)?;

    println!("Reading first 5 samples from start...");
    let samples = reader.read_samples(5)?;
    println!("  Samples 0-4: {samples:?}");
    println!("  Current position: {}", reader.position());

    println!("\nSeeking to sample 100...");
    match reader.seek_to_sample(100) {
        Ok(pos) => {
            println!("  Seek to position: {pos}");
            let samples = reader.read_samples(5)?;
            println!("  Samples 100-104: {samples:?}");
            println!("  Current position: {}", reader.position());

            println!("\nSeeking backward to sample 50...");
            reader.seek_to_sample(50)?;
            let samples = reader.read_samples(3)?;
            println!("  Samples 50-52: {samples:?}");
            println!("  Current position: {}", reader.position());
        }
        Err(e) => {
            println!("  Seeking not supported for this format: {e}");
        }
    }

    println!("\n--- SignalReader: Time-based Seeking ---");
    let mut reader = record.signal_reader(signal_idx)?;

    println!("Seeking to 1.0 second...");
    match reader.seek_to_time(1.0) {
        Ok(pos) => {
            println!("  Seek to sample position: {pos}");
            println!(
                "  (1.0 sec × {} Hz = {} samples)",
                record.metadata().sampling_frequency(),
                pos
            );
            let samples = reader.read_samples(3)?;
            println!("  Samples at 1.0s: {samples:?}");
        }
        Err(e) => {
            println!("  Time-based seeking failed: {e}");
        }
    }

    println!("\n--- MultiSignalReader: Frame-based Seeking ---");
    let mut multi_reader = record.multi_signal_reader()?;

    println!("Reading first 3 frames...");
    let frames = multi_reader.read_frames(3)?;
    for (i, frame) in frames.iter().enumerate() {
        println!("  Frame {i}: {frame:?}");
    }
    println!("  Current position: frame {}", multi_reader.position());

    println!("\nSeeking to frame 50...");
    match multi_reader.seek_to_frame(50) {
        Ok(pos) => {
            println!("  Seek to frame: {pos}");
            let frames = multi_reader.read_frames(2)?;
            for (i, frame) in frames.iter().enumerate() {
                println!("  Frame {}: {:?}", 50 + i, frame);
            }
            println!("  Current position: frame {}", multi_reader.position());
        }
        Err(e) => {
            println!("  Frame seeking not supported: {e}");
        }
    }

    println!("\n--- Physical Units Conversion ---");
    let mut reader = record.signal_reader(signal_idx)?;

    println!(
        "Signal gain: {} ADC units per {}",
        reader.gain(),
        reader.units()
    );
    println!("Signal baseline: {} ADC units", reader.baseline());

    let adc_values = reader.read_samples(5)?;
    println!("\nFirst 5 ADC values: {adc_values:?}");

    reader.seek_to_sample(0)?;
    let physical_values = reader.read_physical(5)?;
    println!("First 5 physical values: {physical_values:?}");

    println!("\nConversion example:");
    for i in 0..adc_values.len().min(3) {
        let adc = adc_values[i];
        let physical = physical_values[i];
        println!("  ADC {}{} {}", adc, physical, reader.units());
    }

    Ok(())
}

fn demonstrate_multi_segment_seeking(record: &Record) -> wfdb::Result<()> {
    println!("\n=== Multi-Segment Record ===");
    println!("Segments: {}", record.segment_count());

    if let Some(segments) = record.segment_info() {
        println!("\nSegment details:");
        for (i, seg) in segments.iter().enumerate() {
            println!(
                "  Segment {}: {} ({} samples)",
                i, seg.record_name, seg.num_samples
            );
        }
    }

    println!("\n--- SegmentReader: Cross-segment Seeking ---");
    let mut reader = record.segment_reader()?;

    println!("Reading first 5 frames...");
    let frames = reader.read_frames(5)?;
    for (i, frame) in frames.iter().enumerate() {
        println!("  Frame {i}: {frame:?}");
    }
    println!(
        "  Current position: sample {} (segment {})",
        reader.position(),
        reader.current_segment()
    );

    println!("\nSeeking to sample 100...");
    match reader.seek_to_sample(100) {
        Ok(pos) => {
            println!("  Seek to sample: {pos}");
            println!("  Now at segment: {}", reader.current_segment());

            let frames = reader.read_frames(3)?;
            for (i, frame) in frames.iter().enumerate() {
                println!("  Frame {}: {:?}", 100 + i, frame);
            }
            println!("  Current position: sample {}", reader.position());
        }
        Err(e) => {
            println!("  Seeking failed: {e}");
        }
    }

    println!("\nReading sequentially across segments...");
    reader.seek_to_sample(0)?;
    let mut frame_count = 0;
    while let Some(_frame) = reader.read_frame()? {
        frame_count += 1;
        if frame_count >= 10 {
            println!("  Read {frame_count} frames across segments...");
            break;
        }
    }
    println!(
        "  Final position: sample {} (segment {})",
        reader.position(),
        reader.current_segment()
    );

    Ok(())
}