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