conjure-http 5.11.0

HTTP interfaces for generated Conjure services
Documentation
// Copyright 2025 Palantir Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Implementations for Conjure-generated endpoints.

use std::convert::TryFrom;

use bytes::Bytes;
use conjure_error::Error;
use conjure_object::{Plain, ToPlain};
use futures_core::Stream;
use http::{header::CONTENT_TYPE, HeaderValue, Response, StatusCode};
use serde::de::{DeserializeOwned, IgnoredAny};

use crate::client::{
    AsyncDeserializeResponse, AsyncRequestBody, AsyncSerializeRequest, AsyncWriteBody,
    BoxAsyncWriteBody, BoxLocalAsyncWriteBody, ConjureRuntime, DeserializeResponse, EncodeHeader,
    EncodeParam, LocalAsyncDeserializeResponse, LocalAsyncRequestBody, LocalAsyncSerializeRequest,
    LocalAsyncWriteBody, RequestBody, SerializeRequest, StdResponseDeserializer, WriteBody,
};
use crate::private::APPLICATION_OCTET_STREAM;

/// A body serializer for streaming requests.
pub enum BinaryRequestSerializer {}

impl<'a, T, R> SerializeRequest<'a, T, R> for BinaryRequestSerializer
where
    T: WriteBody<R> + 'a,
{
    fn content_type(_: &ConjureRuntime, _: &T) -> HeaderValue {
        APPLICATION_OCTET_STREAM
    }

    fn serialize(_: &ConjureRuntime, value: T) -> Result<RequestBody<'a, R>, Error> {
        Ok(RequestBody::Streaming(Box::new(value)))
    }
}

impl<'a, T, R> AsyncSerializeRequest<'a, T, R> for BinaryRequestSerializer
where
    T: AsyncWriteBody<R> + Send + 'a,
{
    fn content_type(_: &ConjureRuntime, _: &T) -> HeaderValue {
        APPLICATION_OCTET_STREAM
    }

    fn serialize(_: &ConjureRuntime, value: T) -> Result<AsyncRequestBody<'a, R>, Error> {
        Ok(AsyncRequestBody::Streaming(BoxAsyncWriteBody::new(value)))
    }
}

impl<'a, T, R> LocalAsyncSerializeRequest<'a, T, R> for BinaryRequestSerializer
where
    T: LocalAsyncWriteBody<R> + 'a,
{
    fn content_type(_: &ConjureRuntime, _: &T) -> HeaderValue {
        APPLICATION_OCTET_STREAM
    }

    fn serialize(_: &ConjureRuntime, value: T) -> Result<LocalAsyncRequestBody<'a, R>, Error> {
        Ok(LocalAsyncRequestBody::Streaming(
            BoxLocalAsyncWriteBody::new(value),
        ))
    }
}

/// A body deserializer for collection types.
pub enum CollectionResponseDeserializer {}

impl<T, R> DeserializeResponse<T, R> for CollectionResponseDeserializer
where
    T: DeserializeOwned + Default,
    R: Iterator<Item = Result<Bytes, Error>>,
{
    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
        <StdResponseDeserializer as DeserializeResponse<T, R>>::accept(runtime)
    }

    fn deserialize(runtime: &ConjureRuntime, response: Response<R>) -> Result<T, Error> {
        if response.status() == StatusCode::NO_CONTENT {
            return Ok(T::default());
        }

        <StdResponseDeserializer as DeserializeResponse<T, R>>::deserialize(runtime, response)
    }
}

impl<T, R> AsyncDeserializeResponse<T, R> for CollectionResponseDeserializer
where
    T: DeserializeOwned + Default,
    R: Stream<Item = Result<Bytes, Error>> + Send,
{
    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
        <StdResponseDeserializer as AsyncDeserializeResponse<T, R>>::accept(runtime)
    }

    async fn deserialize(runtime: &ConjureRuntime, response: Response<R>) -> Result<T, Error> {
        if response.status() == StatusCode::NO_CONTENT {
            return Ok(T::default());
        }

        <StdResponseDeserializer as AsyncDeserializeResponse<T, R>>::deserialize(runtime, response)
            .await
    }
}

impl<T, R> LocalAsyncDeserializeResponse<T, R> for CollectionResponseDeserializer
where
    T: DeserializeOwned + Default,
    R: Stream<Item = Result<Bytes, Error>>,
{
    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
        <StdResponseDeserializer as LocalAsyncDeserializeResponse<T, R>>::accept(runtime)
    }

    async fn deserialize(runtime: &ConjureRuntime, response: Response<R>) -> Result<T, Error> {
        if response.status() == StatusCode::NO_CONTENT {
            return Ok(T::default());
        }

        <StdResponseDeserializer as LocalAsyncDeserializeResponse<T, R>>::deserialize(
            runtime, response,
        )
        .await
    }
}

/// A body deserializer for binary types.
pub enum BinaryResponseDeserializer {}

impl<R> DeserializeResponse<R, R> for BinaryResponseDeserializer {
    fn accept(_: &ConjureRuntime) -> Option<HeaderValue> {
        Some(APPLICATION_OCTET_STREAM)
    }

    fn deserialize(_: &ConjureRuntime, response: Response<R>) -> Result<R, Error> {
        if response.headers().get(CONTENT_TYPE) != Some(&APPLICATION_OCTET_STREAM) {
            return Err(Error::internal_safe("invalid response Content-Type"));
        }

        Ok(response.into_body())
    }
}

impl<R> AsyncDeserializeResponse<R, R> for BinaryResponseDeserializer
where
    R: Send,
{
    fn accept(_: &ConjureRuntime) -> Option<HeaderValue> {
        Some(APPLICATION_OCTET_STREAM)
    }

    async fn deserialize(_: &ConjureRuntime, response: Response<R>) -> Result<R, Error> {
        if response.headers().get(CONTENT_TYPE) != Some(&APPLICATION_OCTET_STREAM) {
            return Err(Error::internal_safe("invalid response Content-Type"));
        }

        Ok(response.into_body())
    }
}

impl<R> LocalAsyncDeserializeResponse<R, R> for BinaryResponseDeserializer {
    fn accept(_: &ConjureRuntime) -> Option<HeaderValue> {
        Some(APPLICATION_OCTET_STREAM)
    }

    async fn deserialize(_: &ConjureRuntime, response: Response<R>) -> Result<R, Error> {
        if response.headers().get(CONTENT_TYPE) != Some(&APPLICATION_OCTET_STREAM) {
            return Err(Error::internal_safe("invalid response Content-Type"));
        }

        Ok(response.into_body())
    }
}

/// A body deserializer for optional binary types.
pub enum OptionalBinaryResponseDeserializer {}

impl<R> DeserializeResponse<Option<R>, R> for OptionalBinaryResponseDeserializer {
    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
        <BinaryResponseDeserializer as DeserializeResponse<R, R>>::accept(runtime)
    }

    fn deserialize(runtime: &ConjureRuntime, response: Response<R>) -> Result<Option<R>, Error> {
        if response.status() == StatusCode::NO_CONTENT {
            return Ok(None);
        }

        <BinaryResponseDeserializer as DeserializeResponse<R, R>>::deserialize(runtime, response)
            .map(Some)
    }
}

impl<R> AsyncDeserializeResponse<Option<R>, R> for OptionalBinaryResponseDeserializer
where
    R: Send,
{
    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
        <BinaryResponseDeserializer as AsyncDeserializeResponse<R, R>>::accept(runtime)
    }

    async fn deserialize(
        runtime: &ConjureRuntime,
        response: Response<R>,
    ) -> Result<Option<R>, Error> {
        if response.status() == StatusCode::NO_CONTENT {
            return Ok(None);
        }

        <BinaryResponseDeserializer as AsyncDeserializeResponse<R, R>>::deserialize(
            runtime, response,
        )
        .await
        .map(Some)
    }
}

impl<R> LocalAsyncDeserializeResponse<Option<R>, R> for OptionalBinaryResponseDeserializer {
    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
        <BinaryResponseDeserializer as LocalAsyncDeserializeResponse<R, R>>::accept(runtime)
    }

    async fn deserialize(
        runtime: &ConjureRuntime,
        response: Response<R>,
    ) -> Result<Option<R>, Error> {
        if response.status() == StatusCode::NO_CONTENT {
            return Ok(None);
        }

        <BinaryResponseDeserializer as LocalAsyncDeserializeResponse<R, R>>::deserialize(
            runtime, response,
        )
        .await
        .map(Some)
    }
}

/// A body deserializer for unit types.
pub enum EmptyResponseDeserializer {}

impl<R> DeserializeResponse<(), R> for EmptyResponseDeserializer
where
    R: Iterator<Item = Result<Bytes, Error>>,
{
    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
        <StdResponseDeserializer as DeserializeResponse<(), R>>::accept(runtime)
    }

    fn deserialize(runtime: &ConjureRuntime, response: Response<R>) -> Result<(), Error> {
        if response.status() == StatusCode::NO_CONTENT {
            return Ok(());
        }

        <StdResponseDeserializer as DeserializeResponse<IgnoredAny, R>>::deserialize(
            runtime, response,
        )?;

        Ok(())
    }
}

impl<R> AsyncDeserializeResponse<(), R> for EmptyResponseDeserializer
where
    R: Stream<Item = Result<Bytes, Error>> + Send,
{
    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
        <StdResponseDeserializer as AsyncDeserializeResponse<(), R>>::accept(runtime)
    }

    async fn deserialize(runtime: &ConjureRuntime, response: Response<R>) -> Result<(), Error> {
        if response.status() == StatusCode::NO_CONTENT {
            return Ok(());
        }

        <StdResponseDeserializer as AsyncDeserializeResponse<IgnoredAny, R>>::deserialize(
            runtime, response,
        )
        .await?;

        Ok(())
    }
}

impl<R> LocalAsyncDeserializeResponse<(), R> for EmptyResponseDeserializer
where
    R: Stream<Item = Result<Bytes, Error>>,
{
    fn accept(runtime: &ConjureRuntime) -> Option<HeaderValue> {
        <StdResponseDeserializer as LocalAsyncDeserializeResponse<(), R>>::accept(runtime)
    }

    async fn deserialize(runtime: &ConjureRuntime, response: Response<R>) -> Result<(), Error> {
        if response.status() == StatusCode::NO_CONTENT {
            return Ok(());
        }

        <StdResponseDeserializer as LocalAsyncDeserializeResponse<IgnoredAny, R>>::deserialize(
            runtime, response,
        )
        .await?;

        Ok(())
    }
}

/// An encoder which converts values via their `Plain` implementation.
pub enum PlainEncoder {}

impl<T> EncodeParam<T> for PlainEncoder
where
    T: Plain,
{
    fn encode(_: &ConjureRuntime, value: T) -> Result<Vec<String>, Error> {
        Ok(vec![value.to_plain()])
    }
}

impl<T> EncodeHeader<T> for PlainEncoder
where
    T: Plain,
{
    fn encode(_: &ConjureRuntime, value: T) -> Result<Vec<HeaderValue>, Error> {
        HeaderValue::try_from(value.to_plain())
            .map_err(Error::internal_safe)
            .map(|v| vec![v])
    }
}

/// An encoder which converts a sequence of values via their individual `Plain` implementations.
pub enum PlainSeqEncoder {}

impl<T, U> EncodeParam<T> for PlainSeqEncoder
where
    T: IntoIterator<Item = U>,
    U: Plain,
{
    fn encode(_: &ConjureRuntime, value: T) -> Result<Vec<String>, Error> {
        Ok(value.into_iter().map(|v| v.to_plain()).collect())
    }
}

impl<T, U> EncodeHeader<T> for PlainSeqEncoder
where
    T: IntoIterator<Item = U>,
    U: Plain,
{
    fn encode(_: &ConjureRuntime, value: T) -> Result<Vec<HeaderValue>, Error> {
        value
            .into_iter()
            .map(|v| HeaderValue::try_from(v.to_plain()).map_err(Error::internal_safe))
            .collect()
    }
}