Skip to main content

cloud_sdk/transport/
delivery.rs

1//! Conservative transport-delivery classification.
2
3use core::fmt;
4
5/// Furthest delivery point known for one failed HTTP attempt.
6#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
7pub enum DeliveryPhase {
8    /// Validation or connection establishment failed before request delivery.
9    NotSent,
10    /// Some request bytes may have reached the peer.
11    PossiblySent,
12    /// Any informational or final response head was observed from the peer.
13    ResponseStarted,
14}
15
16/// Error whose furthest possible request-delivery phase is explicit.
17pub trait DeliveryClassified {
18    /// Returns the conservative delivery phase for permit transitions.
19    fn delivery_phase(&self) -> DeliveryPhase;
20}
21
22/// Payload-redacting transport failure with an explicit delivery phase.
23///
24/// Unknown send state must use [`Self::unknown`], which conservatively maps to
25/// [`DeliveryPhase::PossiblySent`].
26#[derive(Clone, Copy)]
27pub struct TransportFailure<E> {
28    phase: DeliveryPhase,
29    error: E,
30}
31
32impl<E> TransportFailure<E> {
33    /// Records a failure proven to have happened before request delivery.
34    #[must_use]
35    pub const fn not_sent(error: E) -> Self {
36        Self {
37            phase: DeliveryPhase::NotSent,
38            error,
39        }
40    }
41
42    /// Records a failure after request delivery may have begun.
43    #[must_use]
44    pub const fn possibly_sent(error: E) -> Self {
45        Self {
46            phase: DeliveryPhase::PossiblySent,
47            error,
48        }
49    }
50
51    /// Records a failure after any response head was observed.
52    #[must_use]
53    pub const fn response_started(error: E) -> Self {
54        Self {
55            phase: DeliveryPhase::ResponseStarted,
56            error,
57        }
58    }
59
60    /// Conservatively records an error whose delivery state is unknown.
61    #[must_use]
62    pub const fn unknown(error: E) -> Self {
63        Self::possibly_sent(error)
64    }
65
66    /// Returns the conservative delivery phase.
67    #[must_use]
68    pub const fn phase(&self) -> DeliveryPhase {
69        self.phase
70    }
71
72    /// Returns a shared view of the payload-free adapter error.
73    #[must_use]
74    pub const fn error(&self) -> &E {
75        &self.error
76    }
77
78    /// Consumes the failure and returns its adapter error.
79    #[must_use]
80    pub fn into_error(self) -> E {
81        self.error
82    }
83
84    /// Transforms the payload-free adapter error while preserving delivery state.
85    #[must_use]
86    pub fn map<F>(self, transform: impl FnOnce(E) -> F) -> TransportFailure<F> {
87        TransportFailure {
88            phase: self.phase,
89            error: transform(self.error),
90        }
91    }
92}
93
94impl<E> DeliveryClassified for TransportFailure<E> {
95    fn delivery_phase(&self) -> DeliveryPhase {
96        self.phase
97    }
98}
99
100impl<E: PartialEq> PartialEq for TransportFailure<E> {
101    fn eq(&self, other: &Self) -> bool {
102        self.phase == other.phase && self.error == other.error
103    }
104}
105
106impl<E: Eq> Eq for TransportFailure<E> {}
107
108impl<E> fmt::Debug for TransportFailure<E> {
109    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
110        formatter
111            .debug_struct("TransportFailure")
112            .field("phase", &self.phase)
113            .field("error", &"[redacted]")
114            .finish()
115    }
116}
117
118impl<E> fmt::Display for TransportFailure<E> {
119    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
120        match self.phase {
121            DeliveryPhase::NotSent => formatter.write_str("transport failed before delivery"),
122            DeliveryPhase::PossiblySent => {
123                formatter.write_str("transport failed with uncertain delivery")
124            }
125            DeliveryPhase::ResponseStarted => {
126                formatter.write_str("transport failed after response start")
127            }
128        }
129    }
130}
131
132impl<E> core::error::Error for TransportFailure<E> {}
133
134#[cfg(test)]
135mod tests {
136    use core::fmt::{self, Write};
137
138    use super::{DeliveryPhase, TransportFailure};
139
140    struct FixedText {
141        bytes: [u8; 96],
142        len: usize,
143    }
144
145    impl FixedText {
146        const fn new() -> Self {
147            Self {
148                bytes: [0_u8; 96],
149                len: 0,
150            }
151        }
152
153        fn as_str(&self) -> &str {
154            core::str::from_utf8(self.bytes.get(..self.len).unwrap_or_default()).unwrap_or_default()
155        }
156    }
157
158    impl Write for FixedText {
159        fn write_str(&mut self, value: &str) -> fmt::Result {
160            let end = self.len.checked_add(value.len()).ok_or(fmt::Error)?;
161            let output = self.bytes.get_mut(self.len..end).ok_or(fmt::Error)?;
162            output.copy_from_slice(value.as_bytes());
163            self.len = end;
164            Ok(())
165        }
166    }
167
168    #[test]
169    fn unknown_delivery_fails_closed_and_debug_redacts_the_error() {
170        let failure = TransportFailure::unknown("secret payload");
171        assert_eq!(failure.phase(), DeliveryPhase::PossiblySent);
172        let mut debug = FixedText::new();
173        assert!(write!(&mut debug, "{failure:?}").is_ok());
174        assert!(debug.as_str().contains("PossiblySent"));
175        assert!(!debug.as_str().contains("secret payload"));
176    }
177}