1use std::any::Any;
4
5use rustev_contract::{
6 definition::Operation,
7 ids::ArtifactId,
8 run::{Charge, CostBound},
9};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum SpendUnit {
14 NanoUsd,
15 None,
16}
17
18#[derive(Debug, Clone)]
20pub struct SpendBinding {
21 pub backend_id: String,
22 pub artifact: ArtifactId,
23 pub cost_unit: SpendUnit,
24}
25
26#[derive(Debug, Clone)]
27pub struct SpendAttempt {
28 pub decision_id: String,
29 pub attempt_id: String,
30 pub backend_id: String,
31 pub target: u32,
32 pub artifact: ArtifactId,
33 pub operation: Operation,
34 pub bound: CostBound,
35 pub cost_unit: SpendUnit,
36}
37
38pub type SpendTicket = Box<dyn Any + Send>;
41
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct SpendRefusal {
44 pub code: String,
45}
46
47impl SpendRefusal {
48 pub fn new(code: impl Into<String>) -> Self {
49 Self { code: code.into() }
50 }
51}
52
53pub trait SpendGate: Send + Sync {
56 fn reserve(&self, attempt: &SpendAttempt) -> Result<SpendTicket, SpendRefusal>;
57 fn reserve_acquisition(
59 &self,
60 _attempt: &AcquisitionSpendAttempt,
61 ) -> Result<SpendTicket, SpendRefusal> {
62 Err(SpendRefusal::new("acquisition-unsupported"))
63 }
64 fn settle(&self, ticket: SpendTicket, charge: &Charge);
65 fn release(&self, ticket: SpendTicket);
66}
67
68#[derive(Clone, Copy)]
70pub struct LiveSpend<'a> {
71 pub gate: &'a dyn SpendGate,
72 pub bindings: &'a [SpendBinding],
73}
74
75#[derive(Clone, Copy, Default)]
77pub struct LiveSources<'a> {
78 pub acquisitions: &'a [LiveAcquisition],
79 pub pages: &'a [rustev_contract::acquisition::AcquisitionPage],
80}
81
82#[derive(Debug, Clone)]
84pub struct LiveAcquisition {
85 pub source: rustev_contract::ids::SourceId,
86 pub posture: AcquisitionPosture,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum AcquisitionPosture {
91 Captured,
92 Live,
93}
94
95#[derive(Debug, Clone)]
97pub struct AcquisitionSpendAttempt {
98 pub decision_id: String,
99 pub attempt_id: String,
100 pub source: rustev_contract::ids::SourceId,
101 pub operation: String,
102 pub request: rustev_contract::ids::AcquisitionRequestId,
103 pub bound: CostBound,
104}
105
106pub(crate) struct HeldSpend<'a> {
107 gate: &'a dyn SpendGate,
108 ticket: Option<SpendTicket>,
109}
110
111impl<'a> HeldSpend<'a> {
112 pub(crate) fn new(gate: &'a dyn SpendGate, ticket: SpendTicket) -> Self {
113 Self {
114 gate,
115 ticket: Some(ticket),
116 }
117 }
118
119 pub(crate) fn release(mut self) {
120 self.gate.release(self.ticket.take().expect("held ticket"));
121 }
122
123 pub(crate) fn settle(mut self, charge: &Charge) {
124 self.gate
125 .settle(self.ticket.take().expect("held ticket"), charge);
126 }
127}
128
129impl Drop for HeldSpend<'_> {
130 fn drop(&mut self) {
131 if let Some(ticket) = self.ticket.take() {
132 self.gate.settle(ticket, &Charge::Unknown);
133 }
134 }
135}