1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use std::{
    borrow::Cow,
    fmt::{self, Debug, Formatter},
    marker::PhantomData,
};

use prost::Message as PbMsg;

use crate::{
    body::Body, common::extensions::Extensions, decode::*, encode::encode_protobuf_message,
};

/// Request parts.
#[non_exhaustive]
#[derive(Debug)]
pub struct Parts {
    /// Body of a request.
    pub body: Body,
    /// Extensions of a request.
    pub extensions: Extensions,
    /// Endpoint of a request.
    pub endpoint: Cow<'static, str>,
}

impl<T> From<Request<T>> for Parts {
    fn from(req: Request<T>) -> Self {
        req.parts
    }
}

impl<T> From<Parts> for Request<T> {
    fn from(parts: Parts) -> Self {
        Self {
            parts,
            message: PhantomData,
        }
    }
}

/// A hRPC request.
pub struct Request<T> {
    parts: Parts,
    message: PhantomData<T>,
}

impl<T> Debug for Request<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.debug_struct("Request")
            .field("body", &self.parts.body)
            .field("extensions", &self.parts.extensions)
            .field("endpoint", &self.parts.endpoint)
            .finish()
    }
}

impl Request<()> {
    /// Create a request with an empty body.
    ///
    /// This is useful for hRPC socket requests, since they don't send any messages.
    pub fn empty() -> Request<()> {
        Self::new_with_body(Body::empty())
    }
}

impl<T> Request<T> {
    /// Creates a new request using the provided body.
    pub fn new_with_body(body: Body) -> Self {
        Self {
            parts: Parts {
                body,
                extensions: Extensions::new(),
                endpoint: Cow::Borrowed(""),
            },
            message: PhantomData,
        }
    }

    /// Get a mutable reference to the extensions of this response.
    #[inline]
    pub fn extensions_mut(&mut self) -> &mut Extensions {
        &mut self.parts.extensions
    }

    /// Get an immutable reference to the extensions of this response.
    #[inline]
    pub fn extensions(&self) -> &Extensions {
        &self.parts.extensions
    }

    /// Get a mutable reference to the endpoint of this response.
    #[inline]
    pub fn endpoint_mut(&mut self) -> &mut Cow<'static, str> {
        &mut self.parts.endpoint
    }

    /// Get an immutable reference to the endpoint of this response.
    #[inline]
    pub fn endpoint(&self) -> &str {
        self.parts.endpoint.as_ref()
    }

    #[allow(dead_code)]
    pub(crate) fn map<M>(self) -> Request<M> {
        Request {
            parts: self.parts,
            message: PhantomData,
        }
    }
}

impl<T: PbMsg> Request<T> {
    /// Create a new request with the specified message.
    pub fn new(msg: &T) -> Self {
        let encoded = encode_protobuf_message(msg).freeze();
        Self::new_with_body(Body::full(encoded))
    }
}

impl<T: PbMsg + Default> Request<T> {
    /// Extract the body from the request and decode it into the message.
    #[inline]
    pub async fn into_message(self) -> Result<T, DecodeBodyError> {
        decode_body(self.parts.body).await
    }
}

/// Trait used for blanket impls on generated protobuf types.
pub trait IntoRequest<T> {
    /// Convert this to a hRPC request.
    fn into_request(self) -> Request<T>;
}

impl<T: PbMsg> IntoRequest<T> for T {
    fn into_request(self) -> Request<Self> {
        Request::new(&self)
    }
}

impl<T> IntoRequest<T> for Request<T> {
    fn into_request(self) -> Request<T> {
        self
    }
}

/// A request that has a message type of `()`. Used in places where the
/// request message type is not important.
pub type BoxRequest = Request<()>;