Skip to main content

canwu_sim/
boundary.rs

1use crate::{
2    CanwuError, DomainRecordChange, DomainRecordMutation, RandomStreamKey, SimulationView,
3    StateKey, StateVisibility, SystemCadence,
4};
5use canwu_core::{
6    BoundaryId, CommandAttemptId, CommandId, EntityRef, EventId, IngressId, RandomDrawId,
7};
8use canwu_time::{SimDuration, SimTime};
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11
12#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
13pub struct ReservationPoolKey {
14    pub state: StateKey,
15    pub entity: EntityRef,
16    pub resource: String,
17}
18
19impl ReservationPoolKey {
20    #[must_use]
21    pub fn new(state: StateKey, entity: EntityRef, resource: impl Into<String>) -> Self {
22        Self {
23            state,
24            entity,
25            resource: resource.into(),
26        }
27    }
28}
29
30#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
31pub struct ReservationRef {
32    pub plugin: String,
33    pub system: String,
34    pub request: String,
35}
36
37impl ReservationRef {
38    #[must_use]
39    pub fn new(
40        plugin: impl Into<String>,
41        system: impl Into<String>,
42        request: impl Into<String>,
43    ) -> Self {
44        Self {
45            plugin: plugin.into(),
46            system: system.into(),
47            request: request.into(),
48        }
49    }
50}
51
52#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
53pub struct ReservationOffer {
54    pub pool: ReservationPoolKey,
55    pub capacity: u64,
56}
57
58#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
59pub struct ReservationRequest {
60    pub request: String,
61    pub pool: ReservationPoolKey,
62    pub quantity: u64,
63    pub priority: i32,
64    pub tie_break: String,
65}
66
67#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
68pub struct ReservationOfferRecord {
69    pub plugin: String,
70    pub system: String,
71    pub offer: ReservationOffer,
72}
73
74#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
75pub struct ReservationRequestRecord {
76    pub reservation: ReservationRef,
77    pub request: ReservationRequest,
78}
79
80#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
81#[serde(rename_all = "snake_case")]
82pub enum ReservationDisposition {
83    Fulfilled,
84    Partial,
85    Rejected,
86}
87
88#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
89pub struct ReservationAllocation {
90    pub reservation: ReservationRef,
91    pub pool: ReservationPoolKey,
92    pub requested: u64,
93    pub granted: u64,
94    pub remaining_after: u64,
95    pub disposition: ReservationDisposition,
96}
97
98#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
99#[serde(tag = "type", rename_all = "snake_case")]
100pub enum BoundaryDirective {
101    SetComponent {
102        state: StateKey,
103        entity: EntityRef,
104        component: String,
105        value: Value,
106        summary: String,
107    },
108    MutateRecord {
109        mutation: DomainRecordMutation,
110        summary: String,
111    },
112    Emit {
113        event_type: String,
114        summary: String,
115        affected: Vec<EntityRef>,
116    },
117    ScheduleIngress {
118        after: SimDuration,
119        packet_type: String,
120        priority: i32,
121        payload: Value,
122        affected: Vec<EntityRef>,
123    },
124}
125
126#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
127pub struct BoundaryProposal {
128    pub offers: Vec<ReservationOffer>,
129    pub requests: Vec<ReservationRequest>,
130    pub directives: Vec<BoundaryDirective>,
131}
132
133#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
134pub struct BoundarySystemContract {
135    pub name: String,
136    pub phase: crate::BoundaryPhase,
137    pub cadence: SystemCadence,
138    pub reads: Vec<StateKey>,
139    pub writes: Vec<StateKey>,
140    pub emits: Vec<String>,
141    pub reservation_offers: Vec<StateKey>,
142    pub reservation_requests: Vec<StateKey>,
143    pub reservation_reads: Vec<ReservationRef>,
144    #[serde(default)]
145    pub random_streams: Vec<RandomStreamKey>,
146    pub visibility: StateVisibility,
147}
148
149impl BoundarySystemContract {
150    #[must_use]
151    pub fn new(
152        name: impl Into<String>,
153        phase: crate::BoundaryPhase,
154        cadence: SystemCadence,
155    ) -> Self {
156        Self {
157            name: name.into(),
158            phase,
159            cadence,
160            reads: Vec::new(),
161            writes: Vec::new(),
162            emits: Vec::new(),
163            reservation_offers: Vec::new(),
164            reservation_requests: Vec::new(),
165            reservation_reads: Vec::new(),
166            random_streams: Vec::new(),
167            visibility: StateVisibility::NextBoundary,
168        }
169    }
170}
171
172#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
173pub struct BoundaryContext {
174    pub boundary_id: BoundaryId,
175    pub at: SimTime,
176    pub phase: crate::BoundaryPhase,
177    pub plugin: String,
178    pub system: String,
179    pub admitted_attempts: Vec<CommandAttemptId>,
180    pub admitted_commands: Vec<CommandId>,
181    pub admitted_ingress: Vec<IngressId>,
182    pub admitted_events: Vec<EventId>,
183    pub emitted_events: Vec<EventId>,
184}
185
186pub type BoundarySystemHandler =
187    fn(&SimulationView<'_>, &BoundaryContext) -> Result<BoundaryProposal, CanwuError>;
188
189#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
190pub struct BoundaryRequest {
191    pub at: SimTime,
192    pub cadences: Vec<SystemCadence>,
193}
194
195impl BoundaryRequest {
196    #[must_use]
197    pub const fn at(at: SimTime) -> Self {
198        Self {
199            at,
200            cadences: Vec::new(),
201        }
202    }
203
204    #[must_use]
205    pub fn with_cadence(mut self, cadence: SystemCadence) -> Self {
206        self.cadences.push(cadence);
207        self
208    }
209}
210
211#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
212pub struct BoundaryChange {
213    pub plugin: String,
214    pub system: String,
215    pub state: StateKey,
216    pub entity: EntityRef,
217    pub component: String,
218    pub previous: Option<Value>,
219    pub value: Value,
220    pub visibility: StateVisibility,
221    pub summary: String,
222}
223
224#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
225#[serde(tag = "kind", rename_all = "snake_case")]
226pub enum BoundaryEmissionKind {
227    Change { change_index: u64 },
228    RecordChange { change_index: u64 },
229    Explicit,
230}
231
232#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
233pub struct BoundaryEmission {
234    pub plugin: String,
235    pub system: String,
236    pub event: EventId,
237    pub kind: BoundaryEmissionKind,
238}
239
240#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
241pub struct BoundaryIngressGeneration {
242    pub ingress: IngressId,
243    pub plugin: String,
244    pub system: String,
245    pub phase: crate::BoundaryPhase,
246    pub visibility: StateVisibility,
247}
248
249#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
250pub struct BoundaryRecord {
251    pub id: BoundaryId,
252    pub at: SimTime,
253    pub correlation_id: u64,
254    pub cadences: Vec<SystemCadence>,
255    #[serde(default, skip_serializing_if = "Vec::is_empty")]
256    pub admitted_attempts: Vec<CommandAttemptId>,
257    pub admitted_commands: Vec<CommandId>,
258    #[serde(default, skip_serializing_if = "Vec::is_empty")]
259    pub admitted_ingress: Vec<IngressId>,
260    #[serde(default, skip_serializing_if = "Vec::is_empty")]
261    pub generated_ingress: Vec<BoundaryIngressGeneration>,
262    pub admitted_events: Vec<EventId>,
263    pub reservation_offers: Vec<ReservationOfferRecord>,
264    pub reservation_requests: Vec<ReservationRequestRecord>,
265    pub allocations: Vec<ReservationAllocation>,
266    #[serde(default)]
267    pub random_draws: Vec<RandomDrawId>,
268    pub changes: Vec<BoundaryChange>,
269    #[serde(default, skip_serializing_if = "Vec::is_empty")]
270    pub record_changes: Vec<DomainRecordChange>,
271    pub emissions: Vec<BoundaryEmission>,
272    #[serde(default)]
273    /// Untagged legacy full-state hash or a `v1:` incremental state commitment.
274    pub state_hash: Option<String>,
275    #[serde(default)]
276    pub previous_hash: String,
277    #[serde(default)]
278    pub hash: String,
279}
280
281#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
282pub struct BoundaryReceipt {
283    pub boundary_id: BoundaryId,
284    pub settled_at: SimTime,
285    pub emitted_events: Vec<EventId>,
286    pub generated_ingress: Vec<IngressId>,
287    pub random_draws: Vec<RandomDrawId>,
288    pub boundary_hash: String,
289    pub change_count: usize,
290    pub record_change_count: usize,
291    pub allocations: Vec<ReservationAllocation>,
292}