1#![doc = include_str!("../README.md")]
2
3use prost::{EncodeError, Message};
4use async_trait::async_trait;
5use reqwest::{RequestBuilder, Response};
6use thiserror::Error;
7
8pub trait ProtobufRequestExt where Self: Sized {
10 fn accept_protobuf(self) -> Self;
13
14 fn protobuf<T: Message + Default>(self, value: T) -> Result<Self, EncodeError>;
17}
18
19#[derive(Debug, Error)]
21pub enum DecodeError {
22 #[error("Failed to extract body bytes: {0}")]
24 Reqwest(#[from] reqwest::Error),
25 #[error("Failed to decode protobuf: {0:?}")]
27 ProstDecode(prost::DecodeError)
28}
29
30impl From<prost::DecodeError> for DecodeError {
31 fn from(x: prost::DecodeError) -> Self {
32 Self::ProstDecode(x)
33 }
34}
35
36
37#[async_trait]
39pub trait ProtobufResponseExt {
40 async fn protobuf<T: Message + Default>(self) -> Result<T, DecodeError>;
42}
43
44impl ProtobufRequestExt for RequestBuilder {
45 fn accept_protobuf(self) -> Self {
46 self.header("Accept", "application/protobuf")
47 }
48
49 fn protobuf<T: Message + Default>(self, value: T) -> Result<Self, EncodeError> {
50 let mut buf = Vec::new();
51 value.encode(&mut buf)?;
52 let this = self.header("Content-Type", "application/protobuf");
53 Ok(this.body(buf))
54 }
55}
56
57#[async_trait]
58impl ProtobufResponseExt for Response {
59 async fn protobuf<T: Message + Default>(self) -> Result<T, DecodeError> {
60 let body = self.bytes().await?;
61 let decoded = T::decode(body)?;
62 Ok(decoded)
63 }
64}