Skip to main content

murk_core/
command.rs

1//! Command, command payload, and receipt types for the ingress pipeline.
2
3use crate::error::IngressError;
4use crate::id::{Coord, FieldId, ParameterKey, TickId};
5
6/// A command submitted to the simulation via the ingress pipeline.
7///
8/// Commands are ordered by `priority_class` (lower = higher priority),
9/// then by `source_id` for disambiguation, then by `arrival_seq` as
10/// a final tiebreaker.
11///
12/// # Examples
13///
14/// ```
15/// use murk_core::{Command, CommandPayload, FieldId, TickId, ParameterKey};
16///
17/// // A command that sets a global parameter.
18/// let cmd = Command {
19///     payload: CommandPayload::SetParameter {
20///         key: ParameterKey(0),
21///         value: 2.5,
22///     },
23///     expires_after_tick: TickId(100),
24///     source_id: Some(1),
25///     source_seq: Some(0),
26///     priority_class: 1,
27///     arrival_seq: 0,
28/// };
29///
30/// assert_eq!(cmd.priority_class, 1);
31/// assert_eq!(cmd.expires_after_tick, TickId(100));
32/// ```
33#[derive(Clone, Debug, PartialEq)]
34pub struct Command {
35    /// The operation to perform.
36    pub payload: CommandPayload,
37    /// The command expires if not applied by this tick.
38    pub expires_after_tick: TickId,
39    /// Optional source identifier for deduplication and ordering.
40    pub source_id: Option<u64>,
41    /// Optional per-source sequence number for ordering.
42    pub source_seq: Option<u64>,
43    /// Priority class. Lower values = higher priority.
44    /// 0 = system, 1 = user default.
45    pub priority_class: u8,
46    /// Monotonic arrival sequence number, set by the ingress pipeline.
47    pub arrival_seq: u64,
48}
49
50/// All command payloads.
51///
52/// `WorldEvent` variants affect per-cell state; `GlobalParameter` variants
53/// affect simulation-wide scalar parameters.
54///
55/// # Examples
56///
57/// ```
58/// use murk_core::{CommandPayload, FieldId, ParameterKey};
59///
60/// // Set a single field value at a coordinate.
61/// let coord: murk_core::Coord = vec![3i32, 7].into();
62/// let payload = CommandPayload::SetField {
63///     coord,
64///     field_id: FieldId(0),
65///     value: 42.0,
66/// };
67///
68/// // Batch-set multiple global parameters atomically.
69/// let batch = CommandPayload::SetParameterBatch {
70///     params: vec![(ParameterKey(0), 1.0), (ParameterKey(1), 0.5)],
71/// };
72///
73/// assert!(matches!(payload, CommandPayload::SetField { .. }));
74/// assert!(matches!(batch, CommandPayload::SetParameterBatch { .. }));
75/// ```
76#[derive(Clone, Debug, PartialEq)]
77pub enum CommandPayload {
78    // --- WorldEvent variants ---
79    /// Move an entity to a target coordinate.
80    ///
81    /// Rejected if `entity_id` is unknown or `target_coord` is out of bounds.
82    Move {
83        /// The entity to move.
84        entity_id: u64,
85        /// The destination coordinate.
86        target_coord: Coord,
87    },
88    /// Spawn a new entity at a coordinate with initial field values.
89    Spawn {
90        /// The spawn location.
91        coord: Coord,
92        /// Initial field values for the new entity.
93        field_values: Vec<(FieldId, f32)>,
94    },
95    /// Remove an entity. Associated field values are cleared at the next tick.
96    Despawn {
97        /// The entity to remove.
98        entity_id: u64,
99    },
100    /// Set a single field value at a coordinate. Primarily for `Sparse` fields.
101    SetField {
102        /// The target cell coordinate.
103        coord: Coord,
104        /// The field to modify.
105        field_id: FieldId,
106        /// The new value.
107        value: f32,
108    },
109    /// Extension point for domain-specific commands.
110    Custom {
111        /// User-registered type identifier.
112        type_id: u32,
113        /// Opaque payload data.
114        data: Vec<u8>,
115    },
116
117    // --- GlobalParameter variants ---
118    /// Set a single global parameter. Takes effect at the next tick boundary.
119    SetParameter {
120        /// The parameter to set.
121        key: ParameterKey,
122        /// The new value.
123        value: f64,
124    },
125    /// Batch-set multiple parameters atomically.
126    SetParameterBatch {
127        /// The parameters to set.
128        params: Vec<(ParameterKey, f64)>,
129    },
130}
131
132/// Receipt returned for each command in a submitted batch.
133///
134/// Indicates whether the command was accepted and, if applied,
135/// which tick it was applied in.
136///
137/// # Examples
138///
139/// ```
140/// use murk_core::command::Receipt;
141/// use murk_core::TickId;
142///
143/// let receipt = Receipt {
144///     accepted: true,
145///     applied_tick_id: Some(TickId(5)),
146///     reason_code: None,
147///     command_index: 0,
148/// };
149///
150/// assert!(receipt.accepted);
151/// assert_eq!(receipt.applied_tick_id, Some(TickId(5)));
152/// ```
153#[derive(Clone, Debug, PartialEq, Eq)]
154pub struct Receipt {
155    /// Whether the command was accepted by the ingress pipeline.
156    pub accepted: bool,
157    /// The tick at which the command was applied, if applicable.
158    pub applied_tick_id: Option<TickId>,
159    /// The reason the command was rejected, if applicable.
160    pub reason_code: Option<IngressError>,
161    /// Index of this command within the submitted batch.
162    pub command_index: usize,
163}