use std::{fmt, rc::Rc};
use super::{
ObjectId, RecordChain, SnapshotMutableState, StateObject, StateRecord, active_snapshot,
};
use crate::{
snapshot_id_set::{SnapshotId, SnapshotIdSet},
snapshot_v2::GlobalSnapshot,
};
#[derive(Debug)]
pub(super) struct StateReadFailure {
state_id: ObjectId,
snapshot_id: SnapshotId,
fresh_snapshot_id: SnapshotId,
fresh_invalid: SnapshotIdSet,
record_chain: Vec<(SnapshotId, bool)>,
}
impl fmt::Display for StateReadFailure {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Reading a state that was created after the snapshot was taken or in a snapshot that has not yet been applied\n\
state={:?}, snapshot_id={}, fresh_snapshot_id={}, fresh_invalid={:?}\n\
record_chain={:?}",
self.state_id,
self.snapshot_id,
self.fresh_snapshot_id,
self.fresh_invalid,
self.record_chain
)
}
}
#[derive(Clone, Copy)]
pub(super) enum ReadNotice<'a> {
Observers(&'a dyn StateObject),
Nobody,
}
#[inline(never)]
pub(super) fn readable_record_for_active_snapshot(
notice: ReadNotice<'_>,
records: &RecordChain,
id: ObjectId,
) -> Result<Rc<StateRecord>, StateReadFailure> {
let readable = |snapshot_id, invalid: &SnapshotIdSet| records.readable(snapshot_id, invalid);
let snapshot = active_snapshot();
if let ReadNotice::Observers(state) = notice {
snapshot.record_read(state);
}
let snapshot_id = snapshot.snapshot_id();
if let Some(record) = snapshot.with_invalid(|invalid| readable(snapshot_id, invalid)) {
return Ok(record);
}
let fresh_snapshot = active_snapshot();
let fresh_id = fresh_snapshot.snapshot_id();
let fresh_invalid = fresh_snapshot.invalid();
if let Some(record) = readable(fresh_id, &fresh_invalid) {
return Ok(record);
}
let global = GlobalSnapshot::get_or_create();
let global_id = global.snapshot_id();
let global_invalid = global.invalid();
if let Some(record) = readable(global_id, &global_invalid) {
return Ok(record);
}
Err(StateReadFailure {
state_id: id,
snapshot_id,
fresh_snapshot_id: fresh_id,
fresh_invalid,
record_chain: records.ids(),
})
}
#[expect(
clippy::multiple_inherent_impl,
reason = "the read that tells no observer, beside the record it reads"
)]
impl<T: Clone + 'static> SnapshotMutableState<T> {
pub(crate) fn value_unobserved(&self) -> T {
readable_record_for_active_snapshot(ReadNotice::Nobody, &self.records, self.id)
.unwrap_or_else(|failure| panic!("{failure}"))
.with_value(Clone::clone)
}
}