use std::any::{Any, TypeId, type_name};
use std::fmt;
use serde::Serialize;
use super::error::{PayloadInsertError, StateError};
use super::schema::{StateFieldSchema, SystemStateSchema};
use super::value::StateValue;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SimulationTime {
iteration: u64,
physical_time: Option<f64>,
}
impl SimulationTime {
pub const fn from_iteration(iteration: u64) -> Self {
Self {
iteration,
physical_time: None,
}
}
pub fn from_iteration_and_physical_time(iteration: u64, physical_time: f64) -> Option<Self> {
physical_time.is_finite().then_some(Self {
iteration,
physical_time: Some(physical_time),
})
}
pub const fn iteration(self) -> u64 {
self.iteration
}
pub const fn physical_time(self) -> Option<f64> {
self.physical_time
}
}
pub struct SystemState {
spec: SystemStateSchema,
time: SimulationTime,
slots: Vec<StateSlot>,
}
#[derive(Clone)]
struct StateSlot {
definition: Option<ValueType>,
value: Option<StateValue>,
}
impl StateSlot {
const fn unbound() -> Self {
Self {
definition: None,
value: None,
}
}
const fn empty_like(&self) -> Self {
Self {
definition: self.definition,
value: None,
}
}
}
#[derive(Clone, Copy)]
struct ValueType {
id: TypeId,
name: &'static str,
}
impl ValueType {
fn of<T>() -> Self
where
T: Any,
{
Self {
id: TypeId::of::<T>(),
name: type_name::<T>(),
}
}
fn is<T>(self) -> bool
where
T: Any,
{
self.id == TypeId::of::<T>()
}
}
impl SystemState {
pub(crate) fn new(spec: SystemStateSchema, time: SimulationTime) -> Self {
let slots = (0..spec.len()).map(|_| StateSlot::unbound()).collect();
Self { spec, time, slots }
}
pub fn clone_structure_without_payloads(&self, time: SimulationTime) -> Self {
Self {
spec: self.spec.clone(),
time,
slots: self.slots.iter().map(StateSlot::empty_like).collect(),
}
}
pub const fn simulation_time(&self) -> SimulationTime {
self.time
}
pub fn replace_simulation_time(&mut self, time: SimulationTime) -> SimulationTime {
std::mem::replace(&mut self.time, time)
}
pub fn advance_simulation_time(
&mut self,
physical_time_increment: Option<f64>,
) -> Result<SimulationTime, StateError> {
let next_iteration =
self.time
.iteration
.checked_add(1)
.ok_or(StateError::IterationOverflow {
iteration: self.time.iteration,
})?;
let next_physical_time = match (self.time.physical_time, physical_time_increment) {
(physical_time, None) => physical_time,
(None, Some(_)) => {
return Err(StateError::MissingPhysicalTime {
iteration: self.time.iteration,
});
}
(Some(current), Some(delta)) => {
let next = current + delta;
if !delta.is_finite() || !next.is_finite() {
return Err(StateError::InvalidPhysicalAdvance { current, delta });
}
Some(next)
}
};
let next = SimulationTime {
iteration: next_iteration,
physical_time: next_physical_time,
};
self.time = next;
Ok(next)
}
pub const fn schema(&self) -> &SystemStateSchema {
&self.spec
}
pub fn declared_field_count(&self) -> usize {
self.slots.len()
}
pub fn has_no_declared_fields(&self) -> bool {
self.slots.is_empty()
}
pub fn populated_field_count(&self) -> usize {
self.slots
.iter()
.filter(|slot| slot.value.is_some())
.count()
}
pub fn has_no_payloads(&self) -> bool {
self.slots.iter().all(|slot| slot.value.is_none())
}
pub fn field_schemas(&self) -> &[StateFieldSchema] {
self.spec.field_schemas()
}
pub fn contains_payload(&self, key: &str) -> Result<bool, StateError> {
let index = self.spec.index_of(key)?;
Ok(self.slots[index].value.is_some())
}
pub fn payload_has_type<T>(&self, key: &str) -> Result<bool, StateError>
where
T: Any,
{
let index = self.spec.index_of(key)?;
Ok(self.slots[index]
.value
.as_ref()
.is_some_and(StateValue::is::<T>))
}
pub fn insert_payload<T>(
&mut self,
key: &str,
payload: T,
) -> Result<Option<T>, PayloadInsertError<T>>
where
T: Serialize + Clone + Send + 'static,
{
let index = match self.spec.index_of(key) {
Ok(index) => index,
Err(error) => return Err(PayloadInsertError::new(error, payload)),
};
let slot = &mut self.slots[index];
match slot.definition {
Some(definition) if !definition.is::<T>() => {
return Err(PayloadInsertError::new(
StateError::TypeMismatch {
field: key.to_owned(),
expected: type_name::<T>(),
actual: definition.name,
},
payload,
));
}
Some(_) => {}
None => slot.definition = Some(ValueType::of::<T>()),
}
let previous = slot.value.replace(StateValue::new(payload));
match previous {
None => Ok(None),
Some(previous) => match previous.downcast::<T>() {
Ok(previous) => Ok(Some(previous)),
Err(_) => unreachable!("a type-bound StateValue failed its consuming downcast"),
},
}
}
pub fn payload<T>(&self, key: &str) -> Result<&T, StateError>
where
T: Any,
{
let index = self.spec.index_of(key)?;
self.validate_slot::<T>(index, key)?;
Ok(self.slots[index]
.value
.as_ref()
.and_then(StateValue::downcast_ref::<T>)
.expect("a validated state slot must contain its bound concrete type"))
}
pub fn payload_mut<T>(&mut self, key: &str) -> Result<&mut T, StateError>
where
T: Any,
{
let index = self.spec.index_of(key)?;
self.validate_slot::<T>(index, key)?;
Ok(self.slots[index]
.value
.as_mut()
.and_then(StateValue::downcast_mut::<T>)
.expect("a validated state slot must contain its bound concrete type"))
}
pub fn borrow_payloads<'state, Q>(
&'state self,
keys: Q::Keys<'_>,
) -> Result<Q::Refs<'state>, StateError>
where
Q: PayloadTuple,
{
Q::borrow(self, keys)
}
pub fn borrow_payloads_mut<'state, Q>(
&'state mut self,
keys: Q::Keys<'_>,
) -> Result<Q::RefsMut<'state>, StateError>
where
Q: PayloadTuple,
{
Q::borrow_mut(self, keys)
}
pub fn take_payload<T>(&mut self, key: &str) -> Result<T, StateError>
where
T: Any + Send,
{
let index = self.spec.index_of(key)?;
self.validate_slot::<T>(index, key)?;
let value = self.slots[index]
.value
.take()
.expect("a validated state slot must contain a payload");
match value.downcast::<T>() {
Ok(payload) => Ok(payload),
Err(_) => unreachable!("a type-bound StateValue failed its consuming downcast"),
}
}
pub fn clear_payload(&mut self, key: &str) -> Result<bool, StateError> {
let index = self.spec.index_of(key)?;
Ok(self.slots[index].value.take().is_some())
}
pub fn clear_all_payloads(&mut self) {
self.slots.iter_mut().for_each(|slot| slot.value = None);
}
fn value(&self, key: &str) -> Result<&StateValue, StateError> {
let index = self.spec.index_of(key)?;
self.slots[index]
.value
.as_ref()
.ok_or_else(|| StateError::MissingPayload {
field: key.to_owned(),
})
}
fn validate_slot<T>(&self, index: usize, key: &str) -> Result<(), StateError>
where
T: Any,
{
let slot = &self.slots[index];
if let Some(definition) = slot.definition
&& !definition.is::<T>()
{
return Err(StateError::TypeMismatch {
field: key.to_owned(),
expected: type_name::<T>(),
actual: definition.name,
});
}
if slot.value.is_none() {
return Err(StateError::MissingPayload {
field: key.to_owned(),
});
}
Ok(())
}
fn resolve_distinct<const N: usize>(&self, keys: [&str; N]) -> Result<[usize; N], StateError> {
let mut indices = [0; N];
for (position, key) in keys.iter().enumerate() {
let index = self.spec.index_of(key)?;
if indices[..position].contains(&index) {
return Err(StateError::RepeatedPayloadBorrow {
field: (*key).to_owned(),
});
}
indices[position] = index;
}
Ok(indices)
}
fn disjoint_slots_mut<const N: usize>(&mut self, indices: [usize; N]) -> [&mut StateSlot; N] {
let mut positions: [(usize, usize); N] =
std::array::from_fn(|position| (position, indices[position]));
positions.sort_unstable_by_key(|(_, index)| *index);
let mut remaining = self.slots.as_mut_slice();
let mut base = 0;
let mut selected: [Option<&mut StateSlot>; N] = std::array::from_fn(|_| None);
for (original_position, index) in positions {
let relative = index - base;
let (_, at_index) = remaining.split_at_mut(relative);
let (slot, tail) = at_index
.split_first_mut()
.expect("resolved state slot index must be in bounds");
selected[original_position] = Some(slot);
remaining = tail;
base = index + 1;
}
selected
.map(|slot| slot.expect("one disjoint slot must be returned for every requested index"))
}
#[allow(
dead_code,
reason = "reserved for storage::JsonStateRecordEncoder, which is implemented in the next module stage"
)]
pub(crate) fn serializable(
&self,
key: &str,
) -> Result<&dyn erased_serde::Serialize, StateError> {
Ok(self.value(key)?.serializable())
}
}
mod tuple_sealed {
pub trait Sealed {}
}
#[doc(hidden)]
pub trait PayloadTuple: tuple_sealed::Sealed {
type Keys<'key>;
type Refs<'state>
where
Self: 'state;
type RefsMut<'state>
where
Self: 'state;
#[doc(hidden)]
fn borrow<'state, 'key>(
state: &'state SystemState,
keys: Self::Keys<'key>,
) -> Result<Self::Refs<'state>, StateError>;
#[doc(hidden)]
fn borrow_mut<'state, 'key>(
state: &'state mut SystemState,
keys: Self::Keys<'key>,
) -> Result<Self::RefsMut<'state>, StateError>;
}
macro_rules! substitute_type {
($_generic:ident => $replacement:ty) => {
$replacement
};
}
macro_rules! impl_state_tuple {
($(($type:ident, $key:ident, $slot:ident, $index:tt)),+ $(,)?) => {
impl<$($type),+> tuple_sealed::Sealed for ($($type,)+)
where
$($type: Any,)+
{
}
impl<$($type),+> PayloadTuple for ($($type,)+)
where
$($type: Any,)+
{
type Keys<'key> = ($(substitute_type!($type => &'key str),)+);
type Refs<'state> = ($(&'state $type,)+) where Self: 'state;
type RefsMut<'state> = ($(&'state mut $type,)+) where Self: 'state;
fn borrow<'state, 'key>(
state: &'state SystemState,
keys: Self::Keys<'key>,
) -> Result<Self::Refs<'state>, StateError> {
let ($($key,)+) = keys;
let indices = state.resolve_distinct([$($key,)+])?;
$(state.validate_slot::<$type>(indices[$index], $key)?;)+
Ok(($(
state.slots[indices[$index]]
.value
.as_ref()
.and_then(StateValue::downcast_ref::<$type>)
.expect("a preflighted state slot must contain its bound concrete type"),
)+))
}
fn borrow_mut<'state, 'key>(
state: &'state mut SystemState,
keys: Self::Keys<'key>,
) -> Result<Self::RefsMut<'state>, StateError> {
let ($($key,)+) = keys;
let indices = state.resolve_distinct([$($key,)+])?;
$(state.validate_slot::<$type>(indices[$index], $key)?;)+
let [$($slot,)+] = state.disjoint_slots_mut(indices);
Ok(($(
$slot
.value
.as_mut()
.and_then(StateValue::downcast_mut::<$type>)
.expect("a preflighted state slot must contain its bound concrete type"),
)+))
}
}
};
}
impl_state_tuple!((A, key_a, slot_a, 0), (B, key_b, slot_b, 1));
impl_state_tuple!(
(A, key_a, slot_a, 0),
(B, key_b, slot_b, 1),
(C, key_c, slot_c, 2),
);
impl_state_tuple!(
(A, key_a, slot_a, 0),
(B, key_b, slot_b, 1),
(C, key_c, slot_c, 2),
(D, key_d, slot_d, 3),
);
impl_state_tuple!(
(A, key_a, slot_a, 0),
(B, key_b, slot_b, 1),
(C, key_c, slot_c, 2),
(D, key_d, slot_d, 3),
(E, key_e, slot_e, 4),
);
impl_state_tuple!(
(A, key_a, slot_a, 0),
(B, key_b, slot_b, 1),
(C, key_c, slot_c, 2),
(D, key_d, slot_d, 3),
(E, key_e, slot_e, 4),
(F, key_f, slot_f, 5),
);
impl_state_tuple!(
(A, key_a, slot_a, 0),
(B, key_b, slot_b, 1),
(C, key_c, slot_c, 2),
(D, key_d, slot_d, 3),
(E, key_e, slot_e, 4),
(F, key_f, slot_f, 5),
(G, key_g, slot_g, 6),
);
impl_state_tuple!(
(A, key_a, slot_a, 0),
(B, key_b, slot_b, 1),
(C, key_c, slot_c, 2),
(D, key_d, slot_d, 3),
(E, key_e, slot_e, 4),
(F, key_f, slot_f, 5),
(G, key_g, slot_g, 6),
(H, key_h, slot_h, 7),
);
impl Clone for SystemState {
fn clone(&self) -> Self {
Self {
spec: self.spec.clone(),
time: self.time,
slots: self.slots.clone(),
}
}
}
impl fmt::Debug for SystemState {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("SystemState")
.field("time", &self.time)
.field("source", &self.spec.template_path())
.field("fields", &self.declared_field_count())
.field("loaded", &self.populated_field_count())
.finish_non_exhaustive()
}
}