1#![no_std]
2#![doc = include_str!("../README.md")]
3
4#[cfg(feature = "std")]
5extern crate std;
6
7macro_rules! impl_static_error {
8 ($error:ty, $($pattern:pat => $message:literal),+ $(,)?) => {
9 impl core::fmt::Display for $error {
10 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
11 formatter.write_str(match self {
12 $($pattern => $message,)+
13 })
14 }
15 }
16
17 impl core::error::Error for $error {}
18 };
19}
20
21pub mod action_polling;
22pub mod buffer;
23mod identity;
24pub mod operation;
25pub mod pagination;
26pub mod rate_limit;
27pub mod transport;
28
29pub use identity::{
30 IdentityError, MAX_PROVIDER_ID_BYTES, MAX_SERVICE_ID_BYTES, ProviderId, ProviderMarker,
31 ServiceId, ServiceMarker,
32};
33
34#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
36pub enum Method {
37 Get,
39 Post,
41 Put,
43 Delete,
45}
46
47impl Method {
48 #[must_use]
50 pub const fn as_str(self) -> &'static str {
51 match self {
52 Self::Get => "GET",
53 Self::Post => "POST",
54 Self::Put => "PUT",
55 Self::Delete => "DELETE",
56 }
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::{Method, ProviderId, ServiceId};
63 use crate::action_polling::ActionPollError;
64 use crate::operation::{
65 OperationMetadataError, PreparedExecutionError, ResponsePolicyError,
66 ResponsePolicyValidationError,
67 };
68 use crate::pagination::PaginationError;
69 use crate::rate_limit::RateLimitError;
70 use crate::transport::{ContentTypeError, EndpointIdentityError, RequestTargetError};
71 use core::fmt::{self, Write};
72
73 #[test]
74 fn exposes_provider_neutral_domains() {
75 assert_eq!(
76 ProviderId::new("example").map(ProviderId::as_str),
77 Ok("example")
78 );
79 assert_eq!(
80 ServiceId::new("compute").map(ServiceId::as_str),
81 Ok("compute")
82 );
83 assert_eq!(Method::Get, Method::Get);
84 assert_eq!(Method::Post.as_str(), "POST");
85 }
86
87 #[test]
88 fn public_errors_implement_payload_free_core_error() {
89 fn assert_error<E: core::error::Error>() {}
90
91 assert_error::<PaginationError>();
92 assert_error::<RateLimitError>();
93 assert_error::<ContentTypeError>();
94 assert_error::<EndpointIdentityError>();
95 assert_error::<RequestTargetError>();
96 assert_error::<ActionPollError<&'static str>>();
97 assert_error::<OperationMetadataError>();
98 assert_error::<ResponsePolicyError>();
99 assert_error::<ResponsePolicyValidationError>();
100 assert_error::<PreparedExecutionError<()>>();
101
102 assert_display(PaginationError::PageZero, "page number must be nonzero");
103 assert_display(RateLimitError::LimitZero, "rate limit must be nonzero");
104 assert_display(ContentTypeError::Empty, "content type is empty");
105 assert_display(
106 EndpointIdentityError::UnboundTransport,
107 "transport endpoint identity is unbound",
108 );
109 assert_display(RequestTargetError::Empty, "request target is empty");
110 assert_display(
111 OperationMetadataError::NonIdempotentRetry,
112 "non-idempotent operation cannot be retry eligible",
113 );
114 assert_display(
115 ResponsePolicyError::UnexpectedContentType,
116 "response content type is not accepted",
117 );
118 assert_display(
119 ResponsePolicyValidationError::MissingSuccessStatus,
120 "response policy has no success status",
121 );
122 assert_display(
123 PreparedExecutionError::<()>::Transport(()),
124 "prepared request transport failed",
125 );
126 assert_display(
127 ActionPollError::Policy("sentinel-secret"),
128 "action poll policy failed",
129 );
130 }
131
132 fn assert_display(error: impl fmt::Display, expected: &str) {
133 let mut output = DisplayBuffer::new();
134 assert!(write!(&mut output, "{error}").is_ok());
135 assert_eq!(output.as_str(), expected);
136 }
137
138 struct DisplayBuffer {
139 bytes: [u8; 128],
140 len: usize,
141 }
142
143 impl DisplayBuffer {
144 const fn new() -> Self {
145 Self {
146 bytes: [0; 128],
147 len: 0,
148 }
149 }
150
151 fn as_str(&self) -> &str {
152 core::str::from_utf8(self.bytes.get(..self.len).unwrap_or_default()).unwrap_or_default()
153 }
154 }
155
156 impl Write for DisplayBuffer {
157 fn write_str(&mut self, value: &str) -> fmt::Result {
158 let end = self.len.checked_add(value.len()).ok_or(fmt::Error)?;
159 let target = self.bytes.get_mut(self.len..end).ok_or(fmt::Error)?;
160 target.copy_from_slice(value.as_bytes());
161 self.len = end;
162 Ok(())
163 }
164 }
165}