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