cloud-sdk-reqwest 0.32.4

Optional provider-neutral reqwest transport boundary for cloud-sdk.
Documentation
use cloud_sdk::authentication::{AuthenticationScopePolicy, ScopeRequirement};
use cloud_sdk::operation::{
    AttemptBudget, CheckedResponseGuard, ContentTypePolicy, CostIntent, MutationPermit,
    OperationImpact, OperationMetadata, PermitClock, PermitContext, PermitTimestamp,
    PermitValidity, PlanChange, PlanConfirmation, PlanFingerprintScope, PreparedExecutionError,
    PreparedRequest, ProviderService, ReplayPolicy, RequestIdPolicy, RequestSemantics,
    ResponseBodyPolicy, ResponsePolicy, RetryEligibility, build_canonical_plan,
};
use cloud_sdk::transport::{
    BoundTransport, EndpointIdentity, EndpointPolicy, HeaderName, MediaType, RawResponsePolicy,
    ResponseMediaPolicy, StatusCode, TransportRequest,
};

use super::super::{
    AsyncClient, BearerCredential, BearerCredentialScope, BearerToken, HttpsEndpoint,
    TransportError,
};

pub(super) fn test_credential(token: BearerToken, endpoint: &HttpsEndpoint) -> BearerCredential {
    BearerCredential::new(
        token,
        BearerCredentialScope::new(
            cloud_sdk::provider_id!("example"),
            cloud_sdk::service_id!("compute"),
            endpoint.clone(),
        ),
    )
}

pub(super) fn prepared<'request>(
    client: &'request AsyncClient,
    request: TransportRequest<'request>,
) -> PreparedRequest<'request> {
    let endpoint = client
        .endpoint_identity()
        .unwrap_or_else(|_| unreachable!());
    prepared_with_policy(
        request,
        ProviderService::new(
            cloud_sdk::provider_id!("example"),
            cloud_sdk::service_id!("compute"),
            EndpointPolicy::fixed(endpoint),
        ),
        test_authentication_policy(endpoint),
    )
}

pub(super) fn prepared_with_policy<'request>(
    request: TransportRequest<'request>,
    service: ProviderService<'request>,
    authentication: AuthenticationScopePolicy<'request>,
) -> PreparedRequest<'request> {
    let direct = matches!(request.method().as_str(), "GET" | "HEAD");
    let metadata = OperationMetadata::new(
        if direct {
            OperationImpact::ReadOnly
        } else {
            OperationImpact::Mutation
        },
        if direct {
            RequestSemantics::Safe
        } else {
            RequestSemantics::NonIdempotent
        },
        RetryEligibility::Never,
        CostIntent::NoKnownCost,
        RequestIdPolicy::Discard,
    )
    .unwrap_or_else(|_| unreachable!());
    let response = ResponsePolicy::new(
        &[StatusCode::OK],
        ContentTypePolicy::Optional(&[MediaType::JSON]),
        ResponseBodyPolicy::Optional,
        8192,
    )
    .unwrap_or_else(|_| unreachable!());
    PreparedRequest::new(
        request,
        service,
        metadata,
        response,
        authentication,
        test_raw_response_policy(),
    )
    .unwrap_or_else(|_| unreachable!())
    .with_operation_id(cloud_sdk::operation_id!("reqwest_transport_test"))
}

pub(super) async fn execute_test<'request, 'buffer>(
    client: &'request AsyncClient,
    request: TransportRequest<'request>,
    output: &'buffer mut [u8],
    headers: &'buffer mut [u8],
) -> Result<CheckedResponseGuard<'buffer>, TransportError> {
    let direct = matches!(request.method().as_str(), "GET" | "HEAD");
    let prepared = prepared(client, request);
    if direct {
        return prepared
            .execute_async(client, output, headers)
            .await
            .map_err(map_execution_error);
    }

    let endpoint = client
        .endpoint_identity()
        .map_err(|_| TransportError::ResponseCommitFailed)?;
    let plan = PlanConfirmation::new(
        prepared,
        endpoint,
        PlanFingerprintScope::Value(b"test-account"),
        PlanFingerprintScope::Absent,
        PermitContext::new(b"reqwest transport test")
            .map_err(|_| TransportError::ResponseCommitFailed)?,
        PermitValidity::new(time(100), time(200))
            .map_err(|_| TransportError::ResponseCommitFailed)?,
        ReplayPolicy::SingleAttempt,
        AttemptBudget::new(1).map_err(|_| TransportError::ResponseCommitFailed)?,
        PlanChange::ChangesState,
        None,
        None,
    );
    let mut fingerprint_storage = [0_u8; 32_768];
    let fingerprint = build_canonical_plan(plan, &mut fingerprint_storage)
        .map_err(|_| TransportError::ResponseCommitFailed)?;
    let mut permit = MutationPermit::new(fingerprint.subject(), time(100))
        .map_err(|_| TransportError::ResponseCommitFailed)?;
    let attempt = permit
        .begin(time(101))
        .map_err(|_| TransportError::ResponseCommitFailed)?;
    attempt
        .execute_async(&FixedClock, client, output, headers)
        .await
        .map_err(|error| match error.execution() {
            PreparedExecutionError::Transport(failure) => *failure.error(),
            _ => TransportError::ResponseCommitFailed,
        })
}

fn map_execution_error(
    error: PreparedExecutionError<super::super::AuthenticatedTransportFailure>,
) -> TransportError {
    match error {
        PreparedExecutionError::Transport(failure) => failure.into_error(),
        _ => TransportError::ResponseCommitFailed,
    }
}

struct FixedClock;

impl PermitClock for FixedClock {
    fn now(&self) -> PermitTimestamp {
        time(102)
    }
}

const fn time(value: u64) -> PermitTimestamp {
    PermitTimestamp::from_seconds(value)
}

pub(super) fn test_raw_response_policy() -> RawResponsePolicy<'static> {
    let names = [
        "content-type",
        "ratelimit-limit",
        "ratelimit-remaining",
        "ratelimit-reset",
    ];
    let headers = names.map(|name| HeaderName::new(name).unwrap_or_else(|_| std::process::abort()));
    RawResponsePolicy::new(
        8192,
        8192,
        ResponseMediaPolicy::Optional(&[MediaType::JSON]),
        ResponseMediaPolicy::Optional(&[MediaType::JSON]),
        &headers,
        8,
    )
    .unwrap_or_else(|_| std::process::abort())
}

const fn test_authentication_policy(
    endpoint: EndpointIdentity<'_>,
) -> AuthenticationScopePolicy<'_> {
    AuthenticationScopePolicy::new(
        ScopeRequirement::Required(cloud_sdk::provider_id!("example")),
        ScopeRequirement::Required(cloud_sdk::service_id!("compute")),
        ScopeRequirement::Required(endpoint),
        ScopeRequirement::Forbidden,
        ScopeRequirement::Forbidden,
        ScopeRequirement::Forbidden,
    )
}