Skip to content

Commit fd72d4d

Browse files
Merge branch 'allow-trait-mocking' into 'rust-openapi' (#22332)
allow trait mocking See merge request tools.core/swagger-codegen!379 Co-authored-by: Richard Whitehouse <[email protected]>
1 parent 1e3f6d0 commit fd72d4d

File tree

23 files changed

+223
-121
lines changed

23 files changed

+223
-121
lines changed

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,16 @@ private void postProcessOperationWithModels(CodegenOperation op, List<ModelMap>
870870
op.vendorExtensions.put("x-has-request-body", true);
871871
}
872872

873+
if (op.allParams.stream()
874+
.anyMatch(p -> p.isArray && !p.isPrimitiveType)) {
875+
op.vendorExtensions.put("x-has-borrowed-params", Boolean.TRUE);
876+
for (CodegenParameter param : op.allParams) {
877+
if (param.isArray) {
878+
param.vendorExtensions.put("x-param-needs-lifetime", Boolean.TRUE);
879+
}
880+
}
881+
}
882+
873883
// The CLI generates a structopt structure for each operation. This can only have a single
874884
// use of a short option, which comes from the parameter name, so we need to police
875885
// against duplicates

modules/openapi-generator/src/main/resources/rust-server/Cargo.mustache

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ cli = [
7070
]
7171
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
7272

73+
mock = ["mockall"]
74+
7375
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
7476
native-tls = { version = "0.2", optional = true }
7577
hyper-tls = { version = "0.6", optional = true }
@@ -87,6 +89,7 @@ swagger = { version = "7.0.0-rc3", features = ["serdejson", "server", "client",
8789
headers = "0.4.0"
8890
log = "0.4.27"
8991
mime = "0.3"
92+
mockall = { version = "0.13.1", optional = true }
9093

9194
serde = { version = "1.0", features = ["derive"] }
9295
serde_json = "1.0"

modules/openapi-generator/src/main/resources/rust-server/client-operation.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#[allow(clippy::vec_init_then_push)]
2-
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}(
2+
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}{{#exts.x-has-borrowed-params}}<'a>{{/exts.x-has-borrowed-params}}(
33
&self,
44
{{#exts}}
55
{{#x-callback-params}}
66
callback_{{.}}: String,
77
{{/x-callback-params}}
88
{{/exts}}
99
{{#allParams}}
10-
param_{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{/isArray}}{{{dataType}}}{{^required}}>{{/required}},
10+
param_{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{#exts.x-param-needs-lifetime}}'a {{/exts.x-param-needs-lifetime}}{{/isArray}}{{{dataType}}}{{^required}}>{{/required}},
1111
{{/allParams}}
1212
context: &C) -> Result<{{{operationId}}}Response, ApiError>
1313
{

modules/openapi-generator/src/main/resources/rust-server/example-server-operation.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{{#summary}}
22
/// {{{.}}}
33
{{/summary}}
4-
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}(
4+
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}{{#exts.x-has-borrowed-params}}<'a>{{/exts.x-has-borrowed-params}}(
55
&self,
66
{{#exts}}
77
{{#x-callback-params}}
88
callback_{{.}}: String,
99
{{/x-callback-params}}
1010
{{/exts}}
1111
{{#allParams}}
12-
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{/isArray}}{{{dataType}}}{{^required}}>{{/required}},
12+
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{#exts.x-param-needs-lifetime}}'a {{/exts.x-param-needs-lifetime}}{{/isArray}}{{{dataType}}}{{^required}}>{{/required}},
1313
{{/allParams}}
1414
context: &C) -> Result<{{{operationId}}}Response, ApiError>
1515
{

modules/openapi-generator/src/main/resources/rust-server/lib.mustache

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
use async_trait::async_trait;
55
use futures::Stream;
6+
#[cfg(feature = "mock")]
7+
use mockall::automock;
68
use std::error::Error;
79
use std::collections::BTreeSet;
810
use std::task::{Poll, Context};
@@ -33,6 +35,7 @@ pub use auth::{AuthenticationApi, Claims};
3335
{{/apis}}
3436
{{/apiInfo}}
3537
/// API
38+
#[cfg_attr(feature = "mock", automock)]
3639
#[async_trait]
3740
#[allow(clippy::too_many_arguments, clippy::ptr_arg)]
3841
pub trait Api<C: Send + Sync> {
@@ -43,10 +46,10 @@ pub trait Api<C: Send + Sync> {
4346
{{#summary}}
4447
/// {{{.}}}
4548
{{/summary}}
46-
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}(
49+
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}{{#exts.x-has-borrowed-params}}<'a>{{/exts.x-has-borrowed-params}}(
4750
&self,
4851
{{#allParams}}
49-
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{/isArray}}{{{dataType}}}{{^required}}>{{/required}},
52+
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{#exts.x-param-needs-lifetime}}'a {{/exts.x-param-needs-lifetime}}{{/isArray}}{{{dataType}}}{{^required}}>{{/required}},
5053
{{/allParams}}
5154
context: &C) -> Result<{{{operationId}}}Response, ApiError>;
5255
@@ -57,9 +60,14 @@ pub trait Api<C: Send + Sync> {
5760
}
5861
5962
/// API where `Context` isn't passed on every API call
63+
#[cfg_attr(feature = "mock", automock)]
6064
#[async_trait]
6165
#[allow(clippy::too_many_arguments, clippy::ptr_arg)]
6266
pub trait ApiNoContext<C: Send + Sync> {
67+
// The std::task::Context struct houses a reference to std::task::Waker with the lifetime <'a>.
68+
// Adding an anonymous lifetime `'a` to allow mockall to create a mock object with the right lifetimes.
69+
// This is needed because the compiler is unable to determine the lifetimes on F's trait bound
70+
// where F is the closure created by mockall. We use higher-rank trait bounds here to get around this.
6371
6472
fn context(&self) -> &C;
6573
@@ -70,10 +78,10 @@ pub trait ApiNoContext<C: Send + Sync> {
7078
{{#summary}}
7179
/// {{{.}}}
7280
{{/summary}}
73-
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}(
81+
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}{{#exts.x-has-borrowed-params}}<'a>{{/exts.x-has-borrowed-params}}(
7482
&self,
7583
{{#allParams}}
76-
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{/isArray}}{{{dataType}}}{{^required}}>{{/required}},
84+
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{/isArray}}{{#exts.x-param-needs-lifetime}}'a {{/exts.x-param-needs-lifetime}}{{{dataType}}}{{^required}}>{{/required}},
7785
{{/allParams}}
7886
) -> Result<{{{operationId}}}Response, ApiError>;
7987

@@ -109,10 +117,10 @@ impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ApiNoContext<C> for Contex
109117
{{#summary}}
110118
/// {{{.}}}
111119
{{/summary}}
112-
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}(
120+
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}{{#exts.x-has-borrowed-params}}<'a>{{/exts.x-has-borrowed-params}}(
113121
&self,
114122
{{#allParams}}
115-
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{/isArray}}{{{dataType}}}{{^required}}>{{/required}},
123+
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{/isArray}}{{#exts.x-param-needs-lifetime}}'a {{/exts.x-param-needs-lifetime}}{{{dataType}}}{{^required}}>{{/required}},
116124
{{/allParams}}
117125
) -> Result<{{{operationId}}}Response, ApiError>
118126
{
@@ -146,6 +154,7 @@ impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ApiNoContext<C> for Contex
146154
{{/apiInfo}}
147155

148156
/// Callback API
157+
#[cfg_attr(feature = "mock", automock)]
149158
#[async_trait]
150159
pub trait CallbackApi<C: Send + Sync> {
151160
@@ -159,15 +168,15 @@ pub trait CallbackApi<C: Send + Sync> {
159168
{{#summary}}
160169
/// {{{.}}}
161170
{{/summary}}
162-
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}(
171+
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}{{#exts.x-has-borrowed-params}}<'a>{{/exts.x-has-borrowed-params}}(
163172
&self,
164173
{{#exts}}
165174
{{#x-callback-params}}
166175
callback_{{.}}: String,
167176
{{/x-callback-params}}
168177
{{/exts}}
169178
{{#allParams}}
170-
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{/isArray}}{{{dataType}}}{{^required}}>{{/required}},
179+
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{/isArray}}{{#exts.x-param-needs-lifetime}}'a {{/exts.x-param-needs-lifetime}}{{{dataType}}}{{^required}}>{{/required}},
171180
{{/allParams}}
172181
context: &C) -> Result<{{{operationId}}}Response, ApiError>;
173182

@@ -181,6 +190,7 @@ pub trait CallbackApi<C: Send + Sync> {
181190
}
182191

183192
/// Callback API without a `Context`
193+
#[cfg_attr(feature = "mock", automock)]
184194
#[async_trait]
185195
pub trait CallbackApiNoContext<C: Send + Sync> {
186196
@@ -196,15 +206,15 @@ pub trait CallbackApiNoContext<C: Send + Sync> {
196206
{{#summary}}
197207
/// {{{.}}}
198208
{{/summary}}
199-
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}(
209+
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}{{#exts.x-has-borrowed-params}}<'a>{{/exts.x-has-borrowed-params}}(
200210
&self,
201211
{{#exts}}
202212
{{#x-callback-params}}
203213
callback_{{.}}: String,
204214
{{/x-callback-params}}
205215
{{/exts}}
206216
{{#allParams}}
207-
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{/isArray}}{{{dataType}}}{{^required}}>{{/required}},
217+
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{/isArray}}{{#exts.x-param-needs-lifetime}}'a {{/exts.x-param-needs-lifetime}}{{{dataType}}}{{^required}}>{{/required}},
208218
{{/allParams}}
209219
) -> Result<{{{operationId}}}Response, ApiError>;
210220

@@ -246,15 +256,15 @@ impl<T: CallbackApi<C> + Send + Sync, C: Clone + Send + Sync> CallbackApiNoConte
246256
{{#summary}}
247257
/// {{{.}}}
248258
{{/summary}}
249-
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}(
259+
async fn {{#exts}}{{{x-operation-id}}}{{/exts}}{{#exts.x-has-borrowed-params}}<'a>{{/exts.x-has-borrowed-params}}(
250260
&self,
251261
{{#exts}}
252262
{{#x-callback-params}}
253263
callback_{{.}}: String,
254264
{{/x-callback-params}}
255265
{{/exts}}
256266
{{#allParams}}
257-
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{/isArray}}{{{dataType}}}{{^required}}>{{/required}},
267+
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isArray}}&{{/isArray}}{{#exts.x-param-needs-lifetime}}'a {{/exts.x-param-needs-lifetime}}{{{dataType}}}{{^required}}>{{/required}},
258268
{{/allParams}}
259269
) -> Result<{{{operationId}}}Response, ApiError>
260270
{

samples/server/petstore/rust-server/output/multipart-v3/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ cli = [
2424
]
2525
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
2626

27+
mock = ["mockall"]
28+
2729
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
2830
native-tls = { version = "0.2", optional = true }
2931
hyper-tls = { version = "0.6", optional = true }
@@ -41,6 +43,7 @@ swagger = { version = "7.0.0-rc3", features = ["serdejson", "server", "client",
4143
headers = "0.4.0"
4244
log = "0.4.27"
4345
mime = "0.3"
46+
mockall = { version = "0.13.1", optional = true }
4447

4548
serde = { version = "1.0", features = ["derive"] }
4649
serde_json = "1.0"

samples/server/petstore/rust-server/output/multipart-v3/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
use async_trait::async_trait;
55
use futures::Stream;
6+
#[cfg(feature = "mock")]
7+
use mockall::automock;
68
use std::error::Error;
79
use std::collections::BTreeSet;
810
use std::task::{Poll, Context};
@@ -39,6 +41,7 @@ pub enum MultipleIdenticalMimeTypesPostResponse {
3941
}
4042

4143
/// API
44+
#[cfg_attr(feature = "mock", automock)]
4245
#[async_trait]
4346
#[allow(clippy::too_many_arguments, clippy::ptr_arg)]
4447
pub trait Api<C: Send + Sync> {
@@ -66,9 +69,14 @@ pub trait Api<C: Send + Sync> {
6669
}
6770

6871
/// API where `Context` isn't passed on every API call
72+
#[cfg_attr(feature = "mock", automock)]
6973
#[async_trait]
7074
#[allow(clippy::too_many_arguments, clippy::ptr_arg)]
7175
pub trait ApiNoContext<C: Send + Sync> {
76+
// The std::task::Context struct houses a reference to std::task::Waker with the lifetime <'a>.
77+
// Adding an anonymous lifetime `'a` to allow mockall to create a mock object with the right lifetimes.
78+
// This is needed because the compiler is unable to determine the lifetimes on F's trait bound
79+
// where F is the closure created by mockall. We use higher-rank trait bounds here to get around this.
7280

7381
fn context(&self) -> &C;
7482

samples/server/petstore/rust-server/output/no-example-v3/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ cli = [
2020
]
2121
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
2222

23+
mock = ["mockall"]
24+
2325
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
2426
native-tls = { version = "0.2", optional = true }
2527
hyper-tls = { version = "0.6", optional = true }
@@ -37,6 +39,7 @@ swagger = { version = "7.0.0-rc3", features = ["serdejson", "server", "client",
3739
headers = "0.4.0"
3840
log = "0.4.27"
3941
mime = "0.3"
42+
mockall = { version = "0.13.1", optional = true }
4043

4144
serde = { version = "1.0", features = ["derive"] }
4245
serde_json = "1.0"

samples/server/petstore/rust-server/output/no-example-v3/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
use async_trait::async_trait;
55
use futures::Stream;
6+
#[cfg(feature = "mock")]
7+
use mockall::automock;
68
use std::error::Error;
79
use std::collections::BTreeSet;
810
use std::task::{Poll, Context};
@@ -27,6 +29,7 @@ pub enum OpGetResponse {
2729
}
2830

2931
/// API
32+
#[cfg_attr(feature = "mock", automock)]
3033
#[async_trait]
3134
#[allow(clippy::too_many_arguments, clippy::ptr_arg)]
3235
pub trait Api<C: Send + Sync> {
@@ -38,9 +41,14 @@ pub trait Api<C: Send + Sync> {
3841
}
3942

4043
/// API where `Context` isn't passed on every API call
44+
#[cfg_attr(feature = "mock", automock)]
4145
#[async_trait]
4246
#[allow(clippy::too_many_arguments, clippy::ptr_arg)]
4347
pub trait ApiNoContext<C: Send + Sync> {
48+
// The std::task::Context struct houses a reference to std::task::Waker with the lifetime <'a>.
49+
// Adding an anonymous lifetime `'a` to allow mockall to create a mock object with the right lifetimes.
50+
// This is needed because the compiler is unable to determine the lifetimes on F's trait bound
51+
// where F is the closure created by mockall. We use higher-rank trait bounds here to get around this.
4452

4553
fn context(&self) -> &C;
4654

samples/server/petstore/rust-server/output/openapi-v3/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ cli = [
2323
]
2424
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
2525

26+
mock = ["mockall"]
27+
2628
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
2729
native-tls = { version = "0.2", optional = true }
2830
hyper-tls = { version = "0.6", optional = true }
@@ -40,6 +42,7 @@ swagger = { version = "7.0.0-rc3", features = ["serdejson", "server", "client",
4042
headers = "0.4.0"
4143
log = "0.4.27"
4244
mime = "0.3"
45+
mockall = { version = "0.13.1", optional = true }
4346

4447
serde = { version = "1.0", features = ["derive"] }
4548
serde_json = "1.0"

0 commit comments

Comments
 (0)