Skip to content

Commit 4bb087b

Browse files
alceLucioFranco
authored andcommitted
feat: Add IntoRequest and IntoStreamingRequest traits (#66)
1 parent af5754b commit 4bb087b

File tree

3 files changed

+142
-17
lines changed

3 files changed

+142
-17
lines changed

tonic-build/src/client.rs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,14 @@ fn generate_unary(method: &Method, proto: &str, path: String) -> TokenStream {
9999
let (request, response) = crate::replace_wellknown(proto, &method);
100100

101101
quote! {
102-
pub async fn #ident(&mut self, request: tonic::Request<#request>)
103-
-> Result<tonic::Response<#response>, tonic::Status> {
102+
pub async fn #ident(
103+
&mut self,
104+
request: impl tonic::IntoRequest<#request>,
105+
) -> Result<tonic::Response<#response>, tonic::Status> {
104106
self.ready().await?;
105107
let codec = tonic::codec::ProstCodec::new();
106108
let path = http::uri::PathAndQuery::from_static(#path);
107-
self.inner.unary(request, path, codec).await
109+
self.inner.unary(request.into_request(), path, codec).await
108110
}
109111
}
110112
}
@@ -115,12 +117,14 @@ fn generate_server_streaming(method: &Method, proto: &str, path: String) -> Toke
115117
let (request, response) = crate::replace_wellknown(proto, &method);
116118

117119
quote! {
118-
pub async fn #ident(&mut self, request: tonic::Request<#request>)
119-
-> Result<tonic::Response<tonic::codec::Streaming<#response>>, tonic::Status> {
120+
pub async fn #ident(
121+
&mut self,
122+
request: impl tonic::IntoRequest<#request>,
123+
) -> Result<tonic::Response<tonic::codec::Streaming<#response>>, tonic::Status> {
120124
self.ready().await?;
121125
let codec = tonic::codec::ProstCodec::new();
122126
let path = http::uri::PathAndQuery::from_static(#path);
123-
self.inner.server_streaming(request, path, codec).await
127+
self.inner.server_streaming(request.into_request(), path, codec).await
124128
}
125129
}
126130
}
@@ -131,14 +135,14 @@ fn generate_client_streaming(method: &Method, proto: &str, path: String) -> Toke
131135
let (request, response) = crate::replace_wellknown(proto, &method);
132136

133137
quote! {
134-
pub async fn #ident<S>(&mut self, request: tonic::Request<S>)
135-
-> Result<tonic::Response<#response>, tonic::Status>
136-
where S: Stream<Item = #request> + Send + 'static,
137-
{
138+
pub async fn #ident(
139+
&mut self,
140+
request: impl tonic::IntoStreamingRequest<Message = #request>
141+
) -> Result<tonic::Response<#response>, tonic::Status> {
138142
self.ready().await?;
139143
let codec = tonic::codec::ProstCodec::new();
140144
let path = http::uri::PathAndQuery::from_static(#path);
141-
self.inner.client_streaming(request, path, codec).await
145+
self.inner.client_streaming(request.into_streaming_request(), path, codec).await
142146
}
143147
}
144148
}
@@ -149,14 +153,14 @@ fn generate_streaming(method: &Method, proto: &str, path: String) -> TokenStream
149153
let (request, response) = crate::replace_wellknown(proto, &method);
150154

151155
quote! {
152-
pub async fn #ident<S>(&mut self, request: tonic::Request<S>)
153-
-> Result<tonic::Response<tonic::codec::Streaming<#response>>, tonic::Status>
154-
where S: Stream<Item = #request> + Send + 'static,
155-
{
156+
pub async fn #ident(
157+
&mut self,
158+
request: impl tonic::IntoStreamingRequest<Message = #request>
159+
) -> Result<tonic::Response<tonic::codec::Streaming<#response>>, tonic::Status> {
156160
self.ready().await?;
157161
let codec = tonic::codec::ProstCodec::new();
158162
let path = http::uri::PathAndQuery::from_static(#path);
159-
self.inner.streaming(request, path, codec).await
163+
self.inner.streaming(request.into_streaming_request(), path, codec).await
160164
}
161165
}
162166
}

tonic/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ pub use async_trait::async_trait;
9898

9999
#[doc(inline)]
100100
pub use codec::Streaming;
101-
pub use request::Request;
101+
pub use request::{IntoRequest, IntoStreamingRequest, Request};
102102
pub use response::Response;
103103
pub use status::{Code, Status};
104104

tonic/src/request.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::metadata::MetadataMap;
2+
use futures_core::Stream;
23

34
/// A gRPC request and metadata from an RPC call.
45
#[derive(Debug)]
@@ -7,6 +8,84 @@ pub struct Request<T> {
78
message: T,
89
}
910

11+
/// Trait implemented by RPC request types.
12+
///
13+
/// Types implementing this trait can be used as arguments to client RPC
14+
/// methods without explicitly wrapping them into `tonic::Request`s. The purpose
15+
/// is to make client calls slightly more convenient to write.
16+
///
17+
/// Tonic's code generation and blanket implementations handle this for you,
18+
/// so it is not necessary to implement this trait directly.
19+
///
20+
/// # Example
21+
///
22+
/// Given the following gRPC method definition:
23+
/// ```proto
24+
/// rpc GetFeature(Point) returns (Feature) {}
25+
/// ```
26+
///
27+
/// we can call `get_feature` in two equivalent ways:
28+
/// ```rust
29+
/// # pub struct Point {}
30+
/// # pub struct Client {}
31+
/// # impl Client {
32+
/// # fn get_feature(&self, r: impl tonic::IntoRequest<Point>) {}
33+
/// # }
34+
/// # let client = Client {};
35+
/// use tonic::Request;
36+
///
37+
/// client.get_feature(Point {});
38+
/// client.get_feature(Request::new(Point {}));
39+
/// ```
40+
pub trait IntoRequest<T>: sealed::Sealed {
41+
/// Wrap the input message `T` in a `tonic::Request`
42+
fn into_request(self) -> Request<T>;
43+
}
44+
45+
/// Trait implemented by RPC streaming request types.
46+
///
47+
/// Types implementing this trait can be used as arguments to client streaming
48+
/// RPC methods without explicitly wrapping them into `tonic::Request`s. The
49+
/// purpose is to make client calls slightly more convenient to write.
50+
///
51+
/// Tonic's code generation and blanket implementations handle this for you,
52+
/// so it is not necessary to implement this trait directly.
53+
///
54+
/// # Example
55+
///
56+
/// Given the following gRPC service method definition:
57+
/// ```proto
58+
/// rpc RecordRoute(stream Point) returns (RouteSummary) {}
59+
/// ```
60+
/// we can call `record_route` in two equivalent ways:
61+
///
62+
/// ```rust
63+
/// # #[derive(Clone)]
64+
/// # pub struct Point {};
65+
/// # pub struct Client {};
66+
/// # impl Client {
67+
/// # fn record_route(&self, r: impl tonic::IntoStreamingRequest<Message = Point>) {}
68+
/// # }
69+
/// # let client = Client {};
70+
/// use tonic::Request;
71+
/// use futures_util::stream;
72+
///
73+
/// let messages = vec![Point {}, Point {}];
74+
///
75+
/// client.record_route(Request::new(stream::iter(messages.clone())));
76+
/// client.record_route(stream::iter(messages));
77+
/// ```
78+
pub trait IntoStreamingRequest: sealed::Sealed {
79+
/// The RPC request stream type
80+
type Stream: Stream<Item = Self::Message> + Send + 'static;
81+
82+
/// The RPC request type
83+
type Message;
84+
85+
/// Wrap the stream of messages in a `tonic::Request`
86+
fn into_streaming_request(self) -> Request<Self::Stream>;
87+
}
88+
1089
impl<T> Request<T> {
1190
/// Create a new gRPC request.
1291
///
@@ -88,3 +167,45 @@ impl<T> Request<T> {
88167
}
89168
}
90169
}
170+
171+
impl<T> IntoRequest<T> for T {
172+
fn into_request(self) -> Request<Self> {
173+
Request::new(self)
174+
}
175+
}
176+
177+
impl<T> IntoRequest<T> for Request<T> {
178+
fn into_request(self) -> Request<T> {
179+
self
180+
}
181+
}
182+
183+
impl<T> IntoStreamingRequest for T
184+
where
185+
T: Stream + Send + 'static,
186+
{
187+
type Stream = T;
188+
type Message = T::Item;
189+
190+
fn into_streaming_request(self) -> Request<Self> {
191+
Request::new(self)
192+
}
193+
}
194+
195+
impl<T> IntoStreamingRequest for Request<T>
196+
where
197+
T: Stream + Send + 'static,
198+
{
199+
type Stream = T;
200+
type Message = T::Item;
201+
202+
fn into_streaming_request(self) -> Self {
203+
self
204+
}
205+
}
206+
207+
impl<T> sealed::Sealed for T {}
208+
209+
mod sealed {
210+
pub trait Sealed {}
211+
}

0 commit comments

Comments
 (0)