1use std::future::Future;
10use std::pin::Pin;
11use std::sync::atomic::{AtomicBool, Ordering};
12use std::sync::{Arc, Mutex, Weak};
13use std::task::{Context, Poll, Waker};
14
15use rustev_contract::acquisition::AcquisitionRequest;
16use rustev_contract::descriptor::BackendDescriptor;
17use rustev_contract::dynamic::{DynamicCapability, DynamicOutcome, DynamicRawOutput, TemplateRef};
18use rustev_contract::eval_report::EvalReport;
19use rustev_contract::output::RawOutput;
20use rustev_contract::remote::ServedIdentity;
21use rustev_contract::run::{Charge, CostBound, CostModel, RunRecord};
22use rustev_contract::snapshot::Snapshot;
23use rustev_contract::source::SourceDescriptor;
24use rustev_contract::time::DurationMs;
25
26pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
28
29#[derive(Debug, Clone)]
31pub struct CallContext {
32 pub remaining_ms: DurationMs,
35 pub trace_id: String,
36 pub principal_handle: Vec<u8>,
39}
40
41pub trait ContextSource: Send + Sync {
44 fn fetch<'a>(
45 &'a self,
46 fields: &'a [String],
47 cx: &'a CallContext,
48 ) -> BoxFuture<'a, Result<Snapshot, String>>;
49}
50
51pub struct AttemptCall<'a> {
53 pub projection: &'a [u8],
55 pub attempt_id: &'a str,
57 pub cancel: CancelSignal,
59 pub cx: &'a CallContext,
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum AdapterFailure {
66 Transient,
68 Overloaded,
70 Permanent,
72 Cancelled,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum CancelAck {
79 NotRequested,
81 Stopped,
83 Unconfirmed,
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum RemoteEnd {
91 Finished,
94 PossiblyContinuing,
97}
98
99#[derive(Debug, Clone, PartialEq)]
101pub struct AttemptReport {
102 pub result: Result<RawOutput, (AdapterFailure, String)>,
103 pub charge: Charge,
104 pub cancel: CancelAck,
105 pub remote: RemoteEnd,
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
115pub struct BatchCapability {
116 pub compatibility: String,
117 pub work_units: u64,
118}
119
120#[derive(Debug, Clone)]
122pub struct BatchMemberCall {
123 pub projection: Vec<u8>,
124 pub attempt_id: String,
125 pub cancel: CancelSignal,
126 pub cx: CallContext,
127}
128
129pub struct BatchAttemptCall {
131 pub batch_id: String,
132 pub cancel: CancelSignal,
133 pub members: Vec<BatchMemberCall>,
134}
135
136#[derive(Debug, Clone, PartialEq)]
139pub struct BatchMemberReport {
140 pub attempt_id: String,
141 pub result: Result<RawOutput, (AdapterFailure, String)>,
142}
143
144#[derive(Debug, Clone, PartialEq)]
147pub struct BatchAttemptReport {
148 pub result: Result<Vec<BatchMemberReport>, (AdapterFailure, String)>,
149 pub charge: Charge,
150 pub cancel: CancelAck,
151 pub remote: RemoteEnd,
152}
153
154pub trait GenerationFields: Send + Sync {
157 fn field(&self, name: &str, value: rustev_contract::output::TextValue);
158}
159
160pub trait DecisionBackend: Send + Sync {
163 fn descriptor(&self) -> &BackendDescriptor;
164 fn cost_model(&self) -> CostModel;
166 fn cost_bound(&self, projection: &[u8]) -> CostBound;
169 fn infer<'a>(&'a self, call: AttemptCall<'a>) -> BoxFuture<'a, AttemptReport>;
170
171 fn infer_generation<'a>(
174 &'a self,
175 call: AttemptCall<'a>,
176 _fields: &'a dyn GenerationFields,
177 ) -> BoxFuture<'a, AttemptReport> {
178 self.infer(call)
179 }
180
181 fn batch_capability(&self, _projection: &[u8]) -> Option<BatchCapability> {
183 None
184 }
185
186 fn take_remote_evidence(
192 &self,
193 _attempt_id: &str,
194 ) -> Option<rustev_contract::run::RemoteEvidence> {
195 None
196 }
197
198 fn infer_batch<'a>(&'a self, _call: BatchAttemptCall) -> BoxFuture<'a, BatchAttemptReport> {
201 Box::pin(async {
202 BatchAttemptReport {
203 result: Err((
204 AdapterFailure::Permanent,
205 "backend did not implement declared batch semantics".into(),
206 )),
207 charge: Charge::Unknown,
208 cancel: CancelAck::NotRequested,
209 remote: RemoteEnd::PossiblyContinuing,
210 }
211 })
212 }
213}
214
215pub struct DynamicCall<'a> {
217 pub projection: &'a [u8],
219 pub template: &'a TemplateRef,
222 pub attempt_id: &'a str,
223 pub cancel: CancelSignal,
225 pub cx: &'a CallContext,
226}
227
228#[derive(Debug, Clone, PartialEq)]
231pub struct DynamicReport {
232 pub result: Result<DynamicRawOutput, (AdapterFailure, String)>,
233 pub served: ServedIdentity,
236 pub charge: Charge,
237 pub cancel: CancelAck,
238 pub remote: RemoteEnd,
239}
240
241pub trait DynamicBackend: Send + Sync {
246 fn backend_id(&self) -> &str;
247 fn dynamic_capability(&self) -> Option<&DynamicCapability>;
250 fn cost_bound(&self, projection: &[u8]) -> CostBound;
252 fn ask<'a>(&'a self, call: DynamicCall<'a>) -> BoxFuture<'a, DynamicReport>;
253}
254
255pub trait DynamicEvidenceSink: Send + Sync {
259 fn deliver<'a>(&'a self, outcome: &'a DynamicOutcome) -> BoxFuture<'a, Result<String, String>>;
260}
261
262pub trait Evaluator: Send + Sync {
264 fn evaluate<'a>(&'a self, cx: &'a CallContext) -> BoxFuture<'a, EvalReport>;
265}
266
267pub trait EvidenceSink: Send + Sync {
271 fn deliver<'a>(&'a self, record: &'a RunRecord) -> BoxFuture<'a, Result<String, String>>;
272
273 fn accepts(&self, schema: &str) -> bool {
276 schema == rustev_contract::schema::RUN
277 }
278
279 fn run_schema(&self) -> &'static str {
285 rustev_contract::schema::RUN
286 }
287}
288
289#[derive(Clone, Default)]
293pub struct CancelSignal(Arc<Inner>);
294
295#[derive(Default)]
296struct Inner {
297 raised: AtomicBool,
298 state: Mutex<State>,
299}
300
301#[derive(Default)]
302struct State {
303 wakers: Vec<Waker>,
304 children: Vec<Weak<Inner>>,
305}
306
307impl std::fmt::Debug for CancelSignal {
308 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
309 f.debug_tuple("CancelSignal")
310 .field(&self.is_raised())
311 .finish()
312 }
313}
314
315fn lock(m: &Mutex<State>) -> std::sync::MutexGuard<'_, State> {
316 m.lock().unwrap_or_else(|e| e.into_inner())
317}
318
319impl Inner {
320 fn raise(&self) {
321 if self.raised.swap(true, Ordering::SeqCst) {
322 return;
323 }
324 let (wakers, children) = {
325 let mut s = lock(&self.state);
326 (
327 std::mem::take(&mut s.wakers),
328 std::mem::take(&mut s.children),
329 )
330 };
331 for w in wakers {
332 w.wake();
333 }
334 for c in children.iter().filter_map(Weak::upgrade) {
335 c.raise();
336 }
337 }
338}
339
340impl CancelSignal {
341 pub fn new() -> Self {
342 Self::default()
343 }
344
345 pub fn raise(&self) {
346 self.0.raise();
347 }
348
349 pub fn is_raised(&self) -> bool {
350 self.0.raised.load(Ordering::SeqCst)
351 }
352
353 pub fn child(&self) -> CancelSignal {
355 let child = CancelSignal::new();
356 {
357 let mut s = lock(&self.0.state);
358 if !self.is_raised() {
359 s.children.retain(|w| w.strong_count() > 0);
360 s.children.push(Arc::downgrade(&child.0));
361 return child;
362 }
363 }
364 child.raise();
365 child
366 }
367
368 pub fn poll_raised(&self, cx: &mut Context<'_>) -> Poll<()> {
370 if self.is_raised() {
371 return Poll::Ready(());
372 }
373 let mut s = lock(&self.0.state);
374 if self.is_raised() {
375 return Poll::Ready(());
376 }
377 if !s.wakers.iter().any(|w| w.will_wake(cx.waker())) {
378 s.wakers.push(cx.waker().clone());
379 }
380 Poll::Pending
381 }
382
383 pub fn registered(&self) -> usize {
386 let s = lock(&self.0.state);
387 s.wakers.len() + s.children.iter().filter(|w| w.strong_count() > 0).count()
388 }
389
390 pub fn raised(&self) -> impl Future<Output = ()> + Send + '_ {
392 std::future::poll_fn(move |cx| self.poll_raised(cx))
393 }
394}
395
396#[cfg(test)]
397mod tests {
398 use super::*;
399
400 #[test]
401 fn raising_a_parent_raises_its_children_and_not_the_reverse() {
402 let parent = CancelSignal::new();
403 let a = parent.child();
404 let b = parent.child();
405 b.raise();
406 assert!(b.is_raised() && !a.is_raised() && !parent.is_raised());
407 parent.raise();
408 assert!(a.is_raised());
409 let late = parent.child();
410 assert!(late.is_raised(), "a child of a raised signal starts raised");
411 }
412}
413
414pub trait AcquisitionSource: Send + Sync {
417 fn descriptor(&self) -> &SourceDescriptor;
418 fn cost_bound(&self, request: &AcquisitionRequest) -> CostBound;
419 fn acquire<'a>(&'a self, call: AcquireCall<'a>) -> BoxFuture<'a, AcquireReport>;
420}
421
422pub struct AcquireCall<'a> {
424 pub operation: &'a str,
425 pub request: &'a rustev_contract::acquisition::AcquisitionRequest,
426 pub request_id: &'a rustev_contract::ids::AcquisitionRequestId,
427 pub attempt_id: &'a str,
428 pub cancel: CancelSignal,
429 pub cx: &'a CallContext,
430}
431
432#[derive(Debug, Clone, Copy, PartialEq, Eq)]
433pub enum AcquisitionFailure {
434 Transient,
435 Overloaded,
436 Permanent,
437 Denied,
438}
439
440#[derive(Debug, Clone, PartialEq)]
442pub struct AcquireReport {
443 pub result: Result<Vec<u8>, (AcquisitionFailure, String)>,
444 pub charge: Charge,
445 pub cancel: CancelAck,
446}