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