todc_utils/specifications/
snapshot.rs1use core::array::from_fn;
3use std::fmt::Debug;
4use std::hash::Hash;
5use std::marker::PhantomData;
6
7use crate::specifications::Specification;
8
9use SnapshotOperation::{Scan, Update};
10
11pub type ProcessId = usize;
13
14#[derive(Debug, Copy, Clone)]
16pub enum SnapshotOperation<T, const N: usize> {
17 Scan(ProcessId, Option<[T; N]>),
22 Update(ProcessId, T),
24}
25
26pub struct SnapshotSpecification<T: Clone + Debug + Default + Eq + Hash, const N: usize> {
30 data_type: PhantomData<T>,
31}
32
33impl<T: Clone + Debug + Default + Eq + Hash, const N: usize> Specification
34 for SnapshotSpecification<T, N>
35{
36 type State = [T; N];
37 type Operation = SnapshotOperation<T, N>;
38
39 fn init() -> Self::State {
40 from_fn(|_| T::default())
41 }
42
43 fn apply(operation: &Self::Operation, state: &Self::State) -> (bool, Self::State) {
44 match operation {
45 Scan(_, result) => match result {
46 Some(view) => (view == state, state.clone()),
47 None => panic!("Cannot apply Scan with an unknown return value."),
48 },
49 Update(i, value) => {
50 let mut new_state = state.clone();
51 new_state[*i] = value.clone();
52 (true, new_state)
53 }
54 }
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::{SnapshotOperation::*, SnapshotSpecification, Specification};
61
62 type Spec = SnapshotSpecification<u32, 3>;
63
64 mod init {
65 use super::*;
66
67 #[test]
68 fn returns_array_of_defaults() {
69 let state = Spec::init();
70 assert_eq!(state, [u32::default(), u32::default(), u32::default()]);
71 }
72 }
73
74 mod apply {
75 use super::*;
76
77 type Value = u32;
78 const NUM_PROCESSES: usize = 3;
79
80 #[test]
81 fn update_applied_to_proper_component() {
82 let id = 1;
83 let value = 123;
84 let (_, new_state) = Spec::apply(&Update(id, value), &Spec::init());
85 assert_eq!(new_state[id], value);
86 for i in [0, 2] {
87 assert_eq!(new_state[i], Value::default());
88 }
89 }
90
91 #[test]
92 fn update_always_valid() {
93 for i in 0..NUM_PROCESSES {
94 let (valid, _) = Spec::apply(&Update(i, i as u32), &Spec::init());
95 assert!(valid);
96 }
97 }
98
99 #[test]
100 fn scan_doesnt_affect_state() {
101 let (_, new_state) = Spec::apply(&Scan(0, Some([0, 0, 0])), &Spec::init());
102 assert_eq!(Spec::init(), new_state);
103 }
104
105 #[test]
106 fn scan_not_valid_if_differs_from_state() {
107 let mut new_state = Spec::init();
108 new_state[0] = 123;
109 let (valid, _) = Spec::apply(&Scan(0, Some(new_state)), &Spec::init());
110 assert!(!valid);
111 }
112 }
113}