made_client/
made_client_error.rs1use std::io;
2
3use thiserror::Error;
4use tonic::Code;
5
6#[derive(Debug, Error)]
8pub enum MadeClientError {
9 #[error("invalid MADE endpoint: {0}")]
10 InvalidEndpoint(String),
11 #[error("invocation id must contain 1 to 256 non-whitespace bytes")]
12 InvalidInvocationId,
13 #[error("invalid TLS client configuration: {0}")]
14 TlsConfiguration(String),
15 #[error("connection attempts exhausted: {0}")]
16 ConnectionExhausted(String),
17 #[error("gRPC transport failed: {0}")]
18 Transport(String),
19 #[error("MADE returned {code:?}: {message}")]
20 RemoteStatus { code: Code, message: String },
21 #[error("public protocol violation: {0}")]
22 ProtocolViolation(String),
23 #[error("checkpoint belongs to {actual}, expected {expected}")]
24 CursorScopeMismatch { expected: String, actual: String },
25 #[error("cursor checkpoint is corrupt: {0}")]
26 CursorCheckpointCorrupt(String),
27 #[error("artifact digest mismatch: expected {expected}, observed {observed}")]
28 ArtifactIntegrityMismatch { expected: String, observed: String },
29 #[error("artifact size mismatch: expected {expected}, observed {observed}")]
30 ArtifactSizeMismatch { expected: u64, observed: u64 },
31 #[error("local I/O failed at {path}: {source}")]
32 Io { path: String, source: io::Error },
33}
34
35impl MadeClientError {
36 #[must_use]
37 pub fn is_retryable_read(&self) -> bool {
38 matches!(
39 self,
40 Self::RemoteStatus {
41 code: Code::Unavailable | Code::DeadlineExceeded,
42 ..
43 } | Self::Transport(_)
44 )
45 }
46
47 #[must_use]
48 pub fn exit_code(&self) -> u8 {
49 match self {
50 Self::InvalidEndpoint(_)
51 | Self::InvalidInvocationId
52 | Self::TlsConfiguration(_)
53 | Self::ConnectionExhausted(_)
54 | Self::Transport(_)
55 | Self::RemoteStatus { .. } => 3,
56 Self::ProtocolViolation(_)
57 | Self::CursorScopeMismatch { .. }
58 | Self::CursorCheckpointCorrupt(_)
59 | Self::ArtifactIntegrityMismatch { .. }
60 | Self::ArtifactSizeMismatch { .. } => 4,
61 Self::Io { .. } => 5,
62 }
63 }
64
65 pub(crate) fn from_status(status: tonic::Status) -> Self {
66 const MAX_MESSAGE_CHARS: usize = 512;
67 let code = status.code();
68 let message: String = status.message().chars().take(MAX_MESSAGE_CHARS).collect();
69 drop(status);
70 Self::RemoteStatus { code, message }
71 }
72
73 pub(crate) fn io(path: &std::path::Path, source: io::Error) -> Self {
74 Self::Io {
75 path: path.display().to_string(),
76 source,
77 }
78 }
79}