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