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