// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT.
/// Auth scheme resolver trait specific to Amazon Simple Storage Service
pub trait ResolveAuthScheme: ::std::marker::Send + ::std::marker::Sync + ::std::fmt::Debug {
    /// Resolve a priority list of auth scheme options with the given parameters
    fn resolve_auth_scheme<'a>(
        &'a self,
        params: &'a crate::config::auth::Params,
        cfg: &'a ::aws_smithy_types::config_bag::ConfigBag,
        runtime_components: &'a ::aws_smithy_runtime_api::client::runtime_components::RuntimeComponents,
    ) -> ::aws_smithy_runtime_api::client::auth::AuthSchemeOptionsFuture<'a>;

    /// Convert this service-specific resolver into a `SharedAuthSchemeOptionResolver`
    fn into_shared_resolver(self) -> ::aws_smithy_runtime_api::client::auth::SharedAuthSchemeOptionResolver
    where
        Self: ::std::marker::Sized + 'static,
    {
        ::aws_smithy_runtime_api::client::auth::SharedAuthSchemeOptionResolver::new(DowncastParams(self))
    }
}

#[derive(Debug)]
struct DowncastParams<T>(T);
impl<T> ::aws_smithy_runtime_api::client::auth::ResolveAuthSchemeOptions for DowncastParams<T>
where
    T: ResolveAuthScheme,
{
    fn resolve_auth_scheme_options_v2<'a>(
        &'a self,
        params: &'a ::aws_smithy_runtime_api::client::auth::AuthSchemeOptionResolverParams,
        cfg: &'a ::aws_smithy_types::config_bag::ConfigBag,
        runtime_components: &'a ::aws_smithy_runtime_api::client::runtime_components::RuntimeComponents,
    ) -> ::aws_smithy_runtime_api::client::auth::AuthSchemeOptionsFuture<'a> {
        match params.get::<crate::config::auth::Params>() {
            ::std::option::Option::Some(concrete_params) => self.0.resolve_auth_scheme(concrete_params, cfg, runtime_components),
            ::std::option::Option::None => ::aws_smithy_runtime_api::client::auth::AuthSchemeOptionsFuture::ready(::std::result::Result::Err(
                "params of expected type was not present".into(),
            )),
        }
    }
}

/// The default auth scheme resolver
#[derive(Debug)]
#[allow(dead_code)]
pub struct DefaultAuthSchemeResolver {
    service_defaults: Vec<::aws_smithy_runtime_api::client::auth::AuthSchemeOption>,
    operation_overrides: ::std::collections::HashMap<&'static str, Vec<::aws_smithy_runtime_api::client::auth::AuthSchemeOption>>,
}

// TODO(https://github.com/smithy-lang/smithy-rs/issues/4177): Remove `allow(...)` once the issue is addressed.
// When generating code for tests (e.g., `codegen-client-test`), this manual implementation
// of the `Default` trait may appear as if it could be derived automatically.
// However, that is not the case in production.
#[allow(clippy::derivable_impls)]
impl Default for DefaultAuthSchemeResolver {
    fn default() -> Self {
        Self {
            service_defaults: vec![
                ::aws_smithy_runtime_api::client::auth::AuthSchemeOption::builder()
                    .scheme_id(::aws_runtime::auth::sigv4::SCHEME_ID)
                    .build()
                    .expect("required fields set"),
                #[cfg(feature = "sigv4a")]
                {
                    ::aws_smithy_runtime_api::client::auth::AuthSchemeOption::builder()
                        .scheme_id(::aws_runtime::auth::sigv4a::SCHEME_ID)
                        .build()
                        .expect("required fields set")
                },
                ::aws_smithy_runtime_api::client::auth::AuthSchemeOption::from(::aws_smithy_runtime::client::auth::no_auth::NO_AUTH_SCHEME_ID),
            ],
            operation_overrides: [(
                "WriteGetObjectResponse",
                vec![::aws_smithy_runtime_api::client::auth::AuthSchemeOption::builder()
                    .scheme_id(::aws_runtime::auth::sigv4::SCHEME_ID)
                    .properties({
                        let mut layer = ::aws_smithy_types::config_bag::Layer::new("WriteGetObjectResponseAuthOptionProperties");
                        layer.store_put(::aws_runtime::auth::PayloadSigningOverride::unsigned_payload());
                        layer.freeze()
                    })
                    .build()
                    .expect("required fields set")],
            )]
            .into(),
        }
    }
}

impl crate::config::auth::ResolveAuthScheme for DefaultAuthSchemeResolver {
    fn resolve_auth_scheme<'a>(
        &'a self,
        params: &'a crate::config::auth::Params,
        _cfg: &'a ::aws_smithy_types::config_bag::ConfigBag,
        _runtime_components: &'a ::aws_smithy_runtime_api::client::runtime_components::RuntimeComponents,
    ) -> ::aws_smithy_runtime_api::client::auth::AuthSchemeOptionsFuture<'a> {
        let operation_name = params.operation_name();

        let modeled_auth_options = match self.operation_overrides.get(operation_name) {
            Some(overrides) => overrides,
            None => &self.service_defaults,
        };

        let _fut = ::aws_smithy_runtime_api::client::auth::AuthSchemeOptionsFuture::ready(Ok(modeled_auth_options.clone()));

        let _fut = ::aws_smithy_runtime_api::client::auth::AuthSchemeOptionsFuture::new(async move {
            crate::endpoint_auth::resolve_endpoint_based_auth_scheme_options(modeled_auth_options, _cfg, _runtime_components).await
        });

        _fut
    }
}

/// Configuration parameters for resolving the correct auth scheme
#[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::fmt::Debug)]
pub struct Params {
    operation_name: ::std::borrow::Cow<'static, str>,
}
impl Params {
    /// Create a builder for [`Params`]
    pub fn builder() -> crate::config::auth::ParamsBuilder {
        crate::config::auth::ParamsBuilder::default()
    }

    /// Return the operation name for [`Params`]
    pub fn operation_name(&self) -> &str {
        self.operation_name.as_ref()
    }
}

#[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::default::Default, ::std::fmt::Debug)]
/// Builder for [`Params`]
pub struct ParamsBuilder {
    operation_name: ::std::option::Option<::std::borrow::Cow<'static, str>>,
}
impl ParamsBuilder {
    /// Set the operation name for the builder
    pub fn operation_name(self, operation_name: impl Into<::std::borrow::Cow<'static, str>>) -> Self {
        self.set_operation_name(::std::option::Option::Some(operation_name.into()))
    }

    /// Set the operation name for the builder
    pub fn set_operation_name(mut self, operation_name: ::std::option::Option<::std::borrow::Cow<'static, str>>) -> Self {
        self.operation_name = operation_name;
        self
    }
    /// Consume this builder, create [`Params`]."
    ///
    /// Return [`BuildError`] if any of the required fields are unset.
    ///
    pub fn build(self) -> ::std::result::Result<crate::config::auth::Params, crate::config::auth::BuildError> {
        ::std::result::Result::Ok(crate::config::auth::Params {
            operation_name: self.operation_name.ok_or_else(|| BuildError::missing("operation_name"))?,
        })
    }
}

/// An error that occurred while constructing `config::auth::Params`
#[derive(Debug)]
pub struct BuildError {
    field: ::std::borrow::Cow<'static, str>,
}

impl BuildError {
    fn missing(field: &'static str) -> Self {
        Self { field: field.into() }
    }
}

impl std::fmt::Display for BuildError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "a required field was missing: `{}`", self.field)
    }
}

impl std::error::Error for BuildError {}
