1use std::fmt;
4
5use crate::{operation_error::OperationError, operation_wire::*};
6use sim_kernel::{CapabilityName, ContentId, Datum, Symbol};
7
8pub(crate) const INTENT_TAG: &str = "intent-v1";
9pub(crate) const GRANT_TAG: &str = "grant-v1";
10pub(crate) const ATTEMPT_TAG: &str = "attempt-v1";
11pub(crate) const DISPATCH_TAG: &str = "dispatch-v1";
12pub(crate) const RECEIPT_TAG: &str = "performer-receipt-v1";
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum ReplayPolicy {
17 Idempotent,
19 ExactlyOnce,
21}
22
23impl ReplayPolicy {
24 pub(crate) fn datum(self) -> Datum {
25 Datum::Symbol(Symbol::qualified(
26 "operation",
27 match self {
28 Self::Idempotent => "idempotent",
29 Self::ExactlyOnce => "exactly-once",
30 },
31 ))
32 }
33
34 pub(crate) fn from_datum(datum: &Datum) -> Result<Self, OperationError> {
35 match datum {
36 Datum::Symbol(value) if *value == Symbol::qualified("operation", "idempotent") => {
37 Ok(Self::Idempotent)
38 }
39 Datum::Symbol(value) if *value == Symbol::qualified("operation", "exactly-once") => {
40 Ok(Self::ExactlyOnce)
41 }
42 _ => Err(OperationError::NonCanonical("replay policy")),
43 }
44 }
45}
46
47#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
49pub struct OperationId(pub(crate) ContentId);
50
51pub type OperationIntentId = OperationId;
53
54impl OperationId {
55 pub const fn content_id(&self) -> &ContentId {
57 &self.0
58 }
59}
60
61impl fmt::Display for OperationId {
62 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
63 render_id(&self.0, formatter)
64 }
65}
66
67#[derive(Clone, Debug, PartialEq, Eq)]
69pub struct OperationIntent {
70 pub(crate) id: OperationId,
71 pub(crate) operation: String,
72 pub(crate) target: Datum,
73 pub(crate) intended_result: Datum,
74 pub(crate) replay_policy: ReplayPolicy,
75}
76
77impl OperationIntent {
78 pub fn new(
80 operation: impl Into<String>,
81 target: Datum,
82 intended_result: Datum,
83 replay_policy: ReplayPolicy,
84 ) -> Result<Self, OperationError> {
85 let operation = operation.into();
86 if operation.is_empty() {
87 return Err(OperationError::EmptyOperation);
88 }
89 let datum = intent_datum(&operation, &target, &intended_result, replay_policy);
90 Ok(Self {
91 id: OperationId(content_id(&datum)?),
92 operation,
93 target,
94 intended_result,
95 replay_policy,
96 })
97 }
98
99 pub const fn id(&self) -> &OperationId {
101 &self.id
102 }
103
104 pub fn operation(&self) -> &str {
106 &self.operation
107 }
108
109 pub const fn target(&self) -> &Datum {
111 &self.target
112 }
113
114 pub const fn intended_result(&self) -> &Datum {
116 &self.intended_result
117 }
118
119 pub const fn replay_policy(&self) -> ReplayPolicy {
121 self.replay_policy
122 }
123
124 pub fn canonical_datum(&self) -> Datum {
126 intent_datum(
127 &self.operation,
128 &self.target,
129 &self.intended_result,
130 self.replay_policy,
131 )
132 }
133
134 pub(crate) fn verify(&self) -> Result<(), OperationError> {
135 if content_id(&self.canonical_datum())? != self.id.0 {
136 return Err(OperationError::ContradictoryIntent);
137 }
138 Ok(())
139 }
140
141 pub(crate) fn from_datum(datum: &Datum) -> Result<Self, OperationError> {
142 let fields = node_fields(datum, INTENT_TAG, 4)?;
143 let operation = string_field(fields, "operation")?.to_owned();
144 let target = field(fields, "target")?.clone();
145 let intended_result = field(fields, "intended-result")?.clone();
146 let replay_policy = ReplayPolicy::from_datum(field(fields, "replay-policy")?)?;
147 let value = Self::new(operation, target, intended_result, replay_policy)?;
148 if value.canonical_datum() != *datum {
149 return Err(OperationError::NonCanonical("operation intent"));
150 }
151 Ok(value)
152 }
153}
154
155#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
157pub struct OperationGrantId(pub(crate) ContentId);
158
159impl OperationGrantId {
160 pub const fn content_id(&self) -> &ContentId {
162 &self.0
163 }
164}
165
166#[derive(Clone, Debug, PartialEq, Eq)]
168pub struct OperationGrant {
169 pub(crate) id: OperationGrantId,
170 pub(crate) operation: OperationId,
171 pub(crate) capability: CapabilityName,
172 pub(crate) authority: Datum,
173}
174
175impl OperationGrant {
176 pub fn new(
178 operation: OperationId,
179 capability: CapabilityName,
180 authority: Datum,
181 ) -> Result<Self, OperationError> {
182 let datum = grant_datum(&operation, &capability, &authority);
183 Ok(Self {
184 id: OperationGrantId(content_id(&datum)?),
185 operation,
186 capability,
187 authority,
188 })
189 }
190
191 pub const fn id(&self) -> &OperationGrantId {
193 &self.id
194 }
195
196 pub const fn operation(&self) -> &OperationId {
198 &self.operation
199 }
200
201 pub const fn capability(&self) -> &CapabilityName {
203 &self.capability
204 }
205
206 pub const fn authority(&self) -> &Datum {
208 &self.authority
209 }
210
211 pub fn canonical_datum(&self) -> Datum {
213 grant_datum(&self.operation, &self.capability, &self.authority)
214 }
215
216 pub(crate) fn verify(&self) -> Result<(), OperationError> {
217 if content_id(&self.canonical_datum())? != self.id.0 {
218 return Err(OperationError::NonCanonical("operation grant"));
219 }
220 Ok(())
221 }
222
223 pub(crate) fn from_datum(datum: &Datum) -> Result<Self, OperationError> {
224 let fields = node_fields(datum, GRANT_TAG, 3)?;
225 let operation = OperationId(id_from_datum(field(fields, "operation")?)?);
226 let capability = CapabilityName::new(string_field(fields, "capability")?);
227 let authority = field(fields, "authority")?.clone();
228 let value = Self::new(operation, capability, authority)?;
229 if value.canonical_datum() != *datum {
230 return Err(OperationError::NonCanonical("operation grant"));
231 }
232 Ok(value)
233 }
234}
235
236#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
238pub struct OperationAttemptId(pub(crate) ContentId);
239
240#[derive(Clone, Debug, PartialEq, Eq)]
242pub struct OperationAttempt {
243 pub(crate) id: OperationAttemptId,
244 pub(crate) operation: OperationId,
245 pub(crate) ordinal: u64,
246}
247
248impl OperationAttempt {
249 pub fn new(operation: OperationId, ordinal: u64) -> Result<Self, OperationError> {
251 let datum = attempt_datum(&operation, ordinal);
252 Ok(Self {
253 id: OperationAttemptId(content_id(&datum)?),
254 operation,
255 ordinal,
256 })
257 }
258
259 pub const fn id(&self) -> &OperationAttemptId {
261 &self.id
262 }
263
264 pub const fn operation(&self) -> &OperationId {
266 &self.operation
267 }
268
269 pub const fn ordinal(&self) -> u64 {
271 self.ordinal
272 }
273
274 pub fn canonical_datum(&self) -> Datum {
276 attempt_datum(&self.operation, self.ordinal)
277 }
278
279 pub(crate) fn verify(&self) -> Result<(), OperationError> {
280 if content_id(&self.canonical_datum())? != self.id.0 {
281 return Err(OperationError::NonCanonical("operation attempt"));
282 }
283 Ok(())
284 }
285
286 pub(crate) fn from_datum(datum: &Datum) -> Result<Self, OperationError> {
287 let fields = node_fields(datum, ATTEMPT_TAG, 2)?;
288 let operation = OperationId(id_from_datum(field(fields, "operation")?)?);
289 let ordinal = u64_field(fields, "ordinal")?;
290 let value = Self::new(operation, ordinal)?;
291 if value.canonical_datum() != *datum {
292 return Err(OperationError::NonCanonical("operation attempt"));
293 }
294 Ok(value)
295 }
296}
297
298impl OperationAttemptId {
299 pub const fn content_id(&self) -> &ContentId {
301 &self.0
302 }
303}
304
305#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
307pub struct DispatchId(pub(crate) ContentId);
308
309impl DispatchId {
310 pub const fn content_id(&self) -> &ContentId {
312 &self.0
313 }
314}
315
316#[derive(Clone, Debug, PartialEq, Eq)]
318pub struct OperationDispatch {
319 pub(crate) id: DispatchId,
320 pub(crate) operation: OperationId,
321 pub(crate) grant: OperationGrantId,
322 pub(crate) attempt: OperationAttemptId,
323}
324
325impl OperationDispatch {
326 pub(crate) fn new(
327 operation: OperationId,
328 grant: OperationGrantId,
329 attempt: OperationAttemptId,
330 ) -> Result<Self, OperationError> {
331 let datum = dispatch_datum(&operation, &grant, &attempt);
332 Ok(Self {
333 id: DispatchId(content_id(&datum)?),
334 operation,
335 grant,
336 attempt,
337 })
338 }
339
340 pub const fn id(&self) -> &DispatchId {
342 &self.id
343 }
344
345 pub const fn operation(&self) -> &OperationId {
347 &self.operation
348 }
349
350 pub const fn grant(&self) -> &OperationGrantId {
352 &self.grant
353 }
354
355 pub const fn attempt(&self) -> &OperationAttemptId {
357 &self.attempt
358 }
359
360 pub fn canonical_datum(&self) -> Datum {
362 dispatch_datum(&self.operation, &self.grant, &self.attempt)
363 }
364
365 pub(crate) fn from_datum(datum: &Datum) -> Result<Self, OperationError> {
366 let fields = node_fields(datum, DISPATCH_TAG, 3)?;
367 let operation = OperationId(id_from_datum(field(fields, "operation")?)?);
368 let grant = OperationGrantId(id_from_datum(field(fields, "grant")?)?);
369 let attempt = OperationAttemptId(id_from_datum(field(fields, "attempt")?)?);
370 let value = Self::new(operation, grant, attempt)?;
371 if value.canonical_datum() != *datum {
372 return Err(OperationError::NonCanonical("operation dispatch"));
373 }
374 Ok(value)
375 }
376}
377
378#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
380pub struct PerformerReceiptId(pub(crate) ContentId);
381
382impl PerformerReceiptId {
383 pub const fn content_id(&self) -> &ContentId {
385 &self.0
386 }
387}
388
389#[derive(Clone, Debug, PartialEq, Eq)]
391pub struct PerformerReceipt {
392 pub(crate) id: PerformerReceiptId,
393 pub(crate) dispatch: DispatchId,
394 pub(crate) raw: Datum,
395}
396
397impl PerformerReceipt {
398 pub(crate) fn new(dispatch: DispatchId, raw: Datum) -> Result<Self, OperationError> {
399 let datum = receipt_datum(&dispatch, &raw);
400 Ok(Self {
401 id: PerformerReceiptId(content_id(&datum)?),
402 dispatch,
403 raw,
404 })
405 }
406
407 pub const fn id(&self) -> &PerformerReceiptId {
409 &self.id
410 }
411
412 pub const fn dispatch(&self) -> &DispatchId {
414 &self.dispatch
415 }
416
417 pub const fn raw(&self) -> &Datum {
419 &self.raw
420 }
421
422 pub fn canonical_datum(&self) -> Datum {
424 receipt_datum(&self.dispatch, &self.raw)
425 }
426
427 pub(crate) fn from_datum(datum: &Datum) -> Result<Self, OperationError> {
428 let fields = node_fields(datum, RECEIPT_TAG, 2)?;
429 let dispatch = DispatchId(id_from_datum(field(fields, "dispatch")?)?);
430 let raw = field(fields, "raw")?.clone();
431 let value = Self::new(dispatch, raw)?;
432 if value.canonical_datum() != *datum {
433 return Err(OperationError::NonCanonical("performer receipt"));
434 }
435 Ok(value)
436 }
437}
438
439#[derive(Clone, Debug, PartialEq, Eq)]
441pub enum DurableOperationState {
442 IntentPersisted {
444 intent: OperationIntentId,
446 },
447 Dispatched {
449 intent: OperationIntentId,
451 dispatch: DispatchId,
453 },
454 ReceiptPersisted {
456 dispatch: DispatchId,
458 receipt: PerformerReceiptId,
460 },
461}
462
463#[derive(Clone, Debug, PartialEq, Eq)]
465pub enum PerformerResponse {
466 Receipt(Datum),
468 AcknowledgementMissing,
470}
471
472pub trait OperationPerformer {
474 fn perform(&mut self, dispatch: &OperationDispatch) -> PerformerResponse;
476}