use std::{
fmt,
hash::{Hash, Hasher},
};
use sim_incremental_core::{IncrementalError, QueryBudgets};
use sim_kernel::{CanonicalKey, CapabilityName, Cx, Value};
pub const HARD_MAX_WORK: usize = 1_000_000;
pub const HARD_MAX_OBSERVATIONS: usize = 100_000;
pub const HARD_MAX_QUERY_DEPTH: usize = 64;
pub const HARD_MAX_OUTPUT: usize = 1_048_576;
pub const HARD_MAX_EXPR_DEPTH: usize = 128;
pub(super) type ContextFactory = dyn Fn() -> Cx + Send + Sync + 'static;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum CalcQuery {
Cell(String),
NameSlot(String),
LookupStep(String),
Listing(String),
MountEpoch(String),
EffectivePolicy(String),
AuthorityPolicy(String),
CodecRegistry,
AuthorityCeiling,
ForceEpoch(String),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CalcLimits {
pub max_work: usize,
pub max_observations: usize,
pub max_query_depth: usize,
pub max_output: usize,
}
impl CalcLimits {
#[must_use]
pub const fn new(
max_work: usize,
max_observations: usize,
max_query_depth: usize,
max_output: usize,
) -> Self {
Self {
max_work,
max_observations,
max_query_depth,
max_output,
}
}
pub(super) fn clamped(self) -> QueryBudgets {
QueryBudgets::new(
self.max_work.min(HARD_MAX_WORK),
self.max_observations.min(HARD_MAX_OBSERVATIONS),
self.max_query_depth.min(HARD_MAX_QUERY_DEPTH),
self.max_output.min(HARD_MAX_OUTPUT),
)
}
}
impl Default for CalcLimits {
fn default() -> Self {
Self::new(
HARD_MAX_WORK,
HARD_MAX_OBSERVATIONS,
HARD_MAX_QUERY_DEPTH,
HARD_MAX_OUTPUT,
)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum CellFailure {
Evaluation {
message: String,
},
Cycle {
path: Vec<CalcQuery>,
},
ExpressionDepth {
limit: usize,
},
Blocked {
path: String,
reason: String,
},
RequiredCapability {
path: String,
capability: CapabilityName,
},
}
impl fmt::Display for CellFailure {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Evaluation { message } => write!(f, "cell evaluation failed: {message}"),
Self::Cycle { path } => write!(f, "cell dependency cycle {path:?}"),
Self::ExpressionDepth { limit } => {
write!(f, "cell expression depth exceeds hard limit {limit}")
}
Self::Blocked { path, reason } => {
write!(f, "cell {path} is blocked: {reason}")
}
Self::RequiredCapability { path, capability } => {
write!(f, "cell {path} requires capability {capability}")
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CalcError {
NotCalculated {
path: String,
},
Cell(CellFailure),
Incremental(IncrementalError<CalcQuery>),
UnknownAutomaticContinuation {
generation: u64,
},
CorruptAutomaticQueue {
cell: String,
},
}
impl fmt::Display for CalcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NotCalculated { path } => write!(f, "cell {path} has no current result"),
Self::Cell(failure) => failure.fmt(f),
Self::Incremental(error) => error.fmt(f),
Self::UnknownAutomaticContinuation { generation } => {
write!(f, "unknown automatic continuation generation {generation}")
}
Self::CorruptAutomaticQueue { cell } => {
write!(f, "corrupt automatic queue entry for {cell}")
}
}
}
}
impl std::error::Error for CalcError {}
#[derive(Clone)]
pub struct LastGoodValue {
pub(super) value: Value,
}
impl LastGoodValue {
#[must_use]
pub const fn label(&self) -> &'static str {
"last-good"
}
#[must_use]
pub fn value(&self) -> &Value {
&self.value
}
}
#[derive(Clone)]
pub(super) enum MemoOutcome {
Value(Value),
Failure(CellFailure),
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
enum MemoIdentity {
Canonical(CanonicalKey),
Volatile(u64),
Failure(CellFailure),
}
#[derive(Clone)]
pub(super) struct MemoValue {
pub(super) outcome: MemoOutcome,
identity: MemoIdentity,
}
impl MemoValue {
pub(super) fn canonical(value: Value, key: CanonicalKey) -> Self {
Self {
outcome: MemoOutcome::Value(value),
identity: MemoIdentity::Canonical(key),
}
}
pub(super) fn volatile(value: Value, nonce: u64) -> Self {
Self {
outcome: MemoOutcome::Value(value),
identity: MemoIdentity::Volatile(nonce),
}
}
pub(super) fn failure(failure: CellFailure) -> Self {
Self {
outcome: MemoOutcome::Failure(failure.clone()),
identity: MemoIdentity::Failure(failure),
}
}
pub(super) fn is_volatile(&self) -> bool {
matches!(self.identity, MemoIdentity::Volatile(_))
}
}
impl fmt::Debug for MemoValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MemoValue")
.field("identity", &self.identity)
.finish_non_exhaustive()
}
}
impl Hash for MemoValue {
fn hash<H: Hasher>(&self, state: &mut H) {
self.identity.hash(state);
}
}