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