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