cloud_sdk/transport/
delivery.rs1use core::fmt;
4
5#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
7pub enum DeliveryPhase {
8 NotSent,
10 PossiblySent,
12 ResponseStarted,
14}
15
16#[derive(Clone, Copy)]
21pub struct TransportFailure<E> {
22 phase: DeliveryPhase,
23 error: E,
24}
25
26impl<E> TransportFailure<E> {
27 #[must_use]
29 pub const fn not_sent(error: E) -> Self {
30 Self {
31 phase: DeliveryPhase::NotSent,
32 error,
33 }
34 }
35
36 #[must_use]
38 pub const fn possibly_sent(error: E) -> Self {
39 Self {
40 phase: DeliveryPhase::PossiblySent,
41 error,
42 }
43 }
44
45 #[must_use]
47 pub const fn response_started(error: E) -> Self {
48 Self {
49 phase: DeliveryPhase::ResponseStarted,
50 error,
51 }
52 }
53
54 #[must_use]
56 pub const fn unknown(error: E) -> Self {
57 Self::possibly_sent(error)
58 }
59
60 #[must_use]
62 pub const fn phase(&self) -> DeliveryPhase {
63 self.phase
64 }
65
66 #[must_use]
68 pub const fn error(&self) -> &E {
69 &self.error
70 }
71
72 #[must_use]
74 pub fn into_error(self) -> E {
75 self.error
76 }
77}
78
79impl<E: PartialEq> PartialEq for TransportFailure<E> {
80 fn eq(&self, other: &Self) -> bool {
81 self.phase == other.phase && self.error == other.error
82 }
83}
84
85impl<E: Eq> Eq for TransportFailure<E> {}
86
87impl<E> fmt::Debug for TransportFailure<E> {
88 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
89 formatter
90 .debug_struct("TransportFailure")
91 .field("phase", &self.phase)
92 .field("error", &"[redacted]")
93 .finish()
94 }
95}
96
97impl<E> fmt::Display for TransportFailure<E> {
98 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
99 match self.phase {
100 DeliveryPhase::NotSent => formatter.write_str("transport failed before delivery"),
101 DeliveryPhase::PossiblySent => {
102 formatter.write_str("transport failed with uncertain delivery")
103 }
104 DeliveryPhase::ResponseStarted => {
105 formatter.write_str("transport failed after response start")
106 }
107 }
108 }
109}
110
111impl<E> core::error::Error for TransportFailure<E> {}
112
113#[cfg(test)]
114mod tests {
115 use core::fmt::{self, Write};
116
117 use super::{DeliveryPhase, TransportFailure};
118
119 struct FixedText {
120 bytes: [u8; 96],
121 len: usize,
122 }
123
124 impl FixedText {
125 const fn new() -> Self {
126 Self {
127 bytes: [0_u8; 96],
128 len: 0,
129 }
130 }
131
132 fn as_str(&self) -> &str {
133 core::str::from_utf8(self.bytes.get(..self.len).unwrap_or_default()).unwrap_or_default()
134 }
135 }
136
137 impl Write for FixedText {
138 fn write_str(&mut self, value: &str) -> fmt::Result {
139 let end = self.len.checked_add(value.len()).ok_or(fmt::Error)?;
140 let output = self.bytes.get_mut(self.len..end).ok_or(fmt::Error)?;
141 output.copy_from_slice(value.as_bytes());
142 self.len = end;
143 Ok(())
144 }
145 }
146
147 #[test]
148 fn unknown_delivery_fails_closed_and_debug_redacts_the_error() {
149 let failure = TransportFailure::unknown("secret payload");
150 assert_eq!(failure.phase(), DeliveryPhase::PossiblySent);
151 let mut debug = FixedText::new();
152 assert!(write!(&mut debug, "{failure:?}").is_ok());
153 assert!(debug.as_str().contains("PossiblySent"));
154 assert!(!debug.as_str().contains("secret payload"));
155 }
156}