#![allow(missing_docs)]
use serde::{Deserialize, Serialize};
use crate::error::SomaError;
use crate::quad::Quad;
use crate::types::{Layer, UnitId};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RingState {
Uninitialized,
Genesis,
Ring,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UnitState {
pub unit_id: UnitId,
pub soma_quad: Quad,
pub som_quads: [Quad; 4],
}
impl UnitState {
pub fn new(unit_id: UnitId) -> Self {
Self {
unit_id,
soma_quad: Quad::empty(),
som_quads: [Quad::empty(), Quad::empty(), Quad::empty(), Quad::empty()],
}
}
pub fn som_quad(&self, layer: Layer) -> &Quad {
#[allow(clippy::indexing_slicing)] &self.som_quads[layer.index()]
}
pub fn som_quad_mut(&mut self, layer: Layer) -> &mut Quad {
#[allow(clippy::indexing_slicing)] &mut self.som_quads[layer.index()]
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Ring {
units: [UnitState; 6],
pub state: RingState,
pub cycle_index: u64,
}
impl Ring {
pub fn new() -> Self {
Self {
units: [
UnitState::new(UnitId::FU),
UnitState::new(UnitId::MU),
UnitState::new(UnitId::CU),
UnitState::new(UnitId::OU),
UnitState::new(UnitId::SU),
UnitState::new(UnitId::HU),
],
state: RingState::Uninitialized,
cycle_index: 0,
}
}
pub fn unit(&self, id: UnitId) -> &UnitState {
#[allow(clippy::indexing_slicing)] &self.units[id.index()]
}
pub fn unit_mut(&mut self, id: UnitId) -> &mut UnitState {
#[allow(clippy::indexing_slicing)] &mut self.units[id.index()]
}
pub fn all_som_quads(&self) -> Vec<&Quad> {
let mut quads = Vec::with_capacity(24);
for &unit in &UnitId::ALL {
for &layer in &Layer::ALL {
quads.push(self.unit(unit).som_quad(layer));
}
}
quads
}
pub fn all_soma_quads(&self) -> Vec<&Quad> {
UnitId::ALL
.iter()
.map(|&u| &self.unit(u).soma_quad)
.collect()
}
pub fn all_quads(&self) -> Vec<&Quad> {
let mut quads = self.all_som_quads();
quads.extend(self.all_soma_quads());
quads
}
pub fn validate_transition(
&self,
source: UnitId,
destination: UnitId,
) -> Result<(), SomaError> {
let expected = source.successor();
if destination != expected {
return Err(SomaError::InvalidTransition {
from_unit: source,
to_unit: destination,
expected,
});
}
if self.state == RingState::Uninitialized {
return Err(SomaError::RingNotInitialized);
}
Ok(())
}
pub fn is_populated(&self) -> bool {
self.units
.iter()
.all(|u| !u.soma_quad.is_empty() || u.som_quads.iter().any(|q| !q.is_empty()))
}
}
impl Default for Ring {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_ring_is_uninitialized() {
let ring = Ring::new();
assert_eq!(ring.state, RingState::Uninitialized);
assert_eq!(ring.cycle_index, 0);
}
#[test]
fn ring_has_six_units() {
let ring = Ring::new();
for &unit in &UnitId::ALL {
assert_eq!(ring.unit(unit).unit_id, unit);
}
}
#[test]
fn all_som_quads_returns_24() {
let ring = Ring::new();
assert_eq!(ring.all_som_quads().len(), 24);
}
#[test]
fn all_soma_quads_returns_6() {
let ring = Ring::new();
assert_eq!(ring.all_soma_quads().len(), 6);
}
#[test]
fn all_quads_returns_30() {
let ring = Ring::new();
assert_eq!(ring.all_quads().len(), 30);
}
#[test]
fn transition_validation_rejects_bypass() {
let mut ring = Ring::new();
ring.state = RingState::Ring;
assert!(ring.validate_transition(UnitId::FU, UnitId::MU).is_ok());
assert!(ring.validate_transition(UnitId::FU, UnitId::CU).is_err());
assert!(ring.validate_transition(UnitId::FU, UnitId::FU).is_err());
}
#[test]
fn uninitialized_ring_rejects_transitions() {
let ring = Ring::new();
assert!(ring.validate_transition(UnitId::FU, UnitId::MU).is_err());
}
}