dsfb-semiconductor 0.1.1

Deterministic DSFB semiconductor benchmark companion for SECOM and PHM-style dataset adapters
Documentation
#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AlarmSample {
    pub timestamp: f64,
    pub source: String,
    pub active: bool,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct AlarmStream {
    samples: Vec<AlarmSample>,
}

impl AlarmStream {
    pub fn new(mut samples: Vec<AlarmSample>) -> Self {
        samples.sort_by(|left, right| {
            left.timestamp
                .total_cmp(&right.timestamp)
                .then_with(|| left.source.cmp(&right.source))
        });
        Self { samples }
    }

    pub fn from_samples(samples: &[AlarmSample]) -> Self {
        Self::new(samples.to_vec())
    }

    pub fn push_clone(&mut self, sample: &AlarmSample) {
        self.samples.push(sample.clone());
        self.samples.sort_by(|left, right| {
            left.timestamp
                .total_cmp(&right.timestamp)
                .then_with(|| left.source.cmp(&right.source))
        });
    }

    pub fn samples(&self) -> &[AlarmSample] {
        &self.samples
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn alarm_stream_is_sorted_deterministically() {
        let stream = AlarmStream::new(vec![
            AlarmSample {
                timestamp: 5.0,
                source: "ewma".into(),
                active: true,
            },
            AlarmSample {
                timestamp: 1.0,
                source: "threshold".into(),
                active: false,
            },
        ]);
        assert_eq!(stream.samples()[0].timestamp, 1.0);
        assert_eq!(stream.samples()[1].timestamp, 5.0);
    }
}