Skip to main content

liminal_protocol/outcome/
keepalive.rs

1use alloc::string::String;
2
3/// Signed platform descriptor used in keepalive certification failures.
4#[derive(Clone, Debug, PartialEq, Eq)]
5pub struct PlatformName(String);
6
7impl PlatformName {
8    /// Creates a platform descriptor from its signed name.
9    #[must_use]
10    pub const fn new(name: String) -> Self {
11        Self(name)
12    }
13
14    /// Borrows the signed platform name.
15    #[must_use]
16    pub fn as_str(&self) -> &str {
17        &self.0
18    }
19
20    /// Returns the owned signed platform name.
21    #[must_use]
22    pub fn into_string(self) -> String {
23        self.0
24    }
25}
26
27/// Numeric keepalive configuration field.
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub enum KeepaliveField {
30    /// Idle seconds before the first probe.
31    IdleSeconds,
32    /// Seconds between probes.
33    IntervalSeconds,
34    /// Probe count before connection failure.
35    ProbeCount,
36}
37
38/// Socket option selected by certification.
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub enum KeepaliveOption {
41    /// Boolean `SO_KEEPALIVE` enablement.
42    SoKeepalive,
43    /// Platform idle-time option.
44    Idle,
45    /// Platform probe-interval option.
46    Interval,
47    /// Platform probe-count option.
48    Count,
49}
50
51/// Phase in which keepalive certification failed.
52#[derive(Clone, Copy, Debug, PartialEq, Eq)]
53pub enum KeepalivePhase {
54    /// Listener startup configuration validation.
55    StartupConfiguration,
56    /// Configuration of one accepted participant socket.
57    AcceptedSocket,
58}
59
60/// Type-safe read-back mismatch body.
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
62pub enum KeepaliveReadbackMismatch {
63    /// Boolean `SO_KEEPALIVE` mismatch.
64    SoKeepalive {
65        /// Requested enablement.
66        requested: bool,
67        /// Effective enablement.
68        effective: bool,
69    },
70    /// Numeric keepalive-option mismatch.
71    Numeric {
72        /// Idle, interval, or count option; `SO_KEEPALIVE` is absent by type.
73        option: NumericKeepaliveOption,
74        /// Requested unsigned value.
75        requested: u64,
76        /// Effective unsigned value.
77        effective: u64,
78    },
79}
80
81/// Numeric socket options; boolean `SO_KEEPALIVE` is deliberately absent.
82#[derive(Clone, Copy, Debug, PartialEq, Eq)]
83pub enum NumericKeepaliveOption {
84    /// Platform idle-time option.
85    Idle,
86    /// Platform probe-interval option.
87    Interval,
88    /// Platform probe-count option.
89    Count,
90}
91
92/// Startup-only keepalive validation or support failure.
93#[derive(Clone, Debug, PartialEq, Eq)]
94pub enum StartupKeepaliveReason {
95    /// A required numeric value was zero.
96    Zero {
97        /// First zero field in certification order.
98        field: KeepaliveField,
99        /// Requested value, which is zero for this variant.
100        requested: u64,
101        /// Required minimum, which is one for this variant.
102        required_minimum: u64,
103    },
104    /// Requested value is outside the signed platform range.
105    OutOfRange {
106        /// First out-of-range field in certification order.
107        field: KeepaliveField,
108        /// Requested value.
109        requested: u64,
110        /// Inclusive signed platform minimum.
111        supported_min: u64,
112        /// Inclusive signed platform maximum.
113        supported_max: u64,
114        /// Signed target-platform descriptor.
115        platform: PlatformName,
116    },
117    /// Requested value cannot be represented at platform granularity.
118    GranularityMismatch {
119        /// First mismatched field in certification order.
120        field: KeepaliveField,
121        /// Requested value.
122        requested: u64,
123        /// Signed platform granularity.
124        granularity: u64,
125        /// Signed target-platform descriptor.
126        platform: PlatformName,
127    },
128    /// No signed keepalive descriptor exists for the platform.
129    UnsupportedPlatform {
130        /// Unsupported target-platform descriptor.
131        platform: PlatformName,
132    },
133    /// A required option is absent from the signed descriptor.
134    UnsupportedOption {
135        /// First unsupported option in certification order.
136        option: KeepaliveOption,
137        /// Signed target-platform descriptor.
138        platform: PlatformName,
139    },
140}
141
142/// Accepted-socket-only set or read-back certification failure.
143#[derive(Clone, Debug, PartialEq, Eq)]
144pub enum AcceptedSocketKeepaliveReason {
145    /// Setting a required option failed.
146    SetFailed {
147        /// First option whose set failed.
148        option: KeepaliveOption,
149        /// Signed target-platform descriptor.
150        platform: PlatformName,
151        /// Raw operating-system error code.
152        os_error: i32,
153    },
154    /// Reading back a required option failed.
155    ReadbackFailed {
156        /// First option whose read-back failed.
157        option: KeepaliveOption,
158        /// Signed target-platform descriptor.
159        platform: PlatformName,
160        /// Raw operating-system error code.
161        os_error: i32,
162    },
163    /// Read-back value differs from the exact requested value.
164    ReadbackMismatch {
165        /// Exact option-specific requested/effective pair.
166        mismatch: KeepaliveReadbackMismatch,
167        /// Signed target-platform descriptor.
168        platform: PlatformName,
169    },
170}
171
172/// Keepalive startup or accepted-socket certification failed.
173///
174/// The phase is encoded by the outer variant, so startup-only and
175/// accepted-socket-only reasons cannot be combined with the wrong phase.
176#[derive(Clone, Debug, PartialEq, Eq)]
177pub enum KeepaliveCertificationFailed {
178    /// Startup configuration failed before opening the participant listener.
179    StartupConfiguration(StartupKeepaliveReason),
180    /// One accepted socket failed before participant negotiation.
181    AcceptedSocket(AcceptedSocketKeepaliveReason),
182}
183
184impl KeepaliveCertificationFailed {
185    /// Returns the phase fixed by this outcome's reason family.
186    #[must_use]
187    pub const fn phase(&self) -> KeepalivePhase {
188        match self {
189            Self::StartupConfiguration(_) => KeepalivePhase::StartupConfiguration,
190            Self::AcceptedSocket(_) => KeepalivePhase::AcceptedSocket,
191        }
192    }
193}