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 DecisionBackend: Send + Sync {
157 fn descriptor(&self) -> &BackendDescriptor;
158 fn cost_model(&self) -> CostModel;
160 fn cost_bound(&self, projection: &[u8]) -> CostBound;
163 fn infer<'a>(&'a self, call: AttemptCall<'a>) -> BoxFuture<'a, AttemptReport>;
164
165 fn batch_capability(&self, _projection: &[u8]) -> Option<BatchCapability> {
167 None
168 }
169
170 fn take_remote_evidence(
176 &self,
177 _attempt_id: &str,
178 ) -> Option<rustev_contract::run::RemoteEvidence> {
179 None
180 }
181
182 fn infer_batch<'a>(&'a self, _call: BatchAttemptCall) -> BoxFuture<'a, BatchAttemptReport> {
185 Box::pin(async {
186 BatchAttemptReport {
187 result: Err((
188 AdapterFailure::Permanent,
189 "backend did not implement declared batch semantics".into(),
190 )),
191 charge: Charge::Unknown,
192 cancel: CancelAck::NotRequested,
193 remote: RemoteEnd::PossiblyContinuing,
194 }
195 })
196 }
197}
198
199pub struct DynamicCall<'a> {
201 pub projection: &'a [u8],
203 pub template: &'a TemplateRef,
206 pub attempt_id: &'a str,
207 pub cancel: CancelSignal,
209 pub cx: &'a CallContext,
210}
211
212#[derive(Debug, Clone, PartialEq)]
215pub struct DynamicReport {
216 pub result: Result<DynamicRawOutput, (AdapterFailure, String)>,
217 pub served: ServedIdentity,
220 pub charge: Charge,
221 pub cancel: CancelAck,
222 pub remote: RemoteEnd,
223}
224
225pub trait DynamicBackend: Send + Sync {
230 fn backend_id(&self) -> &str;
231 fn dynamic_capability(&self) -> Option<&DynamicCapability>;
234 fn cost_bound(&self, projection: &[u8]) -> CostBound;
236 fn ask<'a>(&'a self, call: DynamicCall<'a>) -> BoxFuture<'a, DynamicReport>;
237}
238
239pub trait DynamicEvidenceSink: Send + Sync {
243 fn deliver<'a>(&'a self, outcome: &'a DynamicOutcome) -> BoxFuture<'a, Result<String, String>>;
244}
245
246pub trait Evaluator: Send + Sync {
248 fn evaluate<'a>(&'a self, cx: &'a CallContext) -> BoxFuture<'a, EvalReport>;
249}
250
251pub trait EvidenceSink: Send + Sync {
255 fn deliver<'a>(&'a self, record: &'a RunRecord) -> BoxFuture<'a, Result<String, String>>;
256
257 fn accepts(&self, schema: &str) -> bool {
260 schema == rustev_contract::schema::RUN
261 }
262
263 fn run_schema(&self) -> &'static str {
269 rustev_contract::schema::RUN
270 }
271}
272
273#[derive(Clone, Default)]
277pub struct CancelSignal(Arc<Inner>);
278
279#[derive(Default)]
280struct Inner {
281 raised: AtomicBool,
282 state: Mutex<State>,
283}
284
285#[derive(Default)]
286struct State {
287 wakers: Vec<Waker>,
288 children: Vec<Weak<Inner>>,
289}
290
291impl std::fmt::Debug for CancelSignal {
292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293 f.debug_tuple("CancelSignal")
294 .field(&self.is_raised())
295 .finish()
296 }
297}
298
299fn lock(m: &Mutex<State>) -> std::sync::MutexGuard<'_, State> {
300 m.lock().unwrap_or_else(|e| e.into_inner())
301}
302
303impl Inner {
304 fn raise(&self) {
305 if self.raised.swap(true, Ordering::SeqCst) {
306 return;
307 }
308 let (wakers, children) = {
309 let mut s = lock(&self.state);
310 (
311 std::mem::take(&mut s.wakers),
312 std::mem::take(&mut s.children),
313 )
314 };
315 for w in wakers {
316 w.wake();
317 }
318 for c in children.iter().filter_map(Weak::upgrade) {
319 c.raise();
320 }
321 }
322}
323
324impl CancelSignal {
325 pub fn new() -> Self {
326 Self::default()
327 }
328
329 pub fn raise(&self) {
330 self.0.raise();
331 }
332
333 pub fn is_raised(&self) -> bool {
334 self.0.raised.load(Ordering::SeqCst)
335 }
336
337 pub fn child(&self) -> CancelSignal {
339 let child = CancelSignal::new();
340 {
341 let mut s = lock(&self.0.state);
342 if !self.is_raised() {
343 s.children.retain(|w| w.strong_count() > 0);
344 s.children.push(Arc::downgrade(&child.0));
345 return child;
346 }
347 }
348 child.raise();
349 child
350 }
351
352 pub fn poll_raised(&self, cx: &mut Context<'_>) -> Poll<()> {
354 if self.is_raised() {
355 return Poll::Ready(());
356 }
357 let mut s = lock(&self.0.state);
358 if self.is_raised() {
359 return Poll::Ready(());
360 }
361 if !s.wakers.iter().any(|w| w.will_wake(cx.waker())) {
362 s.wakers.push(cx.waker().clone());
363 }
364 Poll::Pending
365 }
366
367 pub fn registered(&self) -> usize {
370 let s = lock(&self.0.state);
371 s.wakers.len() + s.children.iter().filter(|w| w.strong_count() > 0).count()
372 }
373
374 pub fn raised(&self) -> impl Future<Output = ()> + Send + '_ {
376 std::future::poll_fn(move |cx| self.poll_raised(cx))
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383
384 #[test]
385 fn raising_a_parent_raises_its_children_and_not_the_reverse() {
386 let parent = CancelSignal::new();
387 let a = parent.child();
388 let b = parent.child();
389 b.raise();
390 assert!(b.is_raised() && !a.is_raised() && !parent.is_raised());
391 parent.raise();
392 assert!(a.is_raised());
393 let late = parent.child();
394 assert!(late.is_raised(), "a child of a raised signal starts raised");
395 }
396}
397
398pub trait AcquisitionSource: Send + Sync {
401 fn descriptor(&self) -> &SourceDescriptor;
402 fn cost_bound(&self, request: &AcquisitionRequest) -> CostBound;
403 fn acquire<'a>(&'a self, call: AcquireCall<'a>) -> BoxFuture<'a, AcquireReport>;
404}
405
406pub struct AcquireCall<'a> {
408 pub operation: &'a str,
409 pub request: &'a rustev_contract::acquisition::AcquisitionRequest,
410 pub request_id: &'a rustev_contract::ids::AcquisitionRequestId,
411 pub attempt_id: &'a str,
412 pub cancel: CancelSignal,
413 pub cx: &'a CallContext,
414}
415
416#[derive(Debug, Clone, Copy, PartialEq, Eq)]
417pub enum AcquisitionFailure {
418 Transient,
419 Overloaded,
420 Permanent,
421 Denied,
422}
423
424#[derive(Debug, Clone, PartialEq)]
426pub struct AcquireReport {
427 pub result: Result<Vec<u8>, (AcquisitionFailure, String)>,
428 pub charge: Charge,
429 pub cancel: CancelAck,
430}