1#![no_std]
2#![doc = include_str!("../README.md")]
3
4#[cfg(feature = "std")]
5extern crate std;
6
7#[cfg(feature = "alloc")]
8extern crate alloc;
9
10macro_rules! impl_static_error {
11 ($error:ty, $($pattern:pat => $message:literal),+ $(,)?) => {
12 impl core::fmt::Display for $error {
13 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
14 formatter.write_str(match self {
15 $($pattern => $message,)+
16 })
17 }
18 }
19
20 impl core::error::Error for $error {}
21 };
22}
23
24pub mod action_polling;
25pub mod authentication;
26pub mod buffer;
27pub mod client;
28pub mod diagnostics;
29mod identity;
30mod method;
31pub mod operation;
32pub mod pagination;
33pub mod rate_limit;
34pub mod retry;
35pub mod transport;
36
37pub use identity::{
38 IdentityError, MAX_PROVIDER_ID_BYTES, MAX_SERVICE_ID_BYTES, ProviderId, ProviderMarker,
39 ServiceId, ServiceMarker,
40};
41pub use method::{MAX_METHOD_BYTES, Method, MethodError};
42
43#[cfg(test)]
44mod tests {
45 use super::{Method, MethodError, ProviderId, ServiceId};
46 use crate::action_polling::{ActionObserveError, ActionPollError};
47 use crate::authentication::{
48 AuthenticationScopeError, CredentialGenerationError, ScopeValueError, SigningBuildError,
49 SigningContextValueError, SigningInputError, SigningOutputError, SigningValueError,
50 };
51 use crate::client::{
52 CheckedDecodeError, ClientExecutionError, WorkspaceAcquireError, WorkspacePoolError,
53 };
54 use crate::operation::{
55 AttemptBudgetError, CurrencyCodeError, ExecutionPermitError, OperationMetadataError,
56 PermitContextError, PermitExecutionError, PermitIdempotencyKeyError, PermitValidityError,
57 PlanCostError, PlanFingerprintBuildError, PreparedExecutionError, ResponsePolicyError,
58 ResponsePolicyValidationError,
59 };
60 use crate::pagination::PaginationError;
61 use crate::rate_limit::{
62 DelayDecisionError, QuotaError, QuotaExtensionError, RateLimitError, RetryAfterError,
63 };
64 use crate::retry::{
65 FingerprintBuildError, IdempotencyIntentError, MaxAttemptsError, RetryExecutionError,
66 RetryPermitError, RetryPolicyError,
67 };
68 use crate::transport::{
69 AsyncExecutionError, ContentTypeError, EndpointIdentityError, HeaderError,
70 InformationalResponseError, RawResponsePolicyError, RequestPathError, RequestTargetError,
71 ResponseWriterError, StreamExecutionError, StreamLimitsError, StreamPolicyError,
72 StreamProgressError, StreamReplayError, StreamSourceIdError, TransportFailure,
73 };
74 use core::fmt::{self, Write};
75
76 #[test]
77 fn exposes_provider_neutral_domains() {
78 assert_eq!(
79 ProviderId::new("example").map(ProviderId::as_str),
80 Ok("example")
81 );
82 assert_eq!(
83 ServiceId::new("compute").map(ServiceId::as_str),
84 Ok("compute")
85 );
86 assert_eq!(Method::Get, Method::Get);
87 assert_eq!(Method::Post.as_str(), "POST");
88 }
89
90 #[test]
91 fn public_errors_implement_payload_free_core_error() {
92 fn assert_error<E: core::error::Error>() {}
93
94 assert_error::<PaginationError>();
95 assert_error::<AuthenticationScopeError>();
96 assert_error::<CredentialGenerationError>();
97 assert_error::<ScopeValueError>();
98 assert_error::<SigningValueError>();
99 assert_error::<SigningContextValueError>();
100 assert_error::<SigningInputError>();
101 assert_error::<SigningBuildError<core::convert::Infallible>>();
102 assert_error::<SigningOutputError<core::convert::Infallible>>();
103 assert_error::<RateLimitError>();
104 assert_error::<QuotaError>();
105 assert_error::<QuotaExtensionError>();
106 assert_error::<RetryAfterError>();
107 assert_error::<DelayDecisionError>();
108 assert_error::<FingerprintBuildError<core::convert::Infallible>>();
109 assert_error::<IdempotencyIntentError>();
110 assert_error::<MaxAttemptsError>();
111 assert_error::<RetryPolicyError>();
112 assert_error::<RetryPermitError>();
113 assert_error::<RetryExecutionError<()>>();
114 assert_error::<ContentTypeError>();
115 assert_error::<HeaderError>();
116 assert_error::<EndpointIdentityError>();
117 assert_error::<RequestTargetError>();
118 assert_error::<MethodError>();
119 assert_error::<ActionPollError>();
120 assert_error::<ActionObserveError<&'static str>>();
121 assert_error::<OperationMetadataError>();
122 assert_error::<ResponsePolicyError>();
123 assert_error::<ResponsePolicyValidationError>();
124 assert_error::<PreparedExecutionError<()>>();
125 assert_error::<AttemptBudgetError>();
126 assert_error::<CurrencyCodeError>();
127 assert_error::<ExecutionPermitError>();
128 assert_error::<PermitContextError>();
129 assert_error::<PermitExecutionError<()>>();
130 assert_error::<PermitIdempotencyKeyError>();
131 assert_error::<PermitValidityError>();
132 assert_error::<PlanCostError>();
133 assert_error::<PlanFingerprintBuildError<core::convert::Infallible>>();
134 assert_error::<ResponseWriterError>();
135 assert_error::<AsyncExecutionError<()>>();
136 assert_error::<RawResponsePolicyError>();
137 assert_error::<InformationalResponseError>();
138 assert_error::<StreamLimitsError>();
139 assert_error::<StreamPolicyError>();
140 assert_error::<StreamProgressError>();
141 assert_error::<StreamSourceIdError>();
142 assert_error::<StreamReplayError>();
143 assert_error::<StreamExecutionError<(), ()>>();
144 assert_error::<TransportFailure<()>>();
145 assert_error::<CheckedDecodeError<()>>();
146 assert_error::<ClientExecutionError<(), (), ()>>();
147 assert_error::<WorkspaceAcquireError>();
148 assert_error::<WorkspacePoolError>();
149
150 assert_display(PaginationError::PageZero, "page number must be nonzero");
151 assert_display(RateLimitError::LimitZero, "rate limit must be nonzero");
152 assert_display(ContentTypeError::Empty, "content type is empty");
153 assert_display(HeaderError::DuplicateName, "HTTP header name is duplicated");
154 assert_display(
155 EndpointIdentityError::UnboundTransport,
156 "transport endpoint identity is unbound",
157 );
158 assert_display(
159 RequestTargetError::Path(RequestPathError::Empty),
160 "invalid request path: request path is empty",
161 );
162 assert_display(
163 MethodError::DeniedMethod,
164 "HTTP method is denied by the transport contract",
165 );
166 assert_display(
167 OperationMetadataError::NonIdempotentRetry,
168 "non-idempotent operation cannot be retry eligible",
169 );
170 assert_display(
171 ResponsePolicyError::UnexpectedContentType,
172 "response content type is not accepted",
173 );
174 assert_display(
175 ResponsePolicyError::InvalidContentType,
176 "response content type is invalid",
177 );
178 assert_display(
179 ResponsePolicyValidationError::MissingSuccessStatus,
180 "response policy has no success status",
181 );
182 assert_display(
183 PreparedExecutionError::<()>::Transport(()),
184 "prepared request transport failed",
185 );
186 assert_display(
187 PreparedExecutionError::<()>::AuthorizationRequired,
188 "state-changing request requires execution authority",
189 );
190 assert_display(
191 ResponseWriterError::AlreadyCommitted,
192 "response writer is already committed",
193 );
194 assert_display(
195 RawResponsePolicyError::UnsafeAdmittedHeader,
196 "an unsafe response header was admitted",
197 );
198 assert_display(
199 InformationalResponseError::SwitchingProtocols,
200 "switching protocols is forbidden",
201 );
202 assert_display(
203 TransportFailure::unknown("sentinel-secret"),
204 "transport failed with uncertain delivery",
205 );
206 assert_display(
207 ActionObserveError::Backoff("sentinel-secret"),
208 "action poll backoff policy failed",
209 );
210 }
211
212 fn assert_display(error: impl fmt::Display, expected: &str) {
213 let mut output = DisplayBuffer::new();
214 assert!(write!(&mut output, "{error}").is_ok());
215 assert_eq!(output.as_str(), expected);
216 }
217
218 struct DisplayBuffer {
219 bytes: [u8; 128],
220 len: usize,
221 }
222
223 impl DisplayBuffer {
224 const fn new() -> Self {
225 Self {
226 bytes: [0; 128],
227 len: 0,
228 }
229 }
230
231 fn as_str(&self) -> &str {
232 core::str::from_utf8(self.bytes.get(..self.len).unwrap_or_default()).unwrap_or_default()
233 }
234 }
235
236 impl Write for DisplayBuffer {
237 fn write_str(&mut self, value: &str) -> fmt::Result {
238 let end = self.len.checked_add(value.len()).ok_or(fmt::Error)?;
239 let target = self.bytes.get_mut(self.len..end).ok_or(fmt::Error)?;
240 target.copy_from_slice(value.as_bytes());
241 self.len = end;
242 Ok(())
243 }
244 }
245}