microsandbox_protocol_client/error.rs
1//! Safe errors with explicit local delivery uncertainty.
2
3use thiserror::Error;
4
5//--------------------------------------------------------------------------------------------------
6// Types
7//--------------------------------------------------------------------------------------------------
8
9/// Whether this attempt crossed writer admission; never a retry instruction.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Delivery {
12 /// No request bytes were submitted for this attempt.
13 NotSent,
14 /// The peer may have acted, including when only part of a write completed.
15 Unknown,
16}
17
18/// Transport/router failure, independent of application response errors.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
20pub enum ErrorKind {
21 /// The shared connection was explicitly closed or its last owner dropped.
22 #[error("connection closed")]
23 Closed,
24 /// EOF while idle, before the expected terminal response.
25 #[error("peer closed before terminal completion")]
26 PeerClosed,
27 /// EOF inside a frame, distinct from a clean byte-stream EOF.
28 #[error("peer closed inside a frame")]
29 TruncatedFrame,
30 /// An OS-level read, write, or connect failed.
31 #[error("transport I/O failed ({0:?})")]
32 Io(std::io::ErrorKind),
33 /// Total setup or per-attempt local wait expired.
34 #[error("local deadline expired")]
35 Timeout,
36 /// Malformed framing or envelope.
37 #[error("invalid protocol data")]
38 InvalidData,
39 /// Invalid local range, limits, or options.
40 #[error("invalid client configuration")]
41 InvalidOptions,
42 /// Byte, item, or in-flight capacity is unavailable.
43 #[error("client capacity exhausted")]
44 Capacity,
45 /// All IDs in the negotiated range are reserved or retired.
46 #[error("correlation ID range exhausted")]
47 IdRangeExhausted,
48 /// An explicit ID has no live send permission on this connection.
49 #[error("stream is closed or not owned by this connection")]
50 StreamClosed,
51 /// A typed operation is unavailable on this protocol/generation.
52 #[error("operation is unsupported by the peer")]
53 UnsupportedOperation,
54 /// Native serialization failed without submitting a request.
55 #[error("could not encode protocol message")]
56 Encode,
57}
58
59/// Error with conservative delivery state. Diagnostics never include payloads.
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
61#[error("{kind} (delivery: {delivery:?})")]
62pub struct ClientError {
63 /// Local reason for failure.
64 pub kind: ErrorKind,
65 /// Whether this particular attempt may have reached the peer.
66 pub delivery: Delivery,
67}
68
69/// Result shared by generic protocol and transport implementations.
70pub type ClientResult<T> = Result<T, ClientError>;
71
72//--------------------------------------------------------------------------------------------------
73// Methods
74//--------------------------------------------------------------------------------------------------
75
76impl ClientError {
77 /// A failure known to precede writer admission.
78 pub fn new(kind: ErrorKind) -> Self {
79 Self {
80 kind,
81 delivery: Delivery::NotSent,
82 }
83 }
84
85 /// Attach delivery information for a particular operation attempt.
86 pub fn with_delivery(self, delivery: Delivery) -> Self {
87 Self { delivery, ..self }
88 }
89}
90
91//--------------------------------------------------------------------------------------------------
92// Trait Implementations
93//--------------------------------------------------------------------------------------------------
94
95impl From<std::io::Error> for ClientError {
96 fn from(error: std::io::Error) -> Self {
97 Self::new(ErrorKind::Io(error.kind()))
98 }
99}
100
101impl From<microsandbox_protocol::wire::WireError> for ClientError {
102 fn from(_: microsandbox_protocol::wire::WireError) -> Self {
103 Self::new(ErrorKind::InvalidData)
104 }
105}