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