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 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}