Skip to main content

wireman_core/client/
mod.rs

1#![allow(clippy::module_name_repetitions)]
2pub(crate) mod codec;
3pub mod reflection;
4pub mod tls;
5
6use crate::descriptor::response::StreamingResponse;
7use crate::descriptor::RequestMessage;
8use crate::descriptor::ResponseMessage;
9use crate::error::Error;
10use crate::Result;
11use tls::TlsConfig;
12use tonic::transport::Uri;
13use tonic::{client::Grpc, transport::Channel};
14
15/// Represents a `gRPC` client for making RPC calls.
16#[derive(Clone, Debug)]
17pub struct GrpcClient {
18    grpc: Grpc<Channel>,
19}
20
21impl GrpcClient {
22    /// Returns a new Grpc Client. if no tls is given, the standard tonic
23    /// client is used.
24    ///
25    /// # Errors
26    ///
27    /// Errors if tls config cannot be build.
28    pub fn new<T: Into<Uri>>(uri: T, tls_config: Option<TlsConfig>) -> Result<Self> {
29        let builder = Channel::builder(uri.into());
30
31        let channel = if let Some(tls_config) = tls_config {
32            builder.tls_config(tls_config.0)?.connect_lazy()
33        } else {
34            builder.connect_lazy()
35        };
36
37        Ok(GrpcClient {
38            grpc: Grpc::new(channel),
39        })
40    }
41
42    /// Make a unary `gRPC` call.
43    ///
44    /// # Errors
45    /// - `gRPC` client is not ready
46    /// - Server call failed
47    pub async fn unary(&mut self, request: &RequestMessage) -> Result<ResponseMessage> {
48        self.grpc.ready().await.map_err(Error::GrpcNotReady)?;
49
50        let path = request.path();
51        let codec = request.codec();
52
53        let request = request.clone().into();
54        let response = self.grpc.unary(request, path, codec).await?;
55
56        Ok(response.into_inner())
57    }
58
59    /// Make a server-streaming `gRPC` call.
60    ///
61    /// # Errors
62    /// - `gRPC` client is not ready
63    /// - Server call failed
64    pub async fn server_streaming(
65        &mut self,
66        request: &RequestMessage,
67    ) -> Result<StreamingResponse> {
68        self.grpc.ready().await.map_err(Error::GrpcNotReady)?;
69
70        let path = request.path();
71        let codec = request.codec();
72
73        let request = request.clone().into();
74        let response = self.grpc.server_streaming(request, path, codec).await?;
75
76        Ok(StreamingResponse::new(response.into_inner()))
77    }
78
79    /// Make a client-streaming `gRPC` call.
80    ///
81    /// # Errors
82    /// - `gRPC` client is not ready
83    /// - Server call failed
84    pub async fn client_streaming<S>(
85        &mut self,
86        head: &RequestMessage,
87        messages: S,
88    ) -> Result<ResponseMessage>
89    where
90        S: tokio_stream::Stream<Item = RequestMessage> + Send + 'static,
91    {
92        self.grpc.ready().await.map_err(Error::GrpcNotReady)?;
93
94        let path = head.path();
95        let codec = head.codec();
96
97        let request = into_streaming_request(head, messages);
98        let response = self.grpc.client_streaming(request, path, codec).await?;
99
100        Ok(response.into_inner())
101    }
102
103    /// Make a bidirectional-streaming `gRPC` call.
104    ///
105    /// # Errors
106    /// - `gRPC` client is not ready
107    /// - Server call failed
108    pub async fn bidirectional_streaming<S>(
109        &mut self,
110        head: &RequestMessage,
111        messages: S,
112    ) -> Result<StreamingResponse>
113    where
114        S: tokio_stream::Stream<Item = RequestMessage> + Send + 'static,
115    {
116        self.grpc.ready().await.map_err(Error::GrpcNotReady)?;
117
118        let path = head.path();
119        let codec = head.codec();
120
121        let request = into_streaming_request(head, messages);
122        let response = self.grpc.streaming(request, path, codec).await?;
123
124        Ok(StreamingResponse::new(response.into_inner()))
125    }
126}
127
128/// Builds a streaming `tonic` request from a message stream, applying the
129/// metadata of the `head` message to the outgoing request.
130fn into_streaming_request<S>(head: &RequestMessage, messages: S) -> tonic::Request<S>
131where
132    S: tokio_stream::Stream<Item = RequestMessage>,
133{
134    let metadata = head.metadata().clone();
135    let mut req = tonic::Request::new(messages);
136    if let Some(metadata) = metadata {
137        *req.metadata_mut() = metadata.inner;
138    }
139    req
140}
141
142/// Creates a new `gRPC` client and sends a message to a `gRPC` server.
143/// This method is async.
144///
145/// # Errors
146/// - Internal error calling the `gRPC` server
147pub async fn call_unary_async(
148    request: &RequestMessage,
149    tls: Option<TlsConfig>,
150) -> Result<ResponseMessage> {
151    let uri = request.uri()?;
152
153    let mut client = GrpcClient::new(uri, tls)?;
154
155    client.unary(request).await
156}
157
158/// Creates a new `gRPC` client and sends a message to a `gRPC` server.
159/// This method is async.
160///
161/// # Errors
162/// - Internal error calling the `gRPC` server
163pub async fn call_server_streaming(
164    req: &RequestMessage,
165    tls: Option<TlsConfig>,
166) -> Result<StreamingResponse> {
167    let uri = req.uri()?;
168
169    let mut client = GrpcClient::new(uri, tls)?;
170
171    client.server_streaming(req).await
172}
173
174/// Creates a new `gRPC` client and streams messages to a `gRPC` server,
175/// returning a single response (client-streaming RPC).
176///
177/// # Errors
178/// - Internal error calling the `gRPC` server
179pub async fn call_client_streaming<S>(
180    head: &RequestMessage,
181    messages: S,
182    tls: Option<TlsConfig>,
183) -> Result<ResponseMessage>
184where
185    S: tokio_stream::Stream<Item = RequestMessage> + Send + 'static,
186{
187    let uri = head.uri()?;
188
189    let mut client = GrpcClient::new(uri, tls)?;
190
191    client.client_streaming(head, messages).await
192}
193
194/// Creates a new `gRPC` client and streams messages to a `gRPC` server,
195/// returning a stream of responses (bidirectional-streaming RPC).
196///
197/// # Errors
198/// - Internal error calling the `gRPC` server
199pub async fn call_bidirectional_streaming<S>(
200    head: &RequestMessage,
201    messages: S,
202    tls: Option<TlsConfig>,
203) -> Result<StreamingResponse>
204where
205    S: tokio_stream::Stream<Item = RequestMessage> + Send + 'static,
206{
207    let uri = head.uri()?;
208
209    let mut client = GrpcClient::new(uri, tls)?;
210
211    client.bidirectional_streaming(head, messages).await
212}