snap7-server 0.1.0

Async Rust S7 PLC simulator/server for testing and development
Documentation
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

#[derive(Clone, Default)]
pub struct DataStore {
    inner: Arc<Mutex<HashMap<(u16, u32), u8>>>,
}

impl DataStore {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn read_bytes(&self, db: u16, start: u32, count: u32) -> Vec<u8> {
        let inner = self.inner.lock().unwrap();
        let end = start.saturating_add(count);
        (start..end)
            .map(|offset| *inner.get(&(db, offset)).unwrap_or(&0))
            .collect()
    }

    pub fn write_bytes(&self, db: u16, start: u32, data: &[u8]) {
        let mut inner = self.inner.lock().unwrap();
        for (i, &byte) in data.iter().enumerate() {
            if let Some(offset) = start.checked_add(i as u32) {
                inner.insert((db, offset), byte);
            }
        }
    }
}

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

    #[test]
    fn read_unset_returns_zeros() {
        let store = DataStore::new();
        let data = store.read_bytes(1, 0, 4);
        assert_eq!(data, vec![0, 0, 0, 0]);
    }

    #[test]
    fn write_then_read_roundtrip() {
        let store = DataStore::new();
        store.write_bytes(1, 0, &[0xDE, 0xAD, 0xBE, 0xEF]);
        let data = store.read_bytes(1, 0, 4);
        assert_eq!(data, vec![0xDE, 0xAD, 0xBE, 0xEF]);
    }

    #[test]
    fn partial_read_within_written_range() {
        let store = DataStore::new();
        store.write_bytes(1, 0, &[0x01, 0x02, 0x03, 0x04]);
        let data = store.read_bytes(1, 1, 2);
        assert_eq!(data, vec![0x02, 0x03]);
    }

    #[test]
    fn write_to_different_dbs_isolated() {
        let store = DataStore::new();
        store.write_bytes(1, 0, &[0xAA]);
        store.write_bytes(2, 0, &[0xBB]);
        assert_eq!(store.read_bytes(1, 0, 1), vec![0xAA]);
        assert_eq!(store.read_bytes(2, 0, 1), vec![0xBB]);
    }
}