nixfleet_state_machine/
wire_conversions.rs

1//! Bidirectional conversions between the wire-format types in
2//! `nixfleet-proto::agent_event` and the state-machine's effect / event
3//! types. Both directions live in this crate (the state-machine) by the
4//! orphan rule: every conversion has a state-machine-local type on at
5//! least one side. Keeps `nixfleet-proto` free of state-machine
6//! awareness (proto is the leaf crate) and CP free of duplicate wire
7//! definitions (the architect's d013 lift per RFC-0004 §2).
8
9use nixfleet_proto::agent_event::{
10    AgentEvent, ProbeModeWire, ProbeStatusWire, ProbeSubResultWire, ProbeTopologyEntryWire,
11};
12
13use crate::effect::OutboundAgentEvent;
14use crate::event::{Event, ProbeTopologyEntry};
15use crate::state::{ProbeMode, ProbeStatus, ProbeSubResult};
16
17// ─────────────────────────────────────────────────────────────────────
18// Wire enum -> state-machine enum (inbound: CP receives an AgentEvent,
19// projects it onto a Remote* reducer event).
20// ─────────────────────────────────────────────────────────────────────
21
22impl From<ProbeStatusWire> for ProbeStatus {
23    fn from(w: ProbeStatusWire) -> Self {
24        match w {
25            ProbeStatusWire::Pass => ProbeStatus::Pass,
26            ProbeStatusWire::Fail => ProbeStatus::Fail,
27        }
28    }
29}
30
31impl From<ProbeStatus> for ProbeStatusWire {
32    fn from(s: ProbeStatus) -> Self {
33        match s {
34            ProbeStatus::Pass => ProbeStatusWire::Pass,
35            ProbeStatus::Fail => ProbeStatusWire::Fail,
36        }
37    }
38}
39
40impl From<ProbeModeWire> for ProbeMode {
41    fn from(w: ProbeModeWire) -> Self {
42        match w {
43            ProbeModeWire::Enforce => ProbeMode::Enforce,
44            ProbeModeWire::Observe => ProbeMode::Observe,
45            ProbeModeWire::Disabled => ProbeMode::Disabled,
46        }
47    }
48}
49
50impl From<ProbeMode> for ProbeModeWire {
51    fn from(m: ProbeMode) -> Self {
52        match m {
53            ProbeMode::Enforce => ProbeModeWire::Enforce,
54            ProbeMode::Observe => ProbeModeWire::Observe,
55            ProbeMode::Disabled => ProbeModeWire::Disabled,
56        }
57    }
58}
59
60impl From<ProbeTopologyEntryWire> for ProbeTopologyEntry {
61    fn from(w: ProbeTopologyEntryWire) -> Self {
62        ProbeTopologyEntry {
63            probe_name: w.probe_name,
64            kind: w.kind,
65            mode: w.mode.into(),
66        }
67    }
68}
69
70impl From<ProbeTopologyEntry> for ProbeTopologyEntryWire {
71    fn from(e: ProbeTopologyEntry) -> Self {
72        ProbeTopologyEntryWire {
73            probe_name: e.probe_name,
74            kind: e.kind,
75            mode: e.mode.into(),
76        }
77    }
78}
79
80impl From<ProbeSubResultWire> for ProbeSubResult {
81    fn from(w: ProbeSubResultWire) -> Self {
82        ProbeSubResult {
83            control_id: w.control_id,
84            status: w.status.into(),
85            framework: w.framework,
86            article: w.article,
87            effective_mode: w.effective_mode.into(),
88            override_reason: w.override_reason,
89        }
90    }
91}
92
93impl From<ProbeSubResult> for ProbeSubResultWire {
94    fn from(s: ProbeSubResult) -> Self {
95        ProbeSubResultWire {
96            control_id: s.control_id,
97            status: s.status.into(),
98            framework: s.framework,
99            article: s.article,
100            effective_mode: s.effective_mode.into(),
101            override_reason: s.override_reason,
102        }
103    }
104}
105
106// ─────────────────────────────────────────────────────────────────────
107// Inbound: CP receives wire AgentEvent, projects to a Remote* reducer
108// Event. Replaces the local `AgentEvent::into_remote_event` impl that
109// previously lived in CP's `routes/events.rs`.
110// ─────────────────────────────────────────────────────────────────────
111
112impl From<AgentEvent> for Event {
113    fn from(e: AgentEvent) -> Self {
114        match e {
115            AgentEvent::DispatchAck {
116                current_closure_at_dispatch,
117                received_at,
118                seq,
119            } => Event::RemoteDispatchAck {
120                current_closure_at_dispatch,
121                received_at,
122                seq,
123            },
124            AgentEvent::ActivationStarted {
125                started_at,
126                switch_method,
127                seq,
128            } => Event::RemoteActivationStarted {
129                started_at,
130                switch_method,
131                seq,
132            },
133            AgentEvent::ActivationCompleted {
134                observed_current_closure,
135                exit_code,
136                completed_at,
137                seq,
138            } => Event::RemoteActivationCompleted {
139                observed_current_closure,
140                exit_code,
141                completed_at,
142                seq,
143            },
144            AgentEvent::ActivationFailed {
145                exit_code,
146                stderr_tail,
147                failed_at,
148                seq,
149            } => Event::RemoteActivationFailed {
150                exit_code,
151                stderr_tail,
152                failed_at,
153                seq,
154            },
155            AgentEvent::ActivationDeferred {
156                component,
157                deferred_at,
158                seq,
159            } => Event::RemoteActivationDeferred {
160                component,
161                deferred_at,
162                seq,
163            },
164            AgentEvent::ProbeTopologyDeclared {
165                probes,
166                declared_at,
167                seq,
168            } => Event::RemoteProbeTopologyDeclared {
169                probes: probes.into_iter().map(Into::into).collect(),
170                declared_at,
171                seq,
172            },
173            AgentEvent::ProbeObservedFirst {
174                probe_name,
175                mode,
176                observed_at,
177                seq,
178            } => Event::RemoteProbeObservedFirst {
179                probe_name,
180                mode: mode.into(),
181                observed_at,
182                seq,
183            },
184            AgentEvent::ProbeResult {
185                probe_name,
186                mode,
187                status,
188                observed_at,
189                failure_reason,
190                sub_results,
191                seq,
192            } => Event::RemoteProbeResult {
193                probe_name,
194                mode: mode.into(),
195                status: status.into(),
196                observed_at,
197                failure_reason,
198                sub_results: sub_results
199                    .map(|v| v.into_iter().map(Into::into).collect()),
200                seq,
201            },
202            AgentEvent::ProbeFailureFirst {
203                probe_name,
204                mode,
205                first_failed_at,
206                seq,
207            } => Event::RemoteProbeFailureFirst {
208                probe_name,
209                mode: mode.into(),
210                first_failed_at,
211                seq,
212            },
213            AgentEvent::Failed {
214                failed_at,
215                sustained_duration_secs,
216                failing_probes,
217                policy_applied,
218                seq,
219            } => Event::RemoteFailed {
220                failed_at,
221                sustained_duration_secs,
222                failing_probes,
223                policy_applied: policy_applied.into(),
224                seq,
225            },
226            AgentEvent::RollbackComplete {
227                reverted_to_closure,
228                exit_code,
229                completed_at,
230                seq,
231            } => Event::RemoteRollbackComplete {
232                reverted_to_closure,
233                exit_code,
234                completed_at,
235                seq,
236            },
237            AgentEvent::Converged {
238                converged_at,
239                current_closure,
240                seq,
241            } => Event::RemoteConverged {
242                converged_at,
243                current_closure,
244                seq,
245            },
246        }
247    }
248}
249
250// ─────────────────────────────────────────────────────────────────────
251// Outbound: agent applier converts a state-machine OutboundAgentEvent
252// into the wire AgentEvent before queuing it. Replaces the agent's
253// hand-built `outbound_event_to_json` (which produced serde_json::Value
254// of the same shape but with no compile-time typing).
255// ─────────────────────────────────────────────────────────────────────
256
257impl From<OutboundAgentEvent> for AgentEvent {
258    fn from(e: OutboundAgentEvent) -> Self {
259        match e {
260            OutboundAgentEvent::DispatchAck {
261                current_closure_at_dispatch,
262                received_at,
263                seq,
264            } => AgentEvent::DispatchAck {
265                current_closure_at_dispatch,
266                received_at,
267                seq,
268            },
269            OutboundAgentEvent::ActivationStarted {
270                started_at,
271                switch_method,
272                seq,
273            } => AgentEvent::ActivationStarted {
274                started_at,
275                switch_method,
276                seq,
277            },
278            OutboundAgentEvent::ActivationCompleted {
279                observed_current_closure,
280                exit_code,
281                completed_at,
282                seq,
283            } => AgentEvent::ActivationCompleted {
284                observed_current_closure,
285                exit_code,
286                completed_at,
287                seq,
288            },
289            OutboundAgentEvent::ActivationFailed {
290                exit_code,
291                stderr_tail,
292                failed_at,
293                seq,
294            } => AgentEvent::ActivationFailed {
295                exit_code,
296                stderr_tail,
297                failed_at,
298                seq,
299            },
300            OutboundAgentEvent::ActivationDeferred {
301                component,
302                deferred_at,
303                seq,
304            } => AgentEvent::ActivationDeferred {
305                component,
306                deferred_at,
307                seq,
308            },
309            OutboundAgentEvent::ProbeTopologyDeclared {
310                probes,
311                declared_at,
312                seq,
313            } => AgentEvent::ProbeTopologyDeclared {
314                probes: probes.into_iter().map(Into::into).collect(),
315                declared_at,
316                seq,
317            },
318            OutboundAgentEvent::ProbeObservedFirst {
319                probe_name,
320                mode,
321                observed_at,
322                seq,
323            } => AgentEvent::ProbeObservedFirst {
324                probe_name,
325                mode: mode.into(),
326                observed_at,
327                seq,
328            },
329            OutboundAgentEvent::ProbeResult {
330                probe_name,
331                mode,
332                status,
333                observed_at,
334                failure_reason,
335                sub_results,
336                seq,
337            } => AgentEvent::ProbeResult {
338                probe_name,
339                mode: mode.into(),
340                status: status.into(),
341                observed_at,
342                failure_reason,
343                sub_results: sub_results.map(|v| v.into_iter().map(Into::into).collect()),
344                seq,
345            },
346            OutboundAgentEvent::ProbeFailureFirst {
347                probe_name,
348                mode,
349                first_failed_at,
350                seq,
351            } => AgentEvent::ProbeFailureFirst {
352                probe_name,
353                mode: mode.into(),
354                first_failed_at,
355                seq,
356            },
357            OutboundAgentEvent::Failed {
358                failed_at,
359                sustained_duration_secs,
360                failing_probes,
361                policy_applied,
362                seq,
363            } => AgentEvent::Failed {
364                failed_at,
365                sustained_duration_secs,
366                failing_probes,
367                policy_applied: policy_applied.into(),
368                seq,
369            },
370            OutboundAgentEvent::RollbackComplete {
371                reverted_to_closure,
372                exit_code,
373                completed_at,
374                seq,
375            } => AgentEvent::RollbackComplete {
376                reverted_to_closure,
377                exit_code,
378                completed_at,
379                seq,
380            },
381            OutboundAgentEvent::Converged {
382                converged_at,
383                current_closure,
384                seq,
385            } => AgentEvent::Converged {
386                converged_at,
387                current_closure,
388                seq,
389            },
390        }
391    }
392}