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, Transition};
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 lease_millis(&self) -> u64 {
248 0
249 }
250
251 fn apply(
254 &self,
255 _retainer: &Retainer,
256 _transition: &Transition<'_>,
257 ) -> Result<(), Self::Error> {
258 Ok(())
259 }
260
261 fn fulfill(&self, _retainer: &Retainer) -> Result<(), Self::Error> {
264 self.state
265 .lock()
266 .expect("registry state should not be poisoned")
267 .fulfilled += 1;
268 Ok(())
269 }
270
271 fn breach(&self, _retainer: &Retainer) -> Result<(), Self::Error> {
272 self.state
273 .lock()
274 .expect("registry state should not be poisoned")
275 .breached += 1;
276 Ok(())
277 }
278 }
279
280 struct TestExecutor {
281 outcome: Result<Outcome, TestError>,
282 executions: usize,
283 }
284
285 impl Executor for TestExecutor {
286 type Error = TestError;
287
288 fn execute(&mut self, _execution: Execution) -> Result<Outcome, Self::Error> {
289 self.executions += 1;
290 self.outcome
291 }
292 }
293
294 fn claim() -> Claim {
295 Claim::new(
296 Pact::new(
297 Uuid::new_v4(),
298 "default".to_string(),
299 "example".to_string(),
300 Vec::new(),
301 ),
302 Retainer::new(Uuid::new_v4()),
303 Timestamp::from_millis(0),
304 )
305 }
306
307 #[test]
308 fn successful_execution_fulfills_claim() {
309 let registry = TestRegistry::with_claim(claim());
310 let executor = TestExecutor {
311 outcome: Ok(Outcome::Fulfilled),
312 executions: 0,
313 };
314 let mut driver = Driver::new(registry, executor, ["default".to_string()]);
315
316 assert_eq!(driver.step(), Ok(Step::Fulfilled));
317 let state = driver
318 .registry()
319 .state
320 .lock()
321 .expect("registry state should not be poisoned");
322 assert_eq!(state.fulfilled, 1);
323 assert_eq!(state.breached, 0);
324 drop(state);
325 assert_eq!(driver.executor().executions, 1);
326 }
327
328 #[test]
329 fn breached_execution_breaches_claim() {
330 let registry = TestRegistry::with_claim(claim());
331 let executor = TestExecutor {
332 outcome: Ok(Outcome::Breached),
333 executions: 0,
334 };
335 let mut driver = Driver::new(registry, executor, ["default".to_string()]);
336
337 assert_eq!(driver.step(), Ok(Step::Breached));
338 let state = driver
339 .registry()
340 .state
341 .lock()
342 .expect("registry state should not be poisoned");
343 assert_eq!(state.fulfilled, 0);
344 assert_eq!(state.breached, 1);
345 drop(state);
346 assert_eq!(driver.executor().executions, 1);
347 }
348
349 #[test]
350 fn executor_error_leaves_claim_unsettled() {
351 let registry = TestRegistry::with_claim(claim());
352 let executor = TestExecutor {
353 outcome: Err(TestError),
354 executions: 0,
355 };
356 let mut driver = Driver::new(registry, executor, ["default".to_string()]);
357
358 assert_eq!(driver.step(), Err(DriverError::Executor(TestError)));
363 let state = driver
364 .registry()
365 .state
366 .lock()
367 .expect("registry state should not be poisoned");
368 assert_eq!(state.fulfilled, 0);
369 assert_eq!(state.breached, 0);
370 drop(state);
371 assert_eq!(driver.executor().executions, 1);
372 }
373
374 #[test]
375 fn empty_docket_is_idle() {
376 let registry = TestRegistry::default();
377 let executor = TestExecutor {
378 outcome: Ok(Outcome::Fulfilled),
379 executions: 0,
380 };
381 let mut driver = Driver::new(registry, executor, ["default".to_string()]);
382
383 assert_eq!(driver.step(), Ok(Step::Idle));
384 let state = driver
385 .registry()
386 .state
387 .lock()
388 .expect("registry state should not be poisoned");
389 assert_eq!(state.fulfilled, 0);
390 assert_eq!(state.breached, 0);
391 drop(state);
392 assert_eq!(driver.executor().executions, 0);
393 }
394
395 #[test]
396 fn driver_error_displays_and_exposes_source() {
397 use std::error::Error;
398
399 let executor_error: DriverError<TestError, TestError> = DriverError::Executor(TestError);
400 assert_eq!(
401 executor_error.to_string(),
402 "executor infrastructure failed: test error"
403 );
404 assert_eq!(
405 executor_error
406 .source()
407 .expect("driver error should expose its source")
408 .to_string(),
409 "test error"
410 );
411
412 let registry_error: DriverError<TestError, TestError> = DriverError::Registry(TestError);
413 assert_eq!(
414 registry_error.to_string(),
415 "registry operation failed: test error"
416 );
417 assert_eq!(
418 registry_error
419 .source()
420 .expect("driver error should expose its source")
421 .to_string(),
422 "test error"
423 );
424 }
425}