rust_supervisor/control/outcome.rs
1//! Serializable child control result types used for generation fencing.
2//!
3//! These placeholder names align with the naming contract and future event payloads:
4//! ChildRestartFenceEntered, ChildRestartFenceAbortRequested, ChildRestartFenceReleased,
5//! ChildRestartConflict, ChildAttemptStaleReport.
6
7use crate::child_runner::run_exit::TaskExit;
8use crate::id::types::{ChildId, ChildStartCount, Generation, SupervisorPath};
9use crate::readiness::signal::ReadinessState;
10use crate::runtime::admission::AdmissionConflict;
11use serde::{Deserialize, Serialize};
12use std::time::Duration;
13use uuid::Uuid;
14
15/// Runtime phase for a child attempt.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(rename_all = "snake_case")]
18pub enum ChildAttemptStatus {
19 /// The child attempt is starting.
20 Starting,
21 /// The child attempt is running.
22 Running,
23 /// The child attempt reported readiness.
24 Ready,
25 /// The child attempt is cancelling.
26 Cancelling,
27 /// The child attempt has stopped.
28 Stopped,
29}
30
31/// Control operation requested for a child runtime state record.
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
33#[serde(rename_all = "snake_case")]
34pub enum ChildControlOperation {
35 /// The child runtime state remains active.
36 Active,
37 /// The child runtime state is paused.
38 Paused,
39 /// The child runtime state is quarantined.
40 Quarantined,
41 /// The child runtime state is waiting for removal or already removed.
42 Removed,
43}
44
45/// Stop progress for child control commands.
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
47#[serde(rename_all = "snake_case")]
48pub enum ChildStopState {
49 /// No stop action is in progress.
50 Idle,
51 /// The child currently has no active attempt.
52 NoActiveAttempt,
53 /// Cancellation was delivered to the child.
54 CancelDelivered,
55 /// The child completed stopping.
56 Completed,
57 /// The child failed to stop.
58 Failed,
59}
60
61/// Failure phase for a child control command.
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
63#[serde(rename_all = "snake_case")]
64pub enum ChildControlFailurePhase {
65 /// Waiting for child completion failed.
66 WaitCompletion,
67}
68
69/// Structured child control failure.
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
71pub struct ChildControlFailure {
72 /// Phase where the failure occurred.
73 pub phase: ChildControlFailurePhase,
74 /// Human-readable failure reason.
75 pub reason: String,
76 /// Whether callers can retry to recover.
77 pub recoverable: bool,
78}
79
80impl ChildControlFailure {
81 /// Creates a child control failure.
82 ///
83 /// # Arguments
84 ///
85 /// - `phase`: Phase where the failure occurred.
86 /// - `reason`: Human-readable failure reason.
87 /// - `recoverable`: Whether callers can retry to recover.
88 ///
89 /// # Returns
90 ///
91 /// Returns a [`ChildControlFailure`] value.
92 pub fn new(
93 phase: ChildControlFailurePhase,
94 reason: impl Into<String>,
95 recoverable: bool,
96 ) -> Self {
97 Self {
98 phase,
99 reason: reason.into(),
100 recoverable,
101 }
102 }
103}
104
105/// Generation fencing phase: whether a new attempt may start at a restart boundary for one child.
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
107#[serde(rename_all = "snake_case")]
108pub enum GenerationFencePhase {
109 /// No fence wait, or the active attempt runs on the normal path.
110 #[default]
111 Open,
112 /// Restart accepted: cancellation was delivered to the old attempt; waiting for it to exit.
113 WaitingForOldStop,
114 /// Old attempt exceeded the graceful stop window; runtime requested abort.
115 AbortingOld,
116 /// Old attempt confirmed finished; allowed to start the new instance for the target generation.
117 ReadyToStart,
118 /// Record removed or supervisor tree in a shutdown window; must not proceed with restart start.
119 Closed,
120}
121
122/// Discrete outcome for one restart command at the generation fence from the control plane view.
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
124#[serde(rename_all = "snake_case")]
125pub enum GenerationFenceDecision {
126 /// No active attempt; target generation started immediately.
127 StartedImmediately,
128 /// Active attempt still present; restart queued to wait for the old stop.
129 QueuedAfterStop,
130 /// A pending restart already exists; this duplicate command was merged.
131 AlreadyPending,
132 /// Supervisor tree is shutting down; restart is not allowed.
133 BlockedByShutdown,
134 /// Request rejected; set [`GenerationFenceOutcome::conflict`] with the structured reason.
135 Rejected,
136}
137
138/// Label for how the runtime treats a late exit report from an old generation.
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
140#[serde(rename_all = "snake_case")]
141pub enum StaleReportHandling {
142 /// Do not change authoritative state; ignore the stale fact.
143 IgnoredForState,
144 /// Recorded for audit so operators can review it later.
145 RecordedForAudit,
146 /// Counted in a low-cardinality metrics bucket.
147 CountedForMetrics,
148}
149
150/// Minimal stale report payload carried with a control command for diagnostics and dashboard projection.
151#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
152pub struct StaleAttemptReport {
153 /// Stable identifier of the child this report belongs to.
154 pub child_id: ChildId,
155 /// Generation attached to the report that is now stale.
156 pub reported_generation: Generation,
157 /// Attempt index attached to the report that is now stale.
158 pub reported_attempt: ChildStartCount,
159 /// Active generation in the record when the report was classified as stale.
160 pub current_generation: Option<Generation>,
161 /// Active attempt in the record when the report was classified as stale.
162 pub current_attempt: Option<ChildStartCount>,
163 /// Exit shape for the old attempt; matches contract `ExitKind`.
164 pub exit_kind: TaskExit,
165 /// Branch the runtime chose when handling this stale report.
166 pub handled_as: StaleReportHandling,
167 /// Unix epoch nanoseconds when the report was classified as stale.
168 pub observed_at_unix_nanos: u128,
169}
170
171impl StaleAttemptReport {
172 /// Builds a stale attempt report record.
173 ///
174 /// # Arguments
175 ///
176 /// - `child_id`: Stable child identifier for this report.
177 /// - `reported_generation`: Stale generation carried from the report.
178 /// - `reported_attempt`: Stale attempt index carried from the report.
179 /// - `current_generation`: Active generation when classified as stale, or `None`.
180 /// - `current_attempt`: Active attempt when classified as stale, or `None`.
181 /// - `exit_kind`: Exit shape for the old attempt; matches contract `ExitKind`.
182 /// - `handled_as`: How the runtime handled this stale report.
183 /// - `observed_at_unix_nanos`: Unix epoch nanoseconds when the report was classified as stale.
184 ///
185 /// # Returns
186 ///
187 /// Returns an owned [`StaleAttemptReport`].
188 ///
189 /// # Examples
190 ///
191 /// ```
192 /// use rust_supervisor::control::outcome::{StaleAttemptReport, StaleReportHandling};
193 /// use rust_supervisor::child_runner::run_exit::TaskExit;
194 /// let report = StaleAttemptReport::new(
195 /// rust_supervisor::id::types::ChildId::new("worker"),
196 /// rust_supervisor::id::types::Generation::initial(),
197 /// rust_supervisor::id::types::ChildStartCount::first(),
198 /// None,
199 /// None,
200 /// TaskExit::Succeeded,
201 /// StaleReportHandling::IgnoredForState,
202 /// 0,
203 /// );
204 /// assert_eq!(report.handled_as, StaleReportHandling::IgnoredForState);
205 /// ```
206 #[allow(clippy::too_many_arguments)]
207 pub fn new(
208 child_id: ChildId,
209 reported_generation: Generation,
210 reported_attempt: ChildStartCount,
211 current_generation: Option<Generation>,
212 current_attempt: Option<ChildStartCount>,
213 exit_kind: TaskExit,
214 handled_as: StaleReportHandling,
215 observed_at_unix_nanos: u128,
216 ) -> Self {
217 Self {
218 child_id,
219 reported_generation,
220 reported_attempt,
221 current_generation,
222 current_attempt,
223 exit_kind,
224 handled_as,
225 observed_at_unix_nanos,
226 }
227 }
228}
229
230/// Accepted but incomplete restart request; pins the old triple until the old attempt leaves.
231///
232/// `command_id` stores the same UUID bytes as [`crate::control::command::CommandMeta`] to avoid a
233/// module cycle between `command` and `outcome`.
234#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
235pub struct PendingRestart {
236 /// Restart command UUID bound to this request; matches audit `command_id`.
237 pub command_id: Uuid,
238 /// Human-readable restart initiator string.
239 pub requested_by: String,
240 /// Human-readable restart reason string.
241 pub reason: String,
242 /// Old generation that must be pinned when the restart is accepted.
243 pub old_generation: Generation,
244 /// Old attempt index that must be pinned when the restart is accepted.
245 pub old_attempt: ChildStartCount,
246 /// Target generation to start after the old attempt exits.
247 pub target_generation: Generation,
248 /// When the runtime accepted the request, in Unix epoch nanoseconds.
249 pub requested_at_unix_nanos: u128,
250 /// Graceful stop deadline for the old attempt after cancellation, in Unix epoch nanoseconds.
251 pub stop_deadline_at_unix_nanos: u128,
252 /// Whether the runtime has requested abort for the old attempt.
253 pub abort_requested: bool,
254 /// Count of duplicate restart requests merged into this pending request; must not bump generation allocation on merge.
255 pub duplicate_request_count: u32,
256}
257
258impl PendingRestart {
259 /// Creates a pending restart record.
260 ///
261 /// # Arguments
262 ///
263 /// Same meaning as the struct fields documented above.
264 ///
265 /// # Returns
266 ///
267 /// Returns an owned [`PendingRestart`].
268 #[allow(clippy::too_many_arguments)]
269 pub fn new(
270 command_id: Uuid,
271 requested_by: impl Into<String>,
272 reason: impl Into<String>,
273 old_generation: Generation,
274 old_attempt: ChildStartCount,
275 target_generation: Generation,
276 requested_at_unix_nanos: u128,
277 stop_deadline_at_unix_nanos: u128,
278 abort_requested: bool,
279 duplicate_request_count: u32,
280 ) -> Self {
281 Self {
282 command_id,
283 requested_by: requested_by.into(),
284 reason: reason.into(),
285 old_generation,
286 old_attempt,
287 target_generation,
288 requested_at_unix_nanos,
289 stop_deadline_at_unix_nanos,
290 abort_requested,
291 duplicate_request_count,
292 }
293 }
294}
295
296/// Minimal generation fence outcome bundled with one restart command.
297#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
298pub struct GenerationFenceOutcome {
299 /// Discrete fencing decision for this command.
300 pub decision: GenerationFenceDecision,
301 /// Old generation pinned or observed by this command, or `None`.
302 pub old_generation: Option<Generation>,
303 /// Old attempt pinned or observed by this command, or `None`.
304 pub old_attempt: Option<ChildStartCount>,
305 /// Generation planned to start after the old attempt exits.
306 pub target_generation: Option<Generation>,
307 /// Whether this command newly delivered cancellation to an active attempt.
308 pub cancel_delivered: bool,
309 /// Whether this command triggered or escalated abort semantics for the old attempt.
310 pub abort_requested: bool,
311 /// Structured failure when rejected or in conflict; required when `decision` is [`GenerationFenceDecision::Rejected`].
312 pub conflict: Option<ChildControlFailure>,
313}
314
315impl GenerationFenceOutcome {
316 /// Builds a minimal generation fence outcome.
317 ///
318 /// # Arguments
319 ///
320 /// - `decision`: Fencing decision for this command.
321 /// - `old_generation`: Recorded old generation at decision time, or `None`.
322 /// - `old_attempt`: Recorded old attempt at decision time, or `None`.
323 /// - `target_generation`: Planned next generation after the old attempt exits, or `None`.
324 /// - `cancel_delivered`: Whether cancellation was newly delivered.
325 /// - `abort_requested`: Whether abort was requested.
326 /// - `conflict`: Optional structured rejection or conflict payload.
327 ///
328 /// # Returns
329 ///
330 /// Returns a populated [`GenerationFenceOutcome`].
331 pub fn new(
332 decision: GenerationFenceDecision,
333 old_generation: Option<Generation>,
334 old_attempt: Option<ChildStartCount>,
335 target_generation: Option<Generation>,
336 cancel_delivered: bool,
337 abort_requested: bool,
338 conflict: Option<ChildControlFailure>,
339 ) -> Self {
340 Self {
341 decision,
342 old_generation,
343 old_attempt,
344 target_generation,
345 cancel_delivered,
346 abort_requested,
347 conflict,
348 }
349 }
350}
351
352/// Generation fence bookkeeping on the runtime side; no timeline, only whether start is allowed.
353#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
354pub struct GenerationFenceState {
355 /// Current discrete fencing phase.
356 pub phase: GenerationFencePhase,
357 /// Active attempt generation from this record perspective, if any.
358 pub active_generation: Option<Generation>,
359 /// Active attempt index from this record perspective, if any.
360 pub active_attempt: Option<ChildStartCount>,
361 /// When `Some`, a pending restart is waiting for the old attempt to exit.
362 pub pending_restart: Option<PendingRestart>,
363 /// Latest recorded stale exit report for diagnostics replay.
364 pub last_stale_report: Option<StaleAttemptReport>,
365}
366
367impl Default for GenerationFenceState {
368 /// Default placeholder: open phase with no pending restart.
369 fn default() -> Self {
370 Self {
371 phase: GenerationFencePhase::Open,
372 active_generation: None,
373 active_attempt: None,
374 pending_restart: None,
375 last_stale_report: None,
376 }
377 }
378}
379
380impl GenerationFenceState {
381 /// Creates a placeholder fence state record.
382 ///
383 /// # Arguments
384 ///
385 /// None.
386 ///
387 /// # Returns
388 ///
389 /// Returns the default record with phase [`GenerationFencePhase::Open`].
390 pub fn placeholder() -> Self {
391 Self::default()
392 }
393}
394
395/// Pending-restart triple summary shared with [`ChildRuntimeRecord`] and dashboards.
396#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
397pub struct PendingRestartSummary {
398 /// Pinned or observed old generation.
399 pub old_generation: Generation,
400 /// Pinned or observed old attempt index.
401 pub old_attempt: ChildStartCount,
402 /// Target generation expected after the old attempt exits.
403 pub target_generation: Generation,
404}
405
406impl From<&PendingRestart> for PendingRestartSummary {
407 /// Compresses a full [`PendingRestart`] into a dashboard-friendly summary.
408 fn from(source: &PendingRestart) -> Self {
409 Self {
410 old_generation: source.old_generation,
411 old_attempt: source.old_attempt,
412 target_generation: source.target_generation,
413 }
414 }
415}
416
417/// Runtime restart limit state.
418#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
419pub struct RestartLimitState {
420 /// Restart accounting window.
421 pub window: Duration,
422 /// Restart limit inside the window.
423 pub limit: u32,
424 /// Restart count used so far.
425 pub used: u32,
426 /// Remaining restart count.
427 pub remaining: u32,
428 /// Whether the restart limit is exhausted.
429 pub exhausted: bool,
430 /// Last update timestamp in Unix epoch nanoseconds.
431 pub updated_at_unix_nanos: u128,
432}
433
434impl RestartLimitState {
435 /// Creates a restart limit state.
436 ///
437 /// # Arguments
438 ///
439 /// - `window`: Restart accounting window.
440 /// - `limit`: Restart limit inside the window.
441 /// - `used`: Restart count used so far.
442 /// - `updated_at_unix_nanos`: Last update timestamp.
443 ///
444 /// # Returns
445 ///
446 /// Returns a [`RestartLimitState`] value.
447 pub fn new(window: Duration, limit: u32, used: u32, updated_at_unix_nanos: u128) -> Self {
448 let remaining = limit.saturating_sub(used);
449 Self {
450 window,
451 limit,
452 used,
453 remaining,
454 exhausted: remaining == 0,
455 updated_at_unix_nanos,
456 }
457 }
458}
459
460impl Default for RestartLimitState {
461 /// Creates the default restart limit state.
462 fn default() -> Self {
463 Self::new(Duration::from_secs(60), u32::MAX, 0, 0)
464 }
465}
466
467/// Liveness state for one child.
468#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
469pub struct ChildLivenessState {
470 /// Last heartbeat timestamp in Unix epoch nanoseconds.
471 pub last_heartbeat_at_unix_nanos: Option<u128>,
472 /// Whether heartbeat is stale.
473 pub heartbeat_stale: bool,
474 /// Latest readiness state.
475 pub readiness: ReadinessState,
476}
477
478impl ChildLivenessState {
479 /// Creates a child liveness state.
480 ///
481 /// # Arguments
482 ///
483 /// - `last_heartbeat_at_unix_nanos`: Last heartbeat timestamp.
484 /// - `heartbeat_stale`: Whether heartbeat is stale.
485 /// - `readiness`: Latest readiness state.
486 ///
487 /// # Returns
488 ///
489 /// Returns a [`ChildLivenessState`] value.
490 pub fn new(
491 last_heartbeat_at_unix_nanos: Option<u128>,
492 heartbeat_stale: bool,
493 readiness: ReadinessState,
494 ) -> Self {
495 Self {
496 last_heartbeat_at_unix_nanos,
497 heartbeat_stale,
498 readiness,
499 }
500 }
501}
502
503/// Public projection of one child runtime state record.
504#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
505pub struct ChildRuntimeRecord {
506 /// Stable child identifier.
507 pub child_id: ChildId,
508 /// Child path in the supervisor tree.
509 pub path: SupervisorPath,
510 /// Current active generation.
511 pub generation: Option<Generation>,
512 /// Current active attempt.
513 pub attempt: Option<ChildStartCount>,
514 /// Current attempt status.
515 pub status: Option<ChildAttemptStatus>,
516 /// Current control operation.
517 pub operation: ChildControlOperation,
518 /// Current liveness state.
519 pub liveness: ChildLivenessState,
520 /// Current restart limit state.
521 pub restart_limit: RestartLimitState,
522 /// Current stop progress.
523 pub stop_state: ChildStopState,
524 /// Most recent control failure.
525 pub failure: Option<ChildControlFailure>,
526 /// Generation fence phase returned with the `CurrentState` projection.
527 #[serde(default)]
528 pub generation_fence_phase: GenerationFencePhase,
529 /// Pending restart triple; present only while the fence queue still waits for the old attempt to exit.
530 #[serde(default)]
531 pub pending_restart: Option<PendingRestartSummary>,
532}
533
534impl ChildRuntimeRecord {
535 /// Creates a public child runtime record.
536 ///
537 /// # Arguments
538 ///
539 /// - `child_id`: Stable child identifier.
540 /// - `path`: Child path in the supervisor tree.
541 /// - `generation`: Current active generation.
542 /// - `attempt`: Current active attempt.
543 /// - `status`: Current attempt status.
544 /// - `operation`: Current control operation.
545 /// - `liveness`: Current liveness state.
546 /// - `restart_limit`: Current restart limit state.
547 /// - `stop_state`: Current stop progress.
548 /// - `failure`: Most recent control failure.
549 /// - `generation_fence_phase`: Projection of generation fencing phase enum.
550 /// - `pending_restart`: Optional queued restart fingerprint for dashboards.
551 ///
552 /// # Returns
553 ///
554 /// Returns a [`ChildRuntimeRecord`] value.
555 #[allow(clippy::too_many_arguments)]
556 pub fn new(
557 child_id: ChildId,
558 path: SupervisorPath,
559 generation: Option<Generation>,
560 attempt: Option<ChildStartCount>,
561 status: Option<ChildAttemptStatus>,
562 operation: ChildControlOperation,
563 liveness: ChildLivenessState,
564 restart_limit: RestartLimitState,
565 stop_state: ChildStopState,
566 failure: Option<ChildControlFailure>,
567 generation_fence_phase: GenerationFencePhase,
568 pending_restart: Option<PendingRestartSummary>,
569 ) -> Self {
570 Self {
571 child_id,
572 path,
573 generation,
574 attempt,
575 status,
576 operation,
577 liveness,
578 restart_limit,
579 stop_state,
580 failure,
581 generation_fence_phase,
582 pending_restart,
583 }
584 }
585}
586
587/// Result returned by a child control command.
588#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
589pub struct ChildControlResult {
590 /// Stable child identifier.
591 pub child_id: ChildId,
592 /// Active attempt targeted by the command.
593 pub attempt: Option<ChildStartCount>,
594 /// Active generation targeted by the command.
595 pub generation: Option<Generation>,
596 /// Control operation before command handling.
597 pub operation_before: ChildControlOperation,
598 /// Control operation after command handling.
599 pub operation_after: ChildControlOperation,
600 /// Current attempt status.
601 pub status: Option<ChildAttemptStatus>,
602 /// Whether this command delivered cancellation.
603 pub cancel_delivered: bool,
604 /// Stop progress after command handling.
605 pub stop_state: ChildStopState,
606 /// Current restart limit state.
607 pub restart_limit: RestartLimitState,
608 /// Current liveness state.
609 pub liveness: ChildLivenessState,
610 /// Whether this command reused existing state idempotently.
611 pub idempotent: bool,
612 /// Current failure reason.
613 pub failure: Option<ChildControlFailure>,
614 /// Optional generation fencing outcome exclusively used by restart control commands.
615 #[serde(default)]
616 pub generation_fence: Option<GenerationFenceOutcome>,
617 /// Admission conflict detail when a concurrent request was rejected.
618 #[serde(default)]
619 pub admission_conflict: Option<AdmissionConflict>,
620}
621
622impl ChildControlResult {
623 /// Creates a child control result.
624 ///
625 /// # Arguments
626 ///
627 /// - `child_id`: Stable child identifier.
628 /// - `attempt`: Active attempt targeted by the command.
629 /// - `generation`: Active generation targeted by the command.
630 /// - `operation_before`: Control operation before command handling.
631 /// - `operation_after`: Control operation after command handling.
632 /// - `status`: Current attempt status.
633 /// - `cancel_delivered`: Whether this command delivered cancellation.
634 /// - `stop_state`: Stop progress after command handling.
635 /// - `restart_limit`: Current restart limit state.
636 /// - `liveness`: Current liveness state.
637 /// - `idempotent`: Whether this command reused existing state idempotently.
638 /// - `failure`: Current failure reason.
639 /// - `generation_fence`: Optional restart-only fencing outcome payload.
640 ///
641 /// # Returns
642 ///
643 /// Returns a [`ChildControlResult`] value.
644 #[allow(clippy::too_many_arguments)]
645 pub fn new(
646 child_id: ChildId,
647 attempt: Option<ChildStartCount>,
648 generation: Option<Generation>,
649 operation_before: ChildControlOperation,
650 operation_after: ChildControlOperation,
651 status: Option<ChildAttemptStatus>,
652 cancel_delivered: bool,
653 stop_state: ChildStopState,
654 restart_limit: RestartLimitState,
655 liveness: ChildLivenessState,
656 idempotent: bool,
657 failure: Option<ChildControlFailure>,
658 generation_fence: Option<GenerationFenceOutcome>,
659 ) -> Self {
660 Self {
661 child_id,
662 attempt,
663 generation,
664 operation_before,
665 operation_after,
666 status,
667 cancel_delivered,
668 stop_state,
669 restart_limit,
670 liveness,
671 idempotent,
672 failure,
673 generation_fence,
674 admission_conflict: None,
675 }
676 }
677
678 /// Creates a conflict result when admission is denied.
679 ///
680 /// # Arguments
681 ///
682 /// - `child_id`: Child that already has an active attempt.
683 /// - `conflict`: Admission conflict detail.
684 ///
685 /// # Returns
686 ///
687 /// Returns a [`ChildControlResult`] carrying the conflict.
688 pub fn conflict(child_id: ChildId, conflict: AdmissionConflict) -> Self {
689 Self {
690 child_id,
691 attempt: Some(conflict.active_attempt),
692 generation: Some(conflict.active_generation),
693 operation_before: ChildControlOperation::Active,
694 operation_after: ChildControlOperation::Active,
695 status: Some(ChildAttemptStatus::Running),
696 cancel_delivered: false,
697 stop_state: ChildStopState::Idle,
698 restart_limit: RestartLimitState::default(),
699 liveness: ChildLivenessState::new(None, false, ReadinessState::Unreported),
700 idempotent: false,
701 failure: None,
702 generation_fence: None,
703 admission_conflict: Some(conflict),
704 }
705 }
706}