wiremock_grpc/wiremock/codegen.rs
1/// Generate mock server code for the given `prefix` and `type`.
2///
3/// For each gRPC server you need to generate codes using this macro.
4///
5/// # Arguments
6/// * `prefix` - The prefix of the RPC (eg. `hello.Greeter` if the RPC is `/helloworld.Greeter/SayHello`)
7/// * `type` - Type of the generated server. This [`Deref`](core::ops::Deref) to [`GrpcServer`](crate::wiremock::grpc_server::GrpcServer). You will be interacting with this type in your test.
8///
9/// # Example
10/// ```no_run
11/// mod wiremock_gen {
12/// // hello.Greeter: is the prefix of all rpc,
13/// // MyMockServer: name of the generated Server,
14/// wiremock_grpc::generate!("hello.Greeter", MyMockServer);
15/// }
16/// use wiremock_gen::*; // this imports generated
17/// use wiremock_grpc::*; // this imports MockBuilder
18///
19/// # async fn example() {
20/// // ... Later in your test (MyMockServer is generated above)
21/// let mut server = MyMockServer::start_default().await;
22/// # }
23/// ```
24#[macro_export]
25#[deprecated(
26 since = "0.4.0",
27 note = "Use `generate_svc!` instead for type-safe API"
28)]
29macro_rules! generate {
30 ($prefix:literal, $type: ident) => {
31 use ::wiremock_grpc::tonic::{
32 codegen::{http, Body, StdError},
33 Code,
34 };
35 use std::{
36 net::SocketAddr,
37 ops::{Deref, DerefMut},
38 task::Poll,
39 };
40
41 use wiremock_grpc::*;
42
43 /// A running gRPC server that binds to service with prefix: `
44 #[doc = $prefix]
45 /// `
46 /// # Example
47 /// ```no_run
48 /// let mut server =
49 #[doc = stringify!($type)]
50 /// ::start_default().await;
51 /// ```
52 /// More documentations in [`crate`]
53 #[derive(Clone)]
54 pub struct $type(pub(crate) GrpcServer);
55
56 impl Deref for $type {
57 type Target = GrpcServer;
58
59 fn deref(&self) -> &Self::Target {
60 &self.0
61 }
62 }
63
64 impl DerefMut for $type {
65 fn deref_mut(&mut self) -> &mut Self::Target {
66 &mut self.0
67 }
68 }
69
70 impl<B> tonic::codegen::Service<tonic::codegen::http::Request<B>> for $type
71 where
72 B: ::wiremock_grpc::http_body::Body + Send + 'static,
73 B::Error: Into<tonic::codegen::StdError> + Send + 'static,
74 {
75 type Response = tonic::codegen::http::Response<tonic::body::Body>;
76 type Error = std::convert::Infallible;
77 type Future = tonic::codegen::BoxFuture<Self::Response, Self::Error>;
78
79 fn poll_ready(
80 &mut self,
81 _cx: &mut std::task::Context<'_>,
82 ) -> Poll<Result<(), Self::Error>> {
83 Poll::Ready(Ok(()))
84 }
85
86 fn call(&mut self, req: tonic::codegen::http::Request<B>) -> Self::Future {
87 self.0.handle_request(req)
88 }
89 }
90
91 impl tonic::server::NamedService for $type {
92 const NAME: &'static str = $prefix;
93 }
94
95 impl $type {
96 /// Start the server and listens to an available port.
97 ///
98 /// The port can be accessed using `address()`
99 /// ```no_run
100 /// let server = MyMockServer::start_default();
101 /// let address = server.address();
102 /// let port : u16 = address.port();
103 /// ```
104 pub async fn start_default() -> Self {
105 let port = GrpcServer::find_unused_port()
106 .await
107 .expect("Unable to find an open port");
108
109 Self(GrpcServer::new(port)).start_internal().await
110 }
111
112 /// Start the server with a specified port.
113 ///
114 /// ## Panics
115 /// * When the the port is not available.
116 pub async fn start(port: u16) -> Self {
117 Self(GrpcServer::new(port)).start_internal().await
118 }
119
120 /// Start the server with a specified address.
121 ///
122 /// ## Panics
123 /// * When the the address is not available.
124 pub async fn start_with_addr(addr: SocketAddr) -> Self {
125 Self(GrpcServer::with_addr(addr)).start_internal().await
126 }
127
128 async fn start_internal(&mut self) -> Self {
129 let address = self.address().clone();
130 let thread = tokio::spawn(
131 tonic::transport::Server::builder()
132 .add_service(self.clone())
133 .serve(address),
134 );
135 self._start(thread).await;
136 self.to_owned()
137 }
138 }
139 };
140}