Skip to main content

cloud_sdk/
lib.rs

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}