Skip to main content

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}