1use std::{
2 fmt,
3 hash::{Hash, Hasher},
4};
5
6use sim_incremental_core::{IncrementalError, QueryBudgets};
7use sim_kernel::{CanonicalKey, CapabilityName, Cx, Value};
8
9pub const HARD_MAX_WORK: usize = 1_000_000;
15pub const HARD_MAX_OBSERVATIONS: usize = 100_000;
16pub const HARD_MAX_QUERY_DEPTH: usize = 64;
17pub const HARD_MAX_OUTPUT: usize = 1_048_576;
18pub const HARD_MAX_EXPR_DEPTH: usize = 128;
19
20pub(super) type ContextFactory = dyn Fn() -> Cx + Send + Sync + 'static;
21
22#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
24pub enum CalcQuery {
25 Cell(String),
27 NameSlot(String),
29 LookupStep(String),
31 Listing(String),
33 MountEpoch(String),
35 EffectivePolicy(String),
37 AuthorityPolicy(String),
39 CodecRegistry,
41 AuthorityCeiling,
43 ForceEpoch(String),
45}
46
47#[derive(Clone, Copy, Debug, Eq, PartialEq)]
49pub struct CalcLimits {
50 pub max_work: usize,
52 pub max_observations: usize,
54 pub max_query_depth: usize,
56 pub max_output: usize,
58}
59
60impl CalcLimits {
61 #[must_use]
63 pub const fn new(
64 max_work: usize,
65 max_observations: usize,
66 max_query_depth: usize,
67 max_output: usize,
68 ) -> Self {
69 Self {
70 max_work,
71 max_observations,
72 max_query_depth,
73 max_output,
74 }
75 }
76
77 pub(super) fn clamped(self) -> QueryBudgets {
78 QueryBudgets::new(
79 self.max_work.min(HARD_MAX_WORK),
80 self.max_observations.min(HARD_MAX_OBSERVATIONS),
81 self.max_query_depth.min(HARD_MAX_QUERY_DEPTH),
82 self.max_output.min(HARD_MAX_OUTPUT),
83 )
84 }
85}
86
87impl Default for CalcLimits {
88 fn default() -> Self {
89 Self::new(
90 HARD_MAX_WORK,
91 HARD_MAX_OBSERVATIONS,
92 HARD_MAX_QUERY_DEPTH,
93 HARD_MAX_OUTPUT,
94 )
95 }
96}
97
98#[derive(Clone, Debug, Eq, Hash, PartialEq)]
100pub enum CellFailure {
101 Evaluation {
103 message: String,
105 },
106 Cycle {
108 path: Vec<CalcQuery>,
110 },
111 ExpressionDepth {
113 limit: usize,
115 },
116 Blocked {
118 path: String,
120 reason: String,
122 },
123 RequiredCapability {
125 path: String,
127 capability: CapabilityName,
129 },
130}
131
132impl fmt::Display for CellFailure {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 match self {
135 Self::Evaluation { message } => write!(f, "cell evaluation failed: {message}"),
136 Self::Cycle { path } => write!(f, "cell dependency cycle {path:?}"),
137 Self::ExpressionDepth { limit } => {
138 write!(f, "cell expression depth exceeds hard limit {limit}")
139 }
140 Self::Blocked { path, reason } => {
141 write!(f, "cell {path} is blocked: {reason}")
142 }
143 Self::RequiredCapability { path, capability } => {
144 write!(f, "cell {path} requires capability {capability}")
145 }
146 }
147 }
148}
149
150#[derive(Clone, Debug, Eq, PartialEq)]
152pub enum CalcError {
153 NotCalculated {
155 path: String,
157 },
158 Cell(CellFailure),
160 Incremental(IncrementalError<CalcQuery>),
162 UnknownAutomaticContinuation {
164 generation: u64,
166 },
167 CorruptAutomaticQueue {
169 cell: String,
171 },
172}
173
174impl fmt::Display for CalcError {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 match self {
177 Self::NotCalculated { path } => write!(f, "cell {path} has no current result"),
178 Self::Cell(failure) => failure.fmt(f),
179 Self::Incremental(error) => error.fmt(f),
180 Self::UnknownAutomaticContinuation { generation } => {
181 write!(f, "unknown automatic continuation generation {generation}")
182 }
183 Self::CorruptAutomaticQueue { cell } => {
184 write!(f, "corrupt automatic queue entry for {cell}")
185 }
186 }
187 }
188}
189
190impl std::error::Error for CalcError {}
191
192#[derive(Clone)]
194pub struct LastGoodValue {
195 pub(super) value: Value,
196}
197
198impl LastGoodValue {
199 #[must_use]
201 pub const fn label(&self) -> &'static str {
202 "last-good"
203 }
204
205 #[must_use]
207 pub fn value(&self) -> &Value {
208 &self.value
209 }
210}
211
212#[derive(Clone)]
213pub(super) enum MemoOutcome {
214 Value(Value),
215 Failure(CellFailure),
216}
217
218#[derive(Clone, Debug, Eq, Hash, PartialEq)]
219enum MemoIdentity {
220 Canonical(CanonicalKey),
221 Volatile(u64),
222 Failure(CellFailure),
223}
224
225#[derive(Clone)]
226pub(super) struct MemoValue {
227 pub(super) outcome: MemoOutcome,
228 identity: MemoIdentity,
229}
230
231impl MemoValue {
232 pub(super) fn canonical(value: Value, key: CanonicalKey) -> Self {
233 Self {
234 outcome: MemoOutcome::Value(value),
235 identity: MemoIdentity::Canonical(key),
236 }
237 }
238
239 pub(super) fn volatile(value: Value, nonce: u64) -> Self {
240 Self {
241 outcome: MemoOutcome::Value(value),
242 identity: MemoIdentity::Volatile(nonce),
243 }
244 }
245
246 pub(super) fn failure(failure: CellFailure) -> Self {
247 Self {
248 outcome: MemoOutcome::Failure(failure.clone()),
249 identity: MemoIdentity::Failure(failure),
250 }
251 }
252
253 pub(super) fn is_volatile(&self) -> bool {
254 matches!(self.identity, MemoIdentity::Volatile(_))
255 }
256}
257
258impl fmt::Debug for MemoValue {
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260 f.debug_struct("MemoValue")
261 .field("identity", &self.identity)
262 .finish_non_exhaustive()
263 }
264}
265
266impl Hash for MemoValue {
267 fn hash<H: Hasher>(&self, state: &mut H) {
268 self.identity.hash(state);
269 }
270}