1#![forbid(unsafe_code)]
4#![warn(missing_docs)]
5
6use std::time::{SystemTime, UNIX_EPOCH};
7
8use pacta_contract::kernel::{Directive, Kernel, Notice, StepResult};
9use pacta_contract::{Outcome, Registry, Timestamp};
10use pacta_executor::{Execution, Executor};
11
12fn current_time() -> Timestamp {
16 let millis = SystemTime::now()
17 .duration_since(UNIX_EPOCH)
18 .map(|elapsed| u64::try_from(elapsed.as_millis()).unwrap_or(u64::MAX))
19 .unwrap_or(0);
20 Timestamp::from_millis(millis)
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28#[non_exhaustive]
29pub enum Step {
30 Idle,
32 Fulfilled,
34 Breached,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
43#[non_exhaustive]
44pub enum DriverError<RegistryError, ExecutorError> {
45 Registry(RegistryError),
47 Executor(ExecutorError),
50}
51
52impl<RegistryError, ExecutorError> std::fmt::Display for DriverError<RegistryError, ExecutorError>
53where
54 RegistryError: std::error::Error,
55 ExecutorError: std::error::Error,
56{
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 match self {
59 Self::Registry(error) => write!(f, "registry operation failed: {error}"),
60 Self::Executor(error) => write!(f, "executor infrastructure failed: {error}"),
61 }
62 }
63}
64
65impl<RegistryError, ExecutorError> std::error::Error for DriverError<RegistryError, ExecutorError>
66where
67 RegistryError: std::error::Error + 'static,
68 ExecutorError: std::error::Error + 'static,
69{
70 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
71 match self {
72 Self::Registry(error) => Some(error),
73 Self::Executor(error) => Some(error),
74 }
75 }
76}
77
78pub struct Driver<R, E> {
91 registry: R,
92 executor: E,
93 dockets: Vec<String>,
94}
95
96impl<R, E> Driver<R, E> {
97 pub fn new(registry: R, executor: E, dockets: impl IntoIterator<Item = String>) -> Self {
99 Self {
100 registry,
101 executor,
102 dockets: dockets.into_iter().collect(),
103 }
104 }
105
106 #[must_use]
108 pub fn registry(&self) -> &R {
109 &self.registry
110 }
111
112 #[must_use]
114 pub fn executor(&self) -> &E {
115 &self.executor
116 }
117}
118
119impl<R, E> Driver<R, E>
120where
121 R: Registry,
122 E: Executor,
123{
124 pub fn step(&mut self) -> Result<Step, DriverError<R::Error, E::Error>> {
128 let dockets: Vec<&str> = self.dockets.iter().map(String::as_str).collect();
129 let now = current_time();
130 let mut kernel = Kernel::new();
131 let mut pending_executor_error: Option<E::Error> = None;
132
133 loop {
134 if let Some(result) = kernel.result() {
135 return match result {
136 StepResult::Idle => Ok(Step::Idle),
137 StepResult::Settled(outcome) => Ok(match outcome {
138 Outcome::Fulfilled => Step::Fulfilled,
139 Outcome::Breached => Step::Breached,
140 }),
141 StepResult::Unsettled => Err(DriverError::Executor(
144 pending_executor_error
145 .expect("an unsettled step implies a pending executor error"),
146 )),
147 _ => unreachable!("driver handles every current kernel step result"),
148 };
149 }
150
151 match kernel.poll() {
152 Directive::Claim => {
153 let claim = self
154 .registry
155 .claim(&dockets, now)
156 .map_err(DriverError::Registry)?;
157 kernel.on_event(Notice::Claimed(claim));
158 }
159 Directive::Execute(pact) => match self.executor.execute(Execution::new(pact)) {
160 Ok(outcome) => kernel.on_event(Notice::Executed(outcome)),
161 Err(error) => {
162 pending_executor_error = Some(error);
163 kernel.on_event(Notice::ExecutionFailed);
164 }
165 },
166 Directive::Settle(retainer, outcome) => {
167 match outcome {
168 Outcome::Fulfilled => self
169 .registry
170 .fulfill(&retainer)
171 .map_err(DriverError::Registry)?,
172 Outcome::Breached => self
173 .registry
174 .breach(&retainer)
175 .map_err(DriverError::Registry)?,
176 }
177 kernel.on_event(Notice::Settled);
178 }
179 Directive::Idle => return Ok(Step::Idle),
180 _ => unreachable!("driver handles every current kernel directive"),
181 }
182 }
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use std::sync::Mutex;
189
190 use pacta_contract::{Claim, Pact, Retainer, Timestamp};
191 use uuid::Uuid;
192
193 use super::*;
194
195 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
196 struct TestError;
197
198 impl std::fmt::Display for TestError {
199 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200 write!(f, "test error")
201 }
202 }
203
204 impl std::error::Error for TestError {}
205
206 #[derive(Default)]
207 struct RegistryState {
208 claim: Option<Claim>,
209 claimed_dockets: Vec<Vec<String>>,
210 fulfilled: usize,
211 breached: usize,
212 }
213
214 #[derive(Default)]
215 struct TestRegistry {
216 state: Mutex<RegistryState>,
217 }
218
219 impl TestRegistry {
220 fn with_claim(claim: Claim) -> Self {
221 Self {
222 state: Mutex::new(RegistryState {
223 claim: Some(claim),
224 ..RegistryState::default()
225 }),
226 }
227 }
228 }
229
230 impl Registry for TestRegistry {
231 type Error = TestError;
232
233 fn claim(&self, dockets: &[&str], _now: Timestamp) -> Result<Option<Claim>, Self::Error> {
234 self.state
235 .lock()
236 .expect("registry state should not be poisoned")
237 .claimed_dockets
238 .push(dockets.iter().map(ToString::to_string).collect());
239 Ok(self
240 .state
241 .lock()
242 .expect("registry state should not be poisoned")
243 .claim
244 .take())
245 }
246
247 fn heartbeat(&self, _retainer: &Retainer, _now: Timestamp) -> Result<(), Self::Error> {
248 Ok(())
249 }
250
251 fn fulfill(&self, _retainer: &Retainer) -> Result<(), Self::Error> {
252 self.state
253 .lock()
254 .expect("registry state should not be poisoned")
255 .fulfilled += 1;
256 Ok(())
257 }
258
259 fn breach(&self, _retainer: &Retainer) -> Result<(), Self::Error> {
260 self.state
261 .lock()
262 .expect("registry state should not be poisoned")
263 .breached += 1;
264 Ok(())
265 }
266
267 fn release(
268 &self,
269 _retainer: &Retainer,
270 _reclaimable_at: Timestamp,
271 ) -> Result<(), Self::Error> {
272 Ok(())
273 }
274 }
275
276 struct TestExecutor {
277 outcome: Result<Outcome, TestError>,
278 executions: usize,
279 }
280
281 impl Executor for TestExecutor {
282 type Error = TestError;
283
284 fn execute(&mut self, _execution: Execution) -> Result<Outcome, Self::Error> {
285 self.executions += 1;
286 self.outcome
287 }
288 }
289
290 fn claim() -> Claim {
291 Claim::new(
292 Pact::new(
293 Uuid::new_v4(),
294 "default".to_string(),
295 "example".to_string(),
296 Vec::new(),
297 ),
298 Retainer::new(Uuid::new_v4()),
299 Timestamp::from_millis(0),
300 )
301 }
302
303 #[test]
304 fn successful_execution_fulfills_claim() {
305 let registry = TestRegistry::with_claim(claim());
306 let executor = TestExecutor {
307 outcome: Ok(Outcome::Fulfilled),
308 executions: 0,
309 };
310 let mut driver = Driver::new(registry, executor, ["default".to_string()]);
311
312 assert_eq!(driver.step(), Ok(Step::Fulfilled));
313 let state = driver
314 .registry()
315 .state
316 .lock()
317 .expect("registry state should not be poisoned");
318 assert_eq!(state.fulfilled, 1);
319 assert_eq!(state.breached, 0);
320 drop(state);
321 assert_eq!(driver.executor().executions, 1);
322 }
323
324 #[test]
325 fn breached_execution_breaches_claim() {
326 let registry = TestRegistry::with_claim(claim());
327 let executor = TestExecutor {
328 outcome: Ok(Outcome::Breached),
329 executions: 0,
330 };
331 let mut driver = Driver::new(registry, executor, ["default".to_string()]);
332
333 assert_eq!(driver.step(), Ok(Step::Breached));
334 let state = driver
335 .registry()
336 .state
337 .lock()
338 .expect("registry state should not be poisoned");
339 assert_eq!(state.fulfilled, 0);
340 assert_eq!(state.breached, 1);
341 drop(state);
342 assert_eq!(driver.executor().executions, 1);
343 }
344
345 #[test]
346 fn executor_error_leaves_claim_unsettled() {
347 let registry = TestRegistry::with_claim(claim());
348 let executor = TestExecutor {
349 outcome: Err(TestError),
350 executions: 0,
351 };
352 let mut driver = Driver::new(registry, executor, ["default".to_string()]);
353
354 assert_eq!(driver.step(), Err(DriverError::Executor(TestError)));
359 let state = driver
360 .registry()
361 .state
362 .lock()
363 .expect("registry state should not be poisoned");
364 assert_eq!(state.fulfilled, 0);
365 assert_eq!(state.breached, 0);
366 drop(state);
367 assert_eq!(driver.executor().executions, 1);
368 }
369
370 #[test]
371 fn empty_docket_is_idle() {
372 let registry = TestRegistry::default();
373 let executor = TestExecutor {
374 outcome: Ok(Outcome::Fulfilled),
375 executions: 0,
376 };
377 let mut driver = Driver::new(registry, executor, ["default".to_string()]);
378
379 assert_eq!(driver.step(), Ok(Step::Idle));
380 let state = driver
381 .registry()
382 .state
383 .lock()
384 .expect("registry state should not be poisoned");
385 assert_eq!(state.fulfilled, 0);
386 assert_eq!(state.breached, 0);
387 drop(state);
388 assert_eq!(driver.executor().executions, 0);
389 }
390
391 #[test]
392 fn driver_error_displays_and_exposes_source() {
393 use std::error::Error;
394
395 let executor_error: DriverError<TestError, TestError> = DriverError::Executor(TestError);
396 assert_eq!(
397 executor_error.to_string(),
398 "executor infrastructure failed: test error"
399 );
400 assert_eq!(
401 executor_error
402 .source()
403 .expect("driver error should expose its source")
404 .to_string(),
405 "test error"
406 );
407
408 let registry_error: DriverError<TestError, TestError> = DriverError::Registry(TestError);
409 assert_eq!(
410 registry_error.to_string(),
411 "registry operation failed: test error"
412 );
413 assert_eq!(
414 registry_error
415 .source()
416 .expect("driver error should expose its source")
417 .to_string(),
418 "test error"
419 );
420 }
421}