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 settle(&self, ticket: SpendTicket, charge: &Charge);
58 fn release(&self, ticket: SpendTicket);
59}
60
61#[derive(Clone, Copy)]
63pub struct LiveSpend<'a> {
64 pub gate: &'a dyn SpendGate,
65 pub bindings: &'a [SpendBinding],
66}
67
68pub(crate) struct HeldSpend<'a> {
69 gate: &'a dyn SpendGate,
70 ticket: Option<SpendTicket>,
71}
72
73impl<'a> HeldSpend<'a> {
74 pub(crate) fn new(gate: &'a dyn SpendGate, ticket: SpendTicket) -> Self {
75 Self {
76 gate,
77 ticket: Some(ticket),
78 }
79 }
80
81 pub(crate) fn release(mut self) {
82 self.gate.release(self.ticket.take().expect("held ticket"));
83 }
84
85 pub(crate) fn settle(mut self, charge: &Charge) {
86 self.gate
87 .settle(self.ticket.take().expect("held ticket"), charge);
88 }
89}
90
91impl Drop for HeldSpend<'_> {
92 fn drop(&mut self) {
93 if let Some(ticket) = self.ticket.take() {
94 self.gate.settle(ticket, &Charge::Unknown);
95 }
96 }
97}