rustferry_remote/
error.rs1use thiserror::Error;
2
3use crate::{
4 protocol::{JobState, ProtocolVersion},
5 provider::ProviderFeature,
6};
7
8pub type RemoteBuildResult<T> = Result<T, RemoteBuildError>;
10
11#[derive(Clone, Debug, Eq, Error, PartialEq)]
13pub enum RemoteBuildError {
14 #[error("remote protocol version {received} is incompatible; this client supports {supported}")]
16 IncompatibleProtocolVersion {
17 supported: ProtocolVersion,
19 received: ProtocolVersion,
21 },
22 #[error("remote event ended before a complete JSON object was received")]
24 TruncatedEvent,
25 #[error("remote event is malformed: {message}")]
27 MalformedEvent {
28 message: String,
30 },
31 #[error("remote event is not valid UTF-8")]
33 InvalidUtf8,
34 #[error("remote event is {bytes} bytes; maximum accepted size is {maximum} bytes")]
36 EventTooLarge {
37 bytes: usize,
39 maximum: usize,
41 },
42 #[error("remote field `{field}` is invalid: {reason}")]
44 InvalidIdentifier {
45 field: &'static str,
47 reason: &'static str,
49 },
50 #[error("remote field `{field}` contains terminal control data")]
52 UnsafeEventText {
53 field: &'static str,
55 },
56 #[error("remote event `{event}` is invalid: {reason}")]
58 InvalidEventPayload {
59 event: &'static str,
61 reason: &'static str,
63 },
64 #[error("source manifest is invalid: {message}")]
66 InvalidSourceManifest {
67 message: String,
69 },
70 #[error("job cannot transition from {from:?} to {to:?}")]
72 InvalidJobTransition {
73 from: JobState,
75 to: JobState,
77 },
78 #[error("remote operation was cancelled")]
80 Cancelled,
81 #[error("provider `{provider}` does not support {feature}")]
83 UnsupportedCapability {
84 provider: String,
86 feature: ProviderFeature,
88 },
89 #[error("provider `{provider}` failed with `{code}`: {message}")]
91 ProviderFailure {
92 provider: String,
94 code: String,
96 message: String,
98 retryable: bool,
100 },
101 #[error("artifact `{artifact_id}` was not found for job `{job_id}`")]
103 ArtifactNotFound {
104 job_id: String,
106 artifact_id: String,
108 },
109 #[error("artifact `{artifact_id}` digest mismatch: expected {expected}, received {actual}")]
111 IntegrityMismatch {
112 artifact_id: String,
114 expected: String,
116 actual: String,
118 },
119 #[error("could not serialize remote protocol data: {message}")]
121 Serialization {
122 message: String,
124 },
125}
126
127impl RemoteBuildError {
128 pub const fn code(&self) -> &'static str {
130 match self {
131 Self::IncompatibleProtocolVersion { .. } => "incompatible_protocol_version",
132 Self::TruncatedEvent => "truncated_event",
133 Self::MalformedEvent { .. } => "malformed_event",
134 Self::InvalidUtf8 => "invalid_utf8",
135 Self::EventTooLarge { .. } => "event_too_large",
136 Self::InvalidIdentifier { .. } => "invalid_identifier",
137 Self::UnsafeEventText { .. } => "unsafe_event_text",
138 Self::InvalidEventPayload { .. } => "invalid_event_payload",
139 Self::InvalidSourceManifest { .. } => "invalid_source_manifest",
140 Self::InvalidJobTransition { .. } => "invalid_job_transition",
141 Self::Cancelled => "cancelled",
142 Self::UnsupportedCapability { .. } => "unsupported_capability",
143 Self::ProviderFailure { .. } => "provider_failure",
144 Self::ArtifactNotFound { .. } => "artifact_not_found",
145 Self::IntegrityMismatch { .. } => "integrity_mismatch",
146 Self::Serialization { .. } => "serialization_failed",
147 }
148 }
149
150 pub const fn retryable(&self) -> bool {
152 match self {
153 Self::ProviderFailure { retryable, .. } => *retryable,
154 Self::TruncatedEvent | Self::Cancelled => true,
155 Self::IncompatibleProtocolVersion { .. }
156 | Self::MalformedEvent { .. }
157 | Self::InvalidUtf8
158 | Self::EventTooLarge { .. }
159 | Self::InvalidIdentifier { .. }
160 | Self::UnsafeEventText { .. }
161 | Self::InvalidEventPayload { .. }
162 | Self::InvalidSourceManifest { .. }
163 | Self::InvalidJobTransition { .. }
164 | Self::UnsupportedCapability { .. }
165 | Self::ArtifactNotFound { .. }
166 | Self::IntegrityMismatch { .. }
167 | Self::Serialization { .. } => false,
168 }
169 }
170}