# AFIT Case Study: AWS SDK
https://docs.rs/aws-credential-types/0.54.1/aws_credential_types/provider/trait.ProvideCredentials.html
https://docs.rs/aws-smithy-async/0.54.3/aws_smithy_async/rt/sleep/trait.AsyncSleep.html
https://docs.rs/aws-config/0.54.1/aws_config/meta/region/trait.ProvideRegion.html
Question this doc answers: what features do we need to meet the needs of AWS SDK from async traits? What works well, what can we workaround, and where are the gaps relative to the current state on nightly.
## TL;DR
* Conclusion:
* given send bounds, the current support for async traits would be sufficient for ergonomic use in the AWS SDK*
* Workarounds:
* erased dyn trait, but it's hidden from clients
## Where would AWS SDK like to use async traits, and how do they solve it today?
Cover `ProvideCredentials`
List other examples and say they are similar, and so we ignore them.
## How would this work with async traits as proposed?
### From a client's perspective
```rust
impl SomeTrait for MyType {
async fn();
}
builder
.with_new(MyType);
```
### How it's implemented
* send bounds, need for that
* dyn dispatch, erasure
### Dyn dispatch as first-class feature
* no real complications here
## Variant: with async syntax
## Provide Credentials
Trait for creating custom credential providers; anything that gives AWS credentials, could be from disk, but also from servers. These are async because they may make HTTP requests and so forth under the hood.
### Today
In the library:
```rust
pub trait ProvideCredentials: Send + Sync + Debug {
fn provide_credentials<'a>(&'a self) -> ProvideCredentials<'a>
where
Self: 'a;
}
pub struct ProvideCredentials<'a>(..);
impl<'a> ProvideCredentials<'a> {
pub fn new(future: impl Future<Output = Result> + Send + 'a) -> Self;
}
// https://docs.rs/aws-config/0.54.1/aws_config/struct.ConfigLoader.html#method.credentials_provider
impl ConfigLoader {
pub fn credentials_provider(
self,
credentials_provider: impl ProvideCredentials + 'static
) -> Self;
}
```
In user's code:
```rust
impl ProvideCredentials for MyProvider {
fn provide_credentials<'a>(&'a self) -> ProvideCredentials<'a>
where
Self: 'a
{
ProvideCredentials::new(async move {
...
})
}
}
```
related PR: https://github.com/awslabs/smithy-rs/pull/1359
then later
```rust
let config_loader: SdkConfig = ConfigLoader::new()
.credentials_provider(MyProvider)
.load()
.await;
```
* Credentials provider is embedded in the `SdkConfig`
* in case credentials expire
* Needs to be sendable across threads for multithreaded users
### Future
```rust
pub type Result = std::result::Result<Credentials, error::CredentialsError>;
pub trait ProvideCredentials: Send + Sync + Debug {
async fn provide_credentials<'a>(&'a self) -> Result
where
Self: 'a;
}
// https://docs.rs/aws-config/0.54.1/aws_config/struct.ConfigLoader.html#method.credentials_provider
impl ConfigLoader {
pub fn credentials_provider(
self,
credentials_provider: impl ProvideCredentials + 'static
) -> Self {
let b: Box<dyn ProvideCredentialsDyn> = Box::new(credentials_provider);
self.field = b;
self
}
}
// Private to your library
trait ProvideCredentialsDyn {
fn provide_credentials<'a>(&'a self) -> Box<dyn Future<Output = Result> + Send>
where
Self: 'a;
}
impl<T> ProvideCredentialsDyn for T
where
T: ProvideCredentials<credentials_provider(..): Send>,
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// needed because we have to say that the future is Send so we can create the future
{
fn provide_credentials<'a>(&'a self) -> Box<dyn Future<Output = Result> + Send>
where
Self: 'a
{
Box::new(ProvideCredentials::provide_credentials(self))
}
}
```
##
https://docs.rs/aws-smithy-async/0.54.3/aws_smithy_async/rt/sleep/trait.AsyncSleep.html
```rust
pub trait AsyncSleep: Debug + Send + Sync {
// Required method
fn sleep(&self, duration: Duration) -> Sleep ⓘ;
}
```
https://docs.rs/aws-config/0.54.1/aws_config/meta/region/trait.ProvideRegion.html
is similar
##
https://docs.rs/aws-smithy-http/0.33.1/aws_smithy_http/body/struct.SdkBody.html
https://docs.rs/http-body/0.4.5/http_body/trait.Body.html
https://docs.rs/aws-smithy-http/latest/src/aws_smithy_http/byte_stream.rs.html#441
## want private impls
currently workaround with this `Inner` type so that other people can't rely on having implemented thin
##
using stream in public API for event streams
https://docs.rs/aws-smithy-http/latest/src/aws_smithy_http/byte_stream.rs.html#541
```rust
impl<B> AsyncIterator for Inner<B>
where
B: http_body::Body<Data = Bytes>,
{
type Item = Result<Bytes, B::Error>;
async fn next(&mut self, {
self.project().body.poll_data(cx)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let size_hint = http_body::Body::size_hint(&self.body);
let lower = size_hint.lower().try_into();
let upper = size_hint.upper().map(|u| u.try_into()).transpose();
match (lower, upper) {
(Ok(lower), Ok(upper)) => (lower, upper),
(Err(_), _) | (_, Err(_)) => {
panic!("{}", SIZE_HINT_32_BIT_PANIC_MESSAGE)
}
}
}
}
```
https://docs.rs/aws-smithy-http/latest/aws_smithy_http/event_stream/struct.EventStreamSender.html#impl-From%3CS%3E-for-EventStreamSender%3CT,+E%3E
https://docs.rs/aws-sdk-s3/0.24.0/aws_sdk_s3/paginator/struct.ListObjectsV2Paginator.html#method.send
### ff
```rust
async trait ProvideCredentials {
async fn ...
}
```
## other relevant features
* private impls
* [trait aliases](https://docs.rs/aws-smithy-client/latest/src/aws_smithy_client/bounds.rs.html)
* [use](https://docs.rs/aws-smithy-client/latest/src/aws_smithy_client/lib.rs.html#168)