[Rust] [Server] New generator bases on Axum (#17549)

* Rust Server - Axum based (#5)

* Fix typo

* Address comment

* Address comment
This commit is contained in:
Linh Tran Tuan 2024-01-09 12:38:54 +09:00 committed by GitHub
parent 210044eb8a
commit 9827098057
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
107 changed files with 35444 additions and 0 deletions

View File

@ -6,11 +6,13 @@ on:
- "samples/client/others/rust/**" - "samples/client/others/rust/**"
- "samples/server/petstore/rust-server/**" - "samples/server/petstore/rust-server/**"
- "samples/client/petstore/rust-server/**" - "samples/client/petstore/rust-server/**"
- "samples/server/petstore/rust-axum/**"
pull_request: pull_request:
paths: paths:
- "samples/client/others/rust/**" - "samples/client/others/rust/**"
- "samples/client/petstore/rust/**" - "samples/client/petstore/rust/**"
- "samples/server/petstore/rust-server/**" - "samples/server/petstore/rust-server/**"
- "samples/server/petstore/rust-axum/**"
jobs: jobs:
build: build:
@ -24,6 +26,7 @@ jobs:
- samples/client/others/rust/ - samples/client/others/rust/
- samples/client/petstore/rust/ - samples/client/petstore/rust/
- samples/server/petstore/rust-server/ - samples/server/petstore/rust-server/
- samples/server/petstore/rust-axum/
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1

View File

@ -22,6 +22,7 @@ cache:
- $HOME/samples/client/petstore/ruby/vendor/bundle - $HOME/samples/client/petstore/ruby/vendor/bundle
- $HOME/samples/client/petstore/python/.venv/ - $HOME/samples/client/petstore/python/.venv/
- $HOME/samples/server/petstore/rust-server/target - $HOME/samples/server/petstore/rust-server/target
- $HOME/samples/server/petstore/rust-axum/target
- $HOME/perl5 - $HOME/perl5
- $HOME/.cargo - $HOME/.cargo
- $HOME/.pub-cache - $HOME/.pub-cache

View File

@ -0,0 +1,10 @@
generatorName: rust-axum
outputDir: samples/server/petstore/rust-axum/output/multipart-v3
inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-server/multipart-v3.yaml
templateDir: modules/openapi-generator/src/main/resources/rust-axum
generateAliasAsModel: true
additionalProperties:
hideGenerationTimestamp: "true"
allowBlockingResponseSerialize: "true"
packageName: multipart-v3
enablePostProcessFile: true

View File

@ -0,0 +1,10 @@
generatorName: rust-axum
outputDir: samples/server/petstore/rust-axum/output/openapi-v3
inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml
templateDir: modules/openapi-generator/src/main/resources/rust-axum
generateAliasAsModel: true
additionalProperties:
hideGenerationTimestamp: "true"
allowBlockingValidator: "true"
packageName: openapi-v3
enablePostProcessFile: true

View File

@ -0,0 +1,8 @@
generatorName: rust-axum
outputDir: samples/server/petstore/rust-axum/output/ops-v3
inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-server/ops-v3.yaml
templateDir: modules/openapi-generator/src/main/resources/rust-axum
generateAliasAsModel: true
additionalProperties:
hideGenerationTimestamp: "true"
packageName: ops-v3

View File

@ -0,0 +1,10 @@
generatorName: rust-axum
outputDir: samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing
inputSpec: modules/openapi-generator/src/test/resources/2_0/rust-server/petstore-with-fake-endpoints-models-for-testing.yaml
templateDir: modules/openapi-generator/src/main/resources/rust-axum
generateAliasAsModel: true
additionalProperties:
hideGenerationTimestamp: "true"
packageName: petstore-with-fake-endpoints-models-for-testing
publishRustRegistry: crates-io
enablePostProcessFile: true

View File

@ -0,0 +1,9 @@
generatorName: rust-axum
outputDir: samples/server/petstore/rust-axum/output/petstore
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/rust-axum
generateAliasAsModel: true
additionalProperties:
hideGenerationTimestamp: "true"
packageName: petstore
enablePostProcessFile: true

View File

@ -0,0 +1,9 @@
generatorName: rust-axum
outputDir: samples/server/petstore/rust-axum/output/ping-bearer-auth
inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-server/ping-bearer-auth.yaml
templateDir: modules/openapi-generator/src/main/resources/rust-axum
generateAliasAsModel: true
additionalProperties:
hideGenerationTimestamp: "true"
packageName: ping-bearer-auth
enablePostProcessFile: true

View File

@ -0,0 +1,9 @@
generatorName: rust-axum
outputDir: samples/server/petstore/rust-axum/output/rust-axum-test
inputSpec: modules/openapi-generator/src/test/resources/2_0/rust-server/rust-server-test.yaml
templateDir: modules/openapi-generator/src/main/resources/rust-axum
generateAliasAsModel: true
additionalProperties:
hideGenerationTimestamp: "true"
packageName: rust-server-test
enablePostProcessFile: true

View File

@ -130,6 +130,7 @@ The following generators are available:
* [python-flask](generators/python-flask.md) * [python-flask](generators/python-flask.md)
* [ruby-on-rails](generators/ruby-on-rails.md) * [ruby-on-rails](generators/ruby-on-rails.md)
* [ruby-sinatra](generators/ruby-sinatra.md) * [ruby-sinatra](generators/ruby-sinatra.md)
* [rust-axum (beta)](generators/rust-axum.md)
* [rust-server](generators/rust-server.md) * [rust-server](generators/rust-server.md)
* [scala-akka-http-server (beta)](generators/scala-akka-http-server.md) * [scala-akka-http-server (beta)](generators/scala-akka-http-server.md)
* [scala-finch](generators/scala-finch.md) * [scala-finch](generators/scala-finch.md)

View File

@ -0,0 +1,235 @@
---
title: Documentation for the rust-axum Generator
---
## METADATA
| Property | Value | Notes |
| -------- | ----- | ----- |
| generator name | rust-axum | pass this to the generate command after -g |
| generator stability | BETA | |
| generator type | SERVER | |
| generator language | Rust | |
| generator default templating engine | mustache | |
| helpTxt | Generates a Rust server library which bases on Axum. | |
## CONFIG OPTIONS
These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details.
| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|allowBlockingResponseSerialize|By default, json/form-urlencoded response serialization, which might perform a lot of compute in a future without yielding, is executed on a blocking thread via tokio::task::spawn_blocking. Set this option to true will override this behaviour and allow blocking call to happen. It helps to improve the performance when response serialization (e.g. returns tiny data) is low cost.| |false|
|allowBlockingValidator|By default, validation process, which might perform a lot of compute in a future without yielding, is executed on a blocking thread via tokio::task::spawn_blocking. Set this option to true will override this behaviour and allow blocking call to happen. It helps to improve the performance when validating request-data (header, path, query, body) is low cost.| |false|
|disableValidator|Disable validating request-data (header, path, query, body) against OpenAPI Schema Specification.| |false|
|packageName|Rust crate name (convention: snake_case).| |openapi|
|packageVersion|Rust crate version.| |null|
## IMPORT MAPPING
| Type/Alias | Imports |
| ---------- | ------- |
## INSTANTIATION TYPES
| Type/Alias | Instantiated By |
| ---------- | --------------- |
|array|Vec|
|map|std::collections::HashMap|
## LANGUAGE PRIMITIVES
<ul class="column-ul">
<li>String</li>
<li>bool</li>
<li>char</li>
<li>f32</li>
<li>f64</li>
<li>i16</li>
<li>i32</li>
<li>i64</li>
<li>i8</li>
<li>isize</li>
<li>str</li>
<li>u16</li>
<li>u32</li>
<li>u64</li>
<li>u8</li>
<li>usize</li>
</ul>
## RESERVED WORDS
<ul class="column-ul">
<li>Self</li>
<li>abstract</li>
<li>as</li>
<li>async</li>
<li>await</li>
<li>become</li>
<li>box</li>
<li>break</li>
<li>const</li>
<li>continue</li>
<li>crate</li>
<li>do</li>
<li>dyn</li>
<li>else</li>
<li>enum</li>
<li>extern</li>
<li>false</li>
<li>final</li>
<li>fn</li>
<li>for</li>
<li>if</li>
<li>impl</li>
<li>in</li>
<li>let</li>
<li>loop</li>
<li>macro</li>
<li>match</li>
<li>mod</li>
<li>move</li>
<li>mut</li>
<li>override</li>
<li>priv</li>
<li>pub</li>
<li>ref</li>
<li>return</li>
<li>self</li>
<li>static</li>
<li>struct</li>
<li>super</li>
<li>trait</li>
<li>true</li>
<li>try</li>
<li>type</li>
<li>typeof</li>
<li>unsafe</li>
<li>unsized</li>
<li>use</li>
<li>virtual</li>
<li>where</li>
<li>while</li>
<li>yield</li>
</ul>
## FEATURE SET
### Client Modification Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|BasePath|✗|ToolingExtension
|Authorizations|✗|ToolingExtension
|UserAgent|✗|ToolingExtension
|MockServer|✗|ToolingExtension
### Data Type Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Custom|✗|OAS2,OAS3
|Int32|✓|OAS2,OAS3
|Int64|✓|OAS2,OAS3
|Float|✓|OAS2,OAS3
|Double|✓|OAS2,OAS3
|Decimal|✓|ToolingExtension
|String|✓|OAS2,OAS3
|Byte|✓|OAS2,OAS3
|Binary|✓|OAS2,OAS3
|Boolean|✓|OAS2,OAS3
|Date|✓|OAS2,OAS3
|DateTime|✓|OAS2,OAS3
|Password|✓|OAS2,OAS3
|File|✓|OAS2
|Uuid|✗|
|Array|✓|OAS2,OAS3
|Null|✗|OAS3
|AnyType|✗|OAS2,OAS3
|Object|✓|OAS2,OAS3
|Maps|✓|ToolingExtension
|CollectionFormat|✓|OAS2
|CollectionFormatMulti|✓|OAS2
|Enum|✓|OAS2,OAS3
|ArrayOfEnum|✓|ToolingExtension
|ArrayOfModel|✓|ToolingExtension
|ArrayOfCollectionOfPrimitives|✓|ToolingExtension
|ArrayOfCollectionOfModel|✓|ToolingExtension
|ArrayOfCollectionOfEnum|✓|ToolingExtension
|MapOfEnum|✓|ToolingExtension
|MapOfModel|✓|ToolingExtension
|MapOfCollectionOfPrimitives|✓|ToolingExtension
|MapOfCollectionOfModel|✓|ToolingExtension
|MapOfCollectionOfEnum|✓|ToolingExtension
### Documentation Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Readme|✗|ToolingExtension
|Model|✓|ToolingExtension
|Api|✓|ToolingExtension
### Global Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Host|✓|OAS2,OAS3
|BasePath|✓|OAS2,OAS3
|Info|✗|OAS2,OAS3
|Schemes|✗|OAS2,OAS3
|PartialSchemes|✓|OAS2,OAS3
|Consumes|✓|OAS2
|Produces|✓|OAS2
|ExternalDocumentation|✗|OAS2,OAS3
|Examples|✗|OAS2,OAS3
|XMLStructureDefinitions|✗|OAS2,OAS3
|MultiServer|✗|OAS3
|ParameterizedServer|✗|OAS3
|ParameterStyling|✗|OAS3
|Callbacks|✗|OAS3
|LinkObjects|✗|OAS3
### Parameter Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Path|✓|OAS2,OAS3
|Query|✓|OAS2,OAS3
|Header|✓|OAS2,OAS3
|Body|✓|OAS2
|FormUnencoded|✓|OAS2
|FormMultipart|✓|OAS2
|Cookie|✗|OAS3
### Schema Support Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Simple|✓|OAS2,OAS3
|Composite|✓|OAS2,OAS3
|Polymorphism|✗|OAS2,OAS3
|Union|✗|OAS3
|allOf|✗|OAS2,OAS3
|anyOf|✗|OAS3
|oneOf|✗|OAS3
|not|✗|OAS3
### Security Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|BasicAuth|✓|OAS2,OAS3
|ApiKey|✓|OAS2,OAS3
|OpenIDConnect|✗|OAS3
|BearerToken|✓|OAS3
|OAuth2_Implicit|✓|OAS2,OAS3
|OAuth2_Password|✓|OAS2,OAS3
|OAuth2_ClientCredentials|✓|OAS2,OAS3
|OAuth2_AuthorizationCode|✓|OAS2,OAS3
|SignatureAuth|✗|OAS3
|AWSV4Signature|✗|ToolingExtension
### Wire Format Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|JSON|✓|OAS2,OAS3
|XML|✗|OAS2,OAS3
|PROTOBUF|✗|ToolingExtension
|Custom|✓|OAS2,OAS3

View File

@ -143,3 +143,4 @@ org.openapitools.codegen.languages.TypeScriptRxjsClientCodegen
org.openapitools.codegen.languages.WsdlSchemaCodegen org.openapitools.codegen.languages.WsdlSchemaCodegen
org.openapitools.codegen.languages.XojoClientCodegen org.openapitools.codegen.languages.XojoClientCodegen
org.openapitools.codegen.languages.ZapierClientCodegen org.openapitools.codegen.languages.ZapierClientCodegen
org.openapitools.codegen.languages.RustAxumServerCodegen

View File

@ -0,0 +1,68 @@
[package]
name = "{{{packageName}}}"
version = "{{{packageVersion}}}"
{{#infoEmail}}
authors = ["{{{.}}}"]
{{/infoEmail}}
{{^infoEmail}}
authors = ["OpenAPI Generator team and contributors"]
{{/infoEmail}}
{{#appDescription}}
description = "{{{.}}}"
{{/appDescription}}
{{#licenseInfo}}
license = "{{.}}"
{{/licenseInfo}}
edition = "2021"
{{#publishRustRegistry}}
publish = ["{{.}}"]
{{/publishRustRegistry}}
{{#repositoryUrl}}
repository = "{{.}}"
{{/repositoryUrl}}
{{#documentationUrl}}
documentation = "{{.}}"
{{/documentationUrl}}
{{#homePageUrl}}
homepage = "{{.}}
{{/homePageUrl}}
[features]
default = ["server"]
server = []
conversion = [
"frunk",
"frunk_derives",
"frunk_core",
"frunk-enum-core",
"frunk-enum-derive",
]
[dependencies]
async-trait = "0.1"
axum = { version = "0.7" }
axum-extra = { version = "0.9", features = ["cookie", "multipart"] }
base64 = "0.21"
bytes = "1"
chrono = { version = "0.4", features = ["serde"] }
frunk = { version = "0.4", optional = true }
frunk-enum-core = { version = "0.3", optional = true }
frunk-enum-derive = { version = "0.3", optional = true }
frunk_core = { version = "0.4", optional = true }
frunk_derives = { version = "0.4", optional = true }
http = "1"
lazy_static = "1"
regex = "1"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["raw_value"] }
serde_urlencoded = "0.7"
tokio = { version = "1", default-features = false, features = [
"signal",
"rt-multi-thread",
] }
tracing = { version = "0.1", features = ["attributes"] }
uuid = { version = "1", features = ["serde"] }
validator = { version = "0.16", features = ["derive"] }
[dev-dependencies]
tracing-subscriber = "0.3"

View File

@ -0,0 +1,19 @@
# Rust Axum Server Generator Templates
The Rust Axum Server Generator templates use Mustache Partials.
The following tree shows which templates include which:
- `Cargo.mustache`
- `gitignore`
- `header.mustache`
- `lib.mustache`
- `response.mustache`
- `models.mustache`
- `README.mustache`
- `server-mod.mustache`
- `server-imports.mustache`
- `server-route.mustache`
- `server-operation-validate.mustache`
- `server-operation.mustache`
- `types.mustache`

View File

@ -0,0 +1,93 @@
# Rust API for {{{packageName}}}
{{#appDescriptionWithNewLines}}
{{{.}}}
{{/appDescriptionWithNewLines}}
## Overview
This server was generated by the [openapi-generator]
(https://openapi-generator.tech) project. By using the
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
server, you can easily generate a server stub.
To see how to make this your own, look here: [README]((https://openapi-generator.tech))
- API version: {{{appVersion}}}
{{^hideGenerationTimestamp}}- Build date: {{{generatedDate}}}{{/hideGenerationTimestamp}}
{{#infoUrl}}For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}){{/infoUrl}}
This autogenerated project defines an API crate `{{{packageName}}}` which contains:
* An `Api` trait defining the API in Rust.
* Data types representing the underlying data model.
* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation.
* Request validations (path, query, body params) are included.
## Using the generated library
The generated library has a few optional features that can be activated through Cargo.
* `server`
* This defaults to enabled and creates the basic skeleton of a server implementation based on Axum.
* To create the server stack you'll need to provide an implementation of the API trait to provide the server function.
* `conversions`
* This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types.
See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`.
### Example
```rust
struct ServerImpl {
// database: sea_orm::DbConn,
}
#[allow(unused_variables)]
#[async_trait]
impl {{{packageName}}}::Api for ServerImpl {
// API implementation goes here
}
pub async fn start_server(addr: &str) {
// initialize tracing
tracing_subscriber::fmt::init();
// Init Axum router
let app = {{{packageName}}}::server::new(Arc::new(ServerImpl));
// Add layers to the router
let app = app.layer(...);
// Run the server with graceful shutdown
let listener = TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await
.unwrap();
}
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
}
```

View File

@ -0,0 +1,2 @@
target
Cargo.lock

View File

@ -0,0 +1,180 @@
use std::{convert::TryFrom, fmt, ops::Deref};
use chrono::{DateTime, Utc};
use http::HeaderValue;
/// A struct to allow homogeneous conversion into a HeaderValue. We can't
/// implement the From/Into trait on HeaderValue because we don't own
/// either of the types.
#[derive(Debug, Clone)]
pub(crate) struct IntoHeaderValue<T>(pub T);
// Generic implementations
impl<T> Deref for IntoHeaderValue<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
// Derive for each TryFrom<T> in http::HeaderValue
macro_rules! ihv_generate {
($t:ident) => {
impl TryFrom<HeaderValue> for IntoHeaderValue<$t> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match hdr_value.parse::<$t>() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)),
Err(e) => Err(format!("Unable to parse {} as a string: {}",
stringify!($t), e)),
},
Err(e) => Err(format!("Unable to parse header {:?} as a string - {}",
hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<$t>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result<Self, Self::Error> {
Ok(hdr_value.0.into())
}
}
};
}
ihv_generate!(u64);
ihv_generate!(i64);
ihv_generate!(i16);
ihv_generate!(u16);
ihv_generate!(u32);
ihv_generate!(usize);
ihv_generate!(isize);
ihv_generate!(i32);
// Custom derivations
// Vec<String>
impl TryFrom<HeaderValue> for IntoHeaderValue<Vec<String>> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => Ok(IntoHeaderValue(
hdr_value
.split(',')
.filter_map(|x| match x.trim() {
"" => None,
y => Some(y.to_string()),
})
.collect())),
Err(e) => Err(format!("Unable to parse header: {:?} as a string - {}",
hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<Vec<String>>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<Vec<String>>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0.join(", ")) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!("Unable to convert {:?} into a header - {}",
hdr_value, e))
}
}
}
// String
impl TryFrom<HeaderValue> for IntoHeaderValue<String> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())),
Err(e) => Err(format!("Unable to convert header {:?} to {}",
hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<String>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<String>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!("Unable to convert {:?} from a header {}",
hdr_value, e))
}
}
}
// Bool
impl TryFrom<HeaderValue> for IntoHeaderValue<bool> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match hdr_value.parse() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)),
Err(e) => Err(format!("Unable to parse bool from {} - {}",
hdr_value, e)),
},
Err(e) => Err(format!("Unable to convert {:?} from a header {}",
hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<bool>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<bool>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0.to_string()) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!("Unable to convert: {:?} into a header: {}",
hdr_value, e))
}
}
}
// DateTime
impl TryFrom<HeaderValue> for IntoHeaderValue<DateTime<Utc>> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) {
Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))),
Err(e) => Err(format!("Unable to parse: {} as date - {}",
hdr_value, e)),
},
Err(e) => Err(format!("Unable to convert header {:?} to string {}",
hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<DateTime<Utc>>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<DateTime<Utc>>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!("Unable to convert {:?} to a header: {}",
hdr_value, e)),
}
}
}

View File

@ -0,0 +1,99 @@
#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)]
#![allow(unused_imports, unused_attributes)]
#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)]
use async_trait::async_trait;
use axum::extract::*;
use axum_extra::extract::{CookieJar, Multipart};
use bytes::Bytes;
use http::Method;
use serde::{Deserialize, Serialize};
use types::*;
pub const BASE_PATH: &str = "{{{basePathWithoutHost}}}";
{{#appVersion}}
pub const API_VERSION: &str = "{{{.}}}";
{{/appVersion}}
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{>response}}
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
/// API
#[async_trait]
#[allow(clippy::ptr_arg)]
pub trait Api {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{#summary}}
/// {{{.}}}.
///
{{/summary}}
{{#vendorExtensions}}
/// {{{operationId}}} - {{{httpMethod}}} {{{basePathWithoutHost}}}{{{path}}}
async fn {{{x-operation-id}}}(
&self,
method: Method,
host: Host,
cookies: CookieJar,
{{#headerParams.size}}
header_params: models::{{{operationIdCamelCase}}}HeaderParams,
{{/headerParams.size}}
{{#pathParams.size}}
path_params: models::{{{operationIdCamelCase}}}PathParams,
{{/pathParams.size}}
{{#queryParams.size}}
query_params: models::{{{operationIdCamelCase}}}QueryParams,
{{/queryParams.size}}
{{^x-consumes-multipart-related}}
{{^x-consumes-multipart}}
{{#bodyParam}}
{{#vendorExtensions}}
{{^x-consumes-plain-text}}
body: {{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}},
{{/x-consumes-plain-text}}
{{#x-consumes-plain-text}}
{{#isString}}
body: String,
{{/isString}}
{{^isString}}
body: Bytes,
{{/isString}}
{{/x-consumes-plain-text}}
{{/vendorExtensions}}
{{/bodyParam}}
{{/x-consumes-multipart}}
{{/x-consumes-multipart-related}}
{{#x-consumes-multipart}}
body: Multipart,
{{/x-consumes-multipart}}
{{#x-consumes-multipart-related}}
body: axum::body::Body,
{{/x-consumes-multipart-related}}
) -> Result<{{{operationId}}}Response, String>;
{{/vendorExtensions}}
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
}
#[cfg(feature = "server")]
pub mod server;
pub mod models;
pub mod types;
#[cfg(feature = "server")]
pub(crate) mod header;

View File

@ -0,0 +1,923 @@
#![allow(unused_qualifications)]
use http::HeaderValue;
use validator::Validate;
#[cfg(feature = "server")]
use crate::header;
use crate::{models, types::*};
{{! Don't "use" structs here - they can conflict with the names of models, and mean that the code won't compile }}
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{#vendorExtensions}}
{{#headerParams.size}}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct {{{operationIdCamelCase}}}HeaderParams {
{{#headerParams}}
{{#hasValidation}}
#[validate(
{{#maxLength}}
{{#minLength}}
length(min = {{minLength}}, max = {{maxLength}}),
{{/minLength}}
{{^minLength}}
length(max = {{maxLength}}),
{{/minLength}}
{{/maxLength}}
{{^maxLength}}
{{#minLength}}
length(min = {{minLength}}),
{{/minLength}}
{{/maxLength}}
{{#pattern}}
{{^isByteArray}}
regex = "RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}HeaderParams_{{{paramName}}}{{/lambda.uppercase}}",
{{/isByteArray}}
{{#isByteArray}}
custom ="validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}HeaderParams_{{{paramName}}}{{/lambda.lowercase}}"
{{/isByteArray}}
{{/pattern}}
{{#maximum}}
{{#minimum}}
range(min = {{minimum}}, max = {{maximum}}),
{{/minimum}}
{{^minimum}}
range(max = {{maximum}}),
{{/minimum}}
{{/maximum}}
{{#minimum}}
{{^maximum}}
range(min = {{minimum}}),
{{/maximum}}
{{/minimum}}
{{#maxItems}}
{{#minItems}}
length(min = {{minItems}}, max = {{maxItems}}),
{{/minItems}}
{{^minItems}}
length(max = {{maxItems}}),
{{/minItems}}
{{/maxItems}}
{{^maxItems}}
{{#minItems}}
length(min = {{minItems}}),
{{/minItems}}
{{/maxItems}}
)]
{{/hasValidation}}
pub {{{paramName}}}: {{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}},
{{/headerParams}}
}
{{#headerParams}}
{{#hasValidation}}
{{#pattern}}
{{^isByteArray}}
lazy_static::lazy_static! {
static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}HeaderParams_{{{paramName}}}{{/lambda.uppercase}}: regex::Regex = regex::Regex::new(r"{{ pattern }}").unwrap();
}
{{/isByteArray}}
{{#isByteArray}}
lazy_static::lazy_static! {
static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}HeaderParams_{{{paramName}}}{{/lambda.uppercase}}: regex::bytes::Regex = regex::bytes::Regex::new(r"{{ pattern }}").unwrap();
}
fn validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}HeaderParams_{{{paramName}}}{{/lambda.lowercase}}(
b: &ByteArray
) -> std::result::Result<(), validator::ValidationError> {
if !RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}HeaderParams_{{{paramName}}}{{/lambda.uppercase}}.is_match(&b.0) {
return Err(validator::ValidationError::new("Character not allowed"));
}
std::result::Result::Ok(())
}
{{/isByteArray}}
{{/pattern}}
{{/hasValidation}}
{{/headerParams}}
{{/headerParams.size}}
{{#pathParams.size}}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct {{{operationIdCamelCase}}}PathParams {
{{#pathParams}}
{{#description}}
/// {{{.}}}
{{/description}}
{{#isEnum}}
/// Note: inline enums are not fully supported by openapi-generator
{{/isEnum}}
{{#hasValidation}}
#[validate(
{{#maxLength}}
{{#minLength}}
length(min = {{minLength}}, max = {{maxLength}}),
{{/minLength}}
{{^minLength}}
length(max = {{maxLength}}),
{{/minLength}}
{{/maxLength}}
{{^maxLength}}
{{#minLength}}
length(min = {{minLength}}),
{{/minLength}}
{{/maxLength}}
{{#pattern}}
{{^isByteArray}}
regex = "RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}PathParams_{{{paramName}}}{{/lambda.uppercase}}",
{{/isByteArray}}
{{#isByteArray}}
custom ="validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}PathParams_{{{paramName}}}{{/lambda.lowercase}}"
{{/isByteArray}}
{{/pattern}}
{{#maximum}}
{{#minimum}}
range(min = {{minimum}}, max = {{maximum}}),
{{/minimum}}
{{^minimum}}
range(max = {{maximum}}),
{{/minimum}}
{{/maximum}}
{{#minimum}}
{{^maximum}}
range(min = {{minimum}}),
{{/maximum}}
{{/minimum}}
{{#maxItems}}
{{#minItems}}
length(min = {{minItems}}, max = {{maxItems}}),
{{/minItems}}
{{^minItems}}
length(max = {{maxItems}}),
{{/minItems}}
{{/maxItems}}
{{^maxItems}}
{{#minItems}}
length(min = {{minItems}}),
{{/minItems}}
{{/maxItems}}
)]
{{/hasValidation}}
{{#required}}
pub {{{paramName}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}},
{{/required}}
{{^required}}
{{#isNullable}}
#[serde(deserialize_with = "deserialize_optional_nullable")]
#[serde(default = "default_optional_nullable")]
{{/isNullable}}
#[serde(skip_serializing_if="Option::is_none")]
pub {{{paramName}}}: Option<{{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}>,
{{/required}}
{{/pathParams}}
}
{{#pathParams}}
{{#hasValidation}}
{{#pattern}}
{{^isByteArray}}
lazy_static::lazy_static! {
static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}PathParams_{{{paramName}}}{{/lambda.uppercase}}: regex::Regex = regex::Regex::new(r"{{ pattern }}").unwrap();
}
{{/isByteArray}}
{{#isByteArray}}
lazy_static::lazy_static! {
static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}PathParams_{{{paramName}}}{{/lambda.uppercase}}: regex::bytes::Regex = regex::bytes::Regex::new(r"{{ pattern }}").unwrap();
}
fn validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}PathParams_{{{paramName}}}{{/lambda.lowercase}}(
b: &ByteArray
) -> std::result::Result<(), validator::ValidationError> {
if !RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}PathParams_{{{paramName}}}{{/lambda.uppercase}}.is_match(&b.0) {
return Err(validator::ValidationError::new("Character not allowed"));
}
std::result::Result::Ok(())
}
{{/isByteArray}}
{{/pattern}}
{{/hasValidation}}
{{/pathParams}}
{{/pathParams.size}}
{{#queryParams.size}}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct {{{operationIdCamelCase}}}QueryParams {
{{#queryParams}}
{{#vendorExtensions}}
{{#description}}
/// {{{.}}}
{{/description}}
{{#isEnum}}
/// Note: inline enums are not fully supported by openapi-generator
{{/isEnum}}
#[serde(rename = "{{{baseName}}}")]
{{#hasValidation}}
#[validate(
{{#maxLength}}
{{#minLength}}
length(min = {{minLength}}, max = {{maxLength}}),
{{/minLength}}
{{^minLength}}
length(max = {{maxLength}}),
{{/minLength}}
{{/maxLength}}
{{^maxLength}}
{{#minLength}}
length(min = {{minLength}}),
{{/minLength}}
{{/maxLength}}
{{#pattern}}
{{^isByteArray}}
regex = "RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}QueryParams_{{{paramName}}}{{/lambda.uppercase}}",
{{/isByteArray}}
{{#isByteArray}}
custom ="validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}QueryParams_{{{paramName}}}{{/lambda.lowercase}}"
{{/isByteArray}}
{{/pattern}}
{{#maximum}}
{{#minimum}}
range(min = {{minimum}}, max = {{maximum}}),
{{/minimum}}
{{^minimum}}
range(max = {{maximum}}),
{{/minimum}}
{{/maximum}}
{{#minimum}}
{{^maximum}}
range(min = {{minimum}}),
{{/maximum}}
{{/minimum}}
{{#maxItems}}
{{#minItems}}
length(min = {{minItems}}, max = {{maxItems}}),
{{/minItems}}
{{^minItems}}
length(max = {{maxItems}}),
{{/minItems}}
{{/maxItems}}
{{^maxItems}}
{{#minItems}}
length(min = {{minItems}}),
{{/minItems}}
{{/maxItems}}
)]
{{/hasValidation}}
{{#required}}
pub {{{paramName}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}},
{{/required}}
{{^required}}
{{#isNullable}}
#[serde(deserialize_with = "deserialize_optional_nullable")]
#[serde(default = "default_optional_nullable")]
{{/isNullable}}
#[serde(skip_serializing_if="Option::is_none")]
pub {{{paramName}}}: Option<{{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}>,
{{/required}}
{{/vendorExtensions}}
{{/queryParams}}
}
{{#queryParams}}
{{#hasValidation}}
{{#pattern}}
{{^isByteArray}}
lazy_static::lazy_static! {
static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}QueryParams_{{{paramName}}}{{/lambda.uppercase}}: regex::Regex = regex::Regex::new(r"{{ pattern }}").unwrap();
}
{{/isByteArray}}
{{#isByteArray}}
lazy_static::lazy_static! {
static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}QueryParams_{{{paramName}}}{{/lambda.uppercase}}: regex::bytes::Regex = regex::bytes::Regex::new(r"{{ pattern }}").unwrap();
}
fn validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}QueryParams_{{{paramName}}}{{/lambda.lowercase}}(
b: &ByteArray
) -> std::result::Result<(), validator::ValidationError> {
if !RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}QueryParams_{{{paramName}}}{{/lambda.uppercase}}.is_match(&b.0) {
return Err(validator::ValidationError::new("Character not allowed"));
}
std::result::Result::Ok(())
}
{{/isByteArray}}
{{/pattern}}
{{/hasValidation}}
{{/queryParams}}
{{/queryParams.size}}
{{/vendorExtensions}}
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
{{! Don't "use" structs here - they can conflict with the names of models, and mean that the code won't compile }}
{{#models}}
{{#model}}
{{#description}}
/// {{{.}}}
{{/description}}
{{#isEnum}}
/// Enumeration of values.
/// Since this enum's variants do not hold data, we can easily define them as `#[repr(C)]`
/// which helps with FFI.
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "conversion", derive(frunk_enum_derive::LabelledGenericEnum))]
pub enum {{{classname}}} {
{{#allowableValues}}
{{#enumVars}}
#[serde(rename = {{{value}}})]
{{{name}}},
{{/enumVars}}
{{/allowableValues}}
}
impl std::fmt::Display for {{{classname}}} {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
{{#allowableValues}}
{{#enumVars}}
{{{classname}}}::{{{name}}} => write!(f, {{{value}}}),
{{/enumVars}}
{{/allowableValues}}
}
}
}
impl std::str::FromStr for {{{classname}}} {
type Err = String;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
{{#allowableValues}}
{{#enumVars}}
{{{value}}} => std::result::Result::Ok({{{classname}}}::{{{name}}}),
{{/enumVars}}
{{/allowableValues}}
_ => std::result::Result::Err(format!("Value not valid: {}", s)),
}
}
}
{{/isEnum}}
{{^isEnum}}
{{#dataType}}
{{#isMap}}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
{{/isMap}}
{{^isMap}}
#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)]
{{/isMap}}
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct {{{classname}}}({{{dataType}}});
impl validator::Validate for {{{classname}}} {
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
std::result::Result::Ok(())
}
}
impl std::convert::From<{{{dataType}}}> for {{{classname}}} {
fn from(x: {{{dataType}}}) -> Self {
{{{classname}}}(x)
}
}
{{#vendorExtensions.x-is-string}}
impl std::string::ToString for {{{classname}}} {
fn to_string(&self) -> String {
self.0.to_string()
}
}
impl std::str::FromStr for {{{classname}}} {
type Err = std::string::ParseError;
fn from_str(x: &str) -> std::result::Result<Self, Self::Err> {
std::result::Result::Ok({{{classname}}}(x.to_string()))
}
}
{{/vendorExtensions.x-is-string}}
impl std::convert::From<{{{classname}}}> for {{{dataType}}} {
fn from(x: {{{classname}}}) -> Self {
x.0
}
}
impl std::ops::Deref for {{{classname}}} {
type Target = {{{dataType}}};
fn deref(&self) -> &{{{dataType}}} {
&self.0
}
}
impl std::ops::DerefMut for {{{classname}}} {
fn deref_mut(&mut self) -> &mut {{{dataType}}} {
&mut self.0
}
}
{{#additionalPropertiesType}}
/// Converts the {{{classname}}} value to the Query Parameters representation (style=form, explode=false)
/// specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde serializer
impl ::std::string::ToString for {{{classname}}} {
fn to_string(&self) -> String {
// Skipping additionalProperties in query parameter serialization
"".to_string()
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl ::std::str::FromStr for {{{classname}}} {
type Err = &'static str;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
std::result::Result::Err("Parsing additionalProperties for {{{classname}}} is not supported")
}
}
{{/additionalPropertiesType}}
{{/dataType}}
{{^dataType}}
{{#arrayModelType}}
{{! vec}}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct {{{classname}}}(Vec<{{{arrayModelType}}}>);
impl validator::Validate for {{{classname}}} {
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
std::result::Result::Ok(())
}
}
impl std::convert::From<Vec<{{{arrayModelType}}}>> for {{{classname}}} {
fn from(x: Vec<{{{arrayModelType}}}>) -> Self {
{{{classname}}}(x)
}
}
impl std::convert::From<{{{classname}}}> for Vec<{{{arrayModelType}}}> {
fn from(x: {{{classname}}}) -> Self {
x.0
}
}
impl std::iter::FromIterator<{{{arrayModelType}}}> for {{{classname}}} {
fn from_iter<U: IntoIterator<Item={{{arrayModelType}}}>>(u: U) -> Self {
{{{classname}}}(Vec::<{{{arrayModelType}}}>::from_iter(u))
}
}
impl std::iter::IntoIterator for {{{classname}}} {
type Item = {{{arrayModelType}}};
type IntoIter = std::vec::IntoIter<{{{arrayModelType}}}>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> std::iter::IntoIterator for &'a {{{classname}}} {
type Item = &'a {{{arrayModelType}}};
type IntoIter = std::slice::Iter<'a, {{{arrayModelType}}}>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl<'a> std::iter::IntoIterator for &'a mut {{{classname}}} {
type Item = &'a mut {{{arrayModelType}}};
type IntoIter = std::slice::IterMut<'a, {{{arrayModelType}}}>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter_mut()
}
}
impl std::ops::Deref for {{{classname}}} {
type Target = Vec<{{{arrayModelType}}}>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for {{{classname}}} {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
/// Converts the {{{classname}}} value to the Query Parameters representation (style=form, explode=false)
/// specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde serializer
impl std::string::ToString for {{{classname}}} {
fn to_string(&self) -> String {
self.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",")
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl std::str::FromStr for {{{classname}}} {
type Err = <{{{arrayModelType}}} as std::str::FromStr>::Err;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let mut items = vec![];
for item in s.split(',')
{
items.push(item.parse()?);
}
std::result::Result::Ok({{{classname}}}(items))
}
}
{{/arrayModelType}}
{{^arrayModelType}}
{{! general struct}}
{{#anyOf.size}}
/// Any of:
{{#anyOf}}
/// - {{{.}}}
{{/anyOf}}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct {{{classname}}}(Box<serde_json::value::RawValue>);
impl validator::Validate for {{{classname}}}
{
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
std::result::Result::Ok(())
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl std::str::FromStr for {{{classname}}} {
type Err = serde_json::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
serde_json::from_str(s)
}
}
impl PartialEq for {{{classname}}} {
fn eq(&self, other: &Self) -> bool {
self.0.get().eq(other.0.get())
}
}
{{/anyOf.size}}
{{#oneOf.size}}
/// One of:
{{#oneOf}}
/// - {{{.}}}
{{/oneOf}}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct {{{classname}}}(Box<serde_json::value::RawValue>);
impl validator::Validate for {{{classname}}}
{
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
std::result::Result::Ok(())
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl std::str::FromStr for {{{classname}}} {
type Err = serde_json::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
serde_json::from_str(s)
}
}
impl PartialEq for {{{classname}}} {
fn eq(&self, other: &Self) -> bool {
self.0.get().eq(other.0.get())
}
}
{{/oneOf.size}}
{{^anyOf.size}}
{{^oneOf.size}}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct {{{classname}}} {
{{#vars}}
{{#description}}
/// {{{.}}}
{{/description}}
{{#isEnum}}
/// Note: inline enums are not fully supported by openapi-generator
{{/isEnum}}
#[serde(rename = "{{{baseName}}}")]
{{#hasValidation}}
#[validate(
{{#maxLength}}
{{#minLength}}
length(min = {{minLength}}, max = {{maxLength}}),
{{/minLength}}
{{^minLength}}
length(max = {{maxLength}}),
{{/minLength}}
{{/maxLength}}
{{^maxLength}}
{{#minLength}}
length(min = {{minLength}}),
{{/minLength}}
{{/maxLength}}
{{#pattern}}
{{^isByteArray}}
regex = "RE_{{#lambda.uppercase}}{{{classname}}}_{{{name}}}{{/lambda.uppercase}}",
{{/isByteArray}}
{{#isByteArray}}
custom ="validate_byte_{{#lambda.lowercase}}{{{classname}}}_{{{name}}}{{/lambda.lowercase}}"
{{/isByteArray}}
{{/pattern}}
{{#maximum}}
{{#minimum}}
range(min = {{minimum}}, max = {{maximum}}),
{{/minimum}}
{{^minimum}}
range(max = {{maximum}}),
{{/minimum}}
{{/maximum}}
{{#minimum}}
{{^maximum}}
range(min = {{minimum}}),
{{/maximum}}
{{/minimum}}
{{#maxItems}}
{{#minItems}}
length(min = {{minItems}}, max = {{maxItems}}),
{{/minItems}}
{{^minItems}}
length(max = {{maxItems}}),
{{/minItems}}
{{/maxItems}}
{{^maxItems}}
{{#minItems}}
length(min = {{minItems}}),
{{/minItems}}
{{/maxItems}}
)]
{{/hasValidation}}
{{#required}}
pub {{{name}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}},
{{/required}}
{{^required}}
{{#isNullable}}
#[serde(deserialize_with = "deserialize_optional_nullable")]
#[serde(default = "default_optional_nullable")]
{{/isNullable}}
#[serde(skip_serializing_if="Option::is_none")]
pub {{{name}}}: Option<{{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}>,
{{/required}}
{{/vars}}
}
{{#vars}}
{{#hasValidation}}
{{#pattern}}
{{^isByteArray}}
lazy_static::lazy_static! {
static ref RE_{{#lambda.uppercase}}{{{classname}}}_{{{name}}}{{/lambda.uppercase}}: regex::Regex = regex::Regex::new(r"{{ pattern }}").unwrap();
}
{{/isByteArray}}
{{#isByteArray}}
lazy_static::lazy_static! {
static ref RE_{{#lambda.uppercase}}{{{classname}}}_{{{name}}}{{/lambda.uppercase}}: regex::bytes::Regex = regex::bytes::Regex::new(r"{{ pattern }}").unwrap();
}
fn validate_byte_{{#lambda.lowercase}}{{{classname}}}_{{{name}}}{{/lambda.lowercase}}(
b: &ByteArray
) -> std::result::Result<(), validator::ValidationError> {
if !RE_{{#lambda.uppercase}}{{{classname}}}_{{{name}}}{{/lambda.uppercase}}.is_match(&b.0) {
return Err(validator::ValidationError::new("Character not allowed"));
}
std::result::Result::Ok(())
}
{{/isByteArray}}
{{/pattern}}
{{/hasValidation}}
{{/vars}}
impl {{{classname}}} {
#[allow(clippy::new_without_default, clippy::too_many_arguments)]
pub fn new({{#vars}}{{^defaultValue}}{{{name}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, {{/defaultValue}}{{/vars}}) -> {{{classname}}} {
{{{classname}}} {
{{#vars}} {{#defaultValue}}{{{name}}}: {{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}{{{name}}}{{/defaultValue}},
{{/vars}}
}
}
}
/// Converts the {{{classname}}} value to the Query Parameters representation (style=form, explode=false)
/// specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde serializer
impl std::string::ToString for {{{classname}}} {
fn to_string(&self) -> String {
let params: Vec<Option<String>> = vec![
{{#vars}}
{{#isByteArray}}
// Skipping {{baseName}} in query parameter serialization
{{/isByteArray}}
{{#isBinary}}
// Skipping {{baseName}} in query parameter serialization
{{/isBinary}}
{{#isMap}}
// Skipping {{baseName}} in query parameter serialization
{{/isMap}}
{{^isPrimitiveType}}
// Skipping {{baseName}} in query parameter serialization
{{/isPrimitiveType}}
{{^isByteArray}}{{^isBinary}}{{^isMap}}{{#isPrimitiveType}}
{{#required}}
Some("{{{baseName}}}".to_string()),
{{^isArray}}
{{#isNullable}}
Some(self.{{{name}}}.as_ref().map_or("null".to_string(), |x| x.to_string())),
{{/isNullable}}
{{^isNullable}}
Some(self.{{{name}}}.to_string()),
{{/isNullable}}
{{/isArray}}
{{#isArray}}
{{#isNullable}}
Some(self.{{{name}}}.as_ref().map_or(vec!["null".to_string()], |x| x.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(","))),
{{/isNullable}}
{{^isNullable}}
Some(self.{{{name}}}.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",")),
{{/isNullable}}
{{/isArray}}
{{/required}}
{{^required}}
self.{{{name}}}.as_ref().map(|{{{name}}}| {
[
"{{{baseName}}}".to_string(),
{{^isArray}}
{{#isNullable}}
{{{name}}}.as_ref().map_or("null".to_string(), |x| x.to_string()),
{{/isNullable}}
{{^isNullable}}
{{{name}}}.to_string(),
{{/isNullable}}
{{/isArray}}
{{#isArray}}
{{#isNullable}}
{{{name}}}.as_ref().map_or("null".to_string(), |x| x.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",")),
{{/isNullable}}
{{^isNullable}}
{{{name}}}.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(","),
{{/isNullable}}
{{/isArray}}
].join(",")
}),
{{/required}}
{{/isPrimitiveType}}{{/isMap}}{{/isBinary}}{{/isByteArray}}
{{/vars}}
];
params.into_iter().flatten().collect::<Vec<_>>().join(",")
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl std::str::FromStr for {{{classname}}} {
type Err = String;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
/// An intermediate representation of the struct to use for parsing.
#[derive(Default)]
#[allow(dead_code)]
struct IntermediateRep {
{{#vars}}
pub {{{name}}}: Vec<{{{dataType}}}>,
{{/vars}}
}
let mut intermediate_rep = IntermediateRep::default();
// Parse into intermediate representation
let mut string_iter = s.split(',');
let mut key_result = string_iter.next();
while key_result.is_some() {
let val = match string_iter.next() {
Some(x) => x,
None => return std::result::Result::Err("Missing value while parsing {{{classname}}}".to_string())
};
if let Some(key) = key_result {
#[allow(clippy::match_single_binding)]
match key {
{{#vars}}
{{#isBinary}}
"{{{baseName}}}" => return std::result::Result::Err("Parsing binary data in this style is not supported in {{{classname}}}".to_string()),
{{/isBinary}}
{{^isBinary}}
{{#isByteArray}}
"{{{baseName}}}" => return std::result::Result::Err("Parsing binary data in this style is not supported in {{{classname}}}".to_string()),
{{/isByteArray}}
{{^isByteArray}}
{{#isContainer}}
"{{{baseName}}}" => return std::result::Result::Err("Parsing a container in this style is not supported in {{{classname}}}".to_string()),
{{/isContainer}}
{{^isContainer}}
{{#isNullable}}
"{{{baseName}}}" => return std::result::Result::Err("Parsing a nullable type in this style is not supported in {{{classname}}}".to_string()),
{{/isNullable}}
{{^isNullable}}
#[allow(clippy::redundant_clone)]
"{{{baseName}}}" => intermediate_rep.{{{name}}}.push(<{{{dataType}}} as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?),
{{/isNullable}}
{{/isContainer}}
{{/isByteArray}}
{{/isBinary}}
{{/vars}}
_ => return std::result::Result::Err("Unexpected key while parsing {{{classname}}}".to_string())
}
}
// Get the next key
key_result = string_iter.next();
}
// Use the intermediate representation to return the struct
std::result::Result::Ok({{{classname}}} {
{{#vars}}
{{#isNullable}}
{{{name}}}: std::result::Result::Err("Nullable types not supported in {{{classname}}}".to_string())?,
{{/isNullable}}
{{^isNullable}}
{{{name}}}: intermediate_rep.{{{name}}}.into_iter().next(){{#required}}.ok_or_else(|| "{{{baseName}}} missing in {{{classname}}}".to_string())?{{/required}},
{{/isNullable}}
{{/vars}}
})
}
}
{{/oneOf.size}}
{{/anyOf.size}}
{{/arrayModelType}}
{{^anyOf.size}}
{{^oneOf.size}}
// Methods for converting between header::IntoHeaderValue<{{{classname}}}> and HeaderValue
#[cfg(feature = "server")]
impl std::convert::TryFrom<header::IntoHeaderValue<{{{classname}}}>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: header::IntoHeaderValue<{{{classname}}}>) -> std::result::Result<Self, Self::Error> {
let hdr_value = hdr_value.to_string();
match HeaderValue::from_str(&hdr_value) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(e) => std::result::Result::Err(
format!("Invalid header value for {{classname}} - value: {} is invalid {}",
hdr_value, e))
}
}
}
#[cfg(feature = "server")]
impl std::convert::TryFrom<HeaderValue> for header::IntoHeaderValue<{{{classname}}}> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> std::result::Result<Self, Self::Error> {
match hdr_value.to_str() {
std::result::Result::Ok(value) => {
match <{{{classname}}} as std::str::FromStr>::from_str(value) {
std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)),
std::result::Result::Err(err) => std::result::Result::Err(
format!("Unable to convert header value '{}' into {{classname}} - {}",
value, err))
}
},
std::result::Result::Err(e) => std::result::Result::Err(
format!("Unable to convert header: {:?} to string: {}",
hdr_value, e))
}
}
}
{{/oneOf.size}}
{{/anyOf.size}}
{{/dataType}}
{{/isEnum}}
{{/model}}
{{/models}}

View File

@ -0,0 +1,71 @@
#[derive(Debug, PartialEq, Serialize, Deserialize)]
{{#vendorExtensions.x-must-use-response}}
#[must_use]
#[allow(clippy::large_enum_variant)]
{{/vendorExtensions.x-must-use-response}}
pub enum {{{operationId}}}Response {
{{#responses}}
{{#message}}
/// {{{.}}}{{/message}}
{{#vendorExtensions}}
{{{x-response-id}}}
{{/vendorExtensions}}
{{^dataType}}
{{#hasHeaders}}
{
{{/hasHeaders}}
{{/dataType}}
{{#dataType}}
{{^hasHeaders}}
{{#vendorExtensions}}
{{#x-produces-plain-text}}
(String)
{{/x-produces-plain-text}}
{{#x-produces-bytes}}
(ByteArray)
{{/x-produces-bytes}}
{{^x-produces-plain-text}}
{{^x-produces-bytes}}
({{{dataType}}})
{{/x-produces-bytes}}
{{/x-produces-plain-text}}
{{/vendorExtensions}}
{{/hasHeaders}}
{{#hasHeaders}}
{
{{#vendorExtensions}}
{{#x-produces-plain-text}}
body: String,
{{/x-produces-plain-text}}
{{#x-produces-bytes}}
body: ByteArray,
{{/x-produces-bytes}}
{{^x-produces-plain-text}}
{{^x-produces-bytes}}
body: {{{dataType}}},
{{/x-produces-bytes}}
{{/x-produces-plain-text}}
{{/vendorExtensions}}
{{/hasHeaders}}
{{/dataType}}
{{#headers}}
{{{name}}}:
{{^required}}
Option<
{{/required}}
{{{dataType}}}
{{^required}}
>
{{/required}}
{{^-last}}
,
{{/-last}}
{{#-last}}
}
{{/-last}}
{{/headers}}
{{^-last}}
,
{{/-last}}
{{/responses}}
}

View File

@ -0,0 +1,13 @@
use std::collections::HashMap;
use axum::{body::Body, extract::*, response::Response, routing::*};
use axum_extra::extract::{CookieJar, Multipart};
use bytes::Bytes;
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
use tracing::error;
use validator::{Validate, ValidationErrors};
use crate::{header, types::*};
#[allow(unused_imports)]
use crate::models;

View File

@ -0,0 +1,16 @@
{{>server-imports}}
use crate::{Api{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}},
{{{operationId}}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
};
{{>server-route}}
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{>server-operation-validate}}
{{>server-operation}}
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}

View File

@ -0,0 +1,221 @@
{{^x-consumes-multipart-related}}
{{^x-consumes-multipart}}
{{#bodyParam}}
{{#vendorExtensions}}
#[derive(validator::Validate)]
#[allow(dead_code)]
struct {{{operationIdCamelCase}}}BodyValidator<'a> {
{{#hasValidation}}
#[validate(
{{#maxLength}}
{{#minLength}}
length(min = {{minLength}}, max = {{maxLength}}),
{{/minLength}}
{{^minLength}}
length(max = {{maxLength}}),
{{/minLength}}
{{/maxLength}}
{{^maxLength}}
{{#minLength}}
length(min = {{minLength}}),
{{/minLength}}
{{/maxLength}}
{{#pattern}}
{{#isString}}
regex = "RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.uppercase}}",
{{/isString}}
{{^isString}}
custom ="validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.lowercase}}",
{{/isString}}
{{/pattern}}
{{#maximum}}
{{#minimum}}
range(min = {{minimum}}, max = {{maximum}}),
{{/minimum}}
{{^minimum}}
range(max = {{maximum}}),
{{/minimum}}
{{/maximum}}
{{#minimum}}
{{^maximum}}
range(min = {{minimum}}),
{{/maximum}}
{{/minimum}}
{{#maxItems}}
{{#minItems}}
length(min = {{minItems}}, max = {{maxItems}}),
{{/minItems}}
{{^minItems}}
length(max = {{maxItems}}),
{{/minItems}}
{{/maxItems}}
{{^maxItems}}
{{#minItems}}
length(min = {{minItems}}),
{{/minItems}}
{{/maxItems}}
)]
{{/hasValidation}}
{{^x-consumes-plain-text}}
{{^hasValidation}}
{{^isMap}}
#[validate]
{{/isMap}}
{{/hasValidation}}
body: &'a {{{dataType}}},
{{/x-consumes-plain-text}}
{{#x-consumes-plain-text}}
{{#isString}}
body: &'a String,
{{/isString}}
{{^isString}}
body: &'a [u8],
{{/isString}}
{{/x-consumes-plain-text}}
}
{{#hasValidation}}
{{#pattern}}
{{#isString}}
lazy_static::lazy_static! {
static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.uppercase}}: regex::Regex = regex::Regex::new(r"{{ pattern }}").unwrap();
}
{{/isString}}
{{^isString}}
lazy_static::lazy_static! {
static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.uppercase}}: regex::bytes::Regex = regex::bytes::Regex::new(r"{{ pattern }}").unwrap();
}
fn validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.lowercase}}(
b: &[u8]
) -> std::result::Result<(), validator::ValidationError> {
if !RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.uppercase}}.is_match(b) {
return Err(validator::ValidationError::new("Character not allowed"));
}
Ok(())
}
{{/isString}}
{{/pattern}}
{{/hasValidation}}
{{/vendorExtensions}}
{{/bodyParam}}
{{/x-consumes-multipart}}
{{/x-consumes-multipart-related}}
#[tracing::instrument(skip_all)]
fn {{#vendorExtensions}}{{{x-operation-id}}}_validation{{/vendorExtensions}}(
{{#headerParams.size}}
header_params: models::{{{operationIdCamelCase}}}HeaderParams,
{{/headerParams.size}}
{{#pathParams.size}}
path_params: models::{{{operationIdCamelCase}}}PathParams,
{{/pathParams.size}}
{{#queryParams.size}}
query_params: models::{{{operationIdCamelCase}}}QueryParams,
{{/queryParams.size}}
{{^x-consumes-multipart-related}}
{{^x-consumes-multipart}}
{{#bodyParam}}
{{#vendorExtensions}}
{{^x-consumes-plain-text}}
body: {{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}},
{{/x-consumes-plain-text}}
{{#x-consumes-plain-text}}
{{#isString}}
body: String,
{{/isString}}
{{^isString}}
body: Bytes,
{{/isString}}
{{/x-consumes-plain-text}}
{{/vendorExtensions}}
{{/bodyParam}}
{{/x-consumes-multipart}}
{{/x-consumes-multipart-related}}
) -> std::result::Result<(
{{#headerParams.size}}
models::{{{operationIdCamelCase}}}HeaderParams,
{{/headerParams.size}}
{{#pathParams.size}}
models::{{{operationIdCamelCase}}}PathParams,
{{/pathParams.size}}
{{#queryParams.size}}
models::{{{operationIdCamelCase}}}QueryParams,
{{/queryParams.size}}
{{^x-consumes-multipart-related}}
{{^x-consumes-multipart}}
{{#bodyParam}}
{{#vendorExtensions}}
{{^x-consumes-plain-text}}
{{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}},
{{/x-consumes-plain-text}}
{{#x-consumes-plain-text}}
{{#isString}}
String,
{{/isString}}
{{^isString}}
Bytes,
{{/isString}}
{{/x-consumes-plain-text}}
{{/vendorExtensions}}
{{/bodyParam}}
{{/x-consumes-multipart}}
{{/x-consumes-multipart-related}}
), ValidationErrors>
{
{{#headerParams.size}}
header_params.validate()?;
{{/headerParams.size}}
{{#pathParams.size}}
path_params.validate()?;
{{/pathParams.size}}
{{#queryParams.size}}
query_params.validate()?;
{{/queryParams.size}}
{{^x-consumes-multipart-related}}
{{^x-consumes-multipart}}
{{#bodyParam}}
{{#vendorExtensions}}
{{^x-consumes-plain-text}}
{{#required}}
let b = {{{operationIdCamelCase}}}BodyValidator { body: &body };
b.validate()?;
{{/required}}
{{^required}}
if let Some(body) = &body {
let b = {{{operationIdCamelCase}}}BodyValidator { body };
b.validate()?;
}
{{/required}}
{{/x-consumes-plain-text}}
{{#x-consumes-plain-text}}
{{#hasValidation}}
let b = {{{operationIdCamelCase}}}BodyValidator { body: &body };
b.validate()?;
{{/hasValidation}}
{{/x-consumes-plain-text}}
{{/vendorExtensions}}
{{/bodyParam}}
{{/x-consumes-multipart}}
{{/x-consumes-multipart-related}}
Ok((
{{#headerParams.size}}
header_params,
{{/headerParams.size}}
{{#pathParams.size}}
path_params,
{{/pathParams.size}}
{{#queryParams.size}}
query_params,
{{/queryParams.size}}
{{^x-consumes-multipart-related}}
{{^x-consumes-multipart}}
{{#bodyParam}}
{{#vendorExtensions}}
body,
{{/vendorExtensions}}
{{/bodyParam}}
{{/x-consumes-multipart}}
{{/x-consumes-multipart-related}}
))
}

View File

@ -0,0 +1,309 @@
/// {{{operationId}}} - {{{httpMethod}}} {{{basePathWithoutHost}}}{{{path}}}
#[tracing::instrument(skip_all)]
async fn {{#vendorExtensions}}{{{x-operation-id}}}{{/vendorExtensions}}<I, A>(
method: Method,
host: Host,
cookies: CookieJar,
{{#headerParams.size}}
headers: HeaderMap,
{{/headerParams.size}}
{{#pathParams.size}}
Path(path_params): Path<models::{{{operationIdCamelCase}}}PathParams>,
{{/pathParams.size}}
{{#queryParams.size}}
Query(query_params): Query<models::{{{operationIdCamelCase}}}QueryParams>,
{{/queryParams.size}}
State(api_impl): State<I>,
{{#vendorExtensions}}
{{^x-consumes-multipart-related}}
{{^x-consumes-multipart}}
{{#bodyParam}}
{{#vendorExtensions}}
{{#x-consumes-json}}
Json(body): Json<{{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}}>,
{{/x-consumes-json}}
{{#x-consumes-form-urlencoded}}
Form(body): Form<{{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}}>,
{{/x-consumes-form-urlencoded}}
{{#x-consumes-plain-text}}
{{#isString}}
body: String,
{{/isString}}
{{^isString}}
body: Bytes,
{{/isString}}
{{/x-consumes-plain-text}}
{{/vendorExtensions}}
{{/bodyParam}}
{{/x-consumes-multipart}}
{{/x-consumes-multipart-related}}
{{#x-consumes-multipart}}
body: Multipart,
{{/x-consumes-multipart}}
{{#x-consumes-multipart-related}}
body: axum::body::Body,
{{/x-consumes-multipart-related}}
{{/vendorExtensions}}
) -> Result<Response, StatusCode>
where
I: AsRef<A> + Send + Sync,
A: Api,
{
{{#headerParams}}
{{#-first}}
// Header parameters
let header_params = {
{{/-first}}
let header_{{{paramName}}} = headers.get(HeaderName::from_static("{{{nameInLowerCase}}}"));
let header_{{{paramName}}} = match header_{{{paramName}}} {
Some(v) => match header::IntoHeaderValue::<{{{dataType}}}>::try_from((*v).clone()) {
Ok(result) =>
{{#required}}
result.0,
{{/required}}
{{^required}}
Some(result.0),
{{/required}}
Err(err) => {
return Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Invalid header {{{baseName}}} - {}", err))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR });
},
},
None => {
{{#required}}
return Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from("Missing required header {{{baseName}}}")).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR });
{{/required}}
{{^required}}
None
{{/required}}
}
};
{{/headerParams}}
{{#headerParams}}
{{#-first}}
models::{{{operationIdCamelCase}}}HeaderParams {
{{/-first}}
{{{paramName}}}: header_{{{paramName}}},
{{#-last}}
}
};
{{/-last}}
{{/headerParams}}
{{^disableValidator}}
{{^allowBlockingValidator}}
#[allow(clippy::redundant_closure)]
let validation = tokio::task::spawn_blocking(move ||
{{/allowBlockingValidator}}
{{#allowBlockingValidator}}
let validation =
{{/allowBlockingValidator}}
{{#vendorExtensions}}{{{x-operation-id}}}_validation{{/vendorExtensions}}(
{{#headerParams.size}}
header_params,
{{/headerParams.size}}
{{#pathParams.size}}
path_params,
{{/pathParams.size}}
{{#queryParams.size}}
query_params,
{{/queryParams.size}}
{{^x-consumes-multipart-related}}
{{^x-consumes-multipart}}
{{#bodyParam}}
body,
{{/bodyParam}}
{{/x-consumes-multipart}}
{{/x-consumes-multipart-related}}
)
{{^allowBlockingValidator}}).await.unwrap(){{/allowBlockingValidator}};
let Ok((
{{#headerParams.size}}
header_params,
{{/headerParams.size}}
{{#pathParams.size}}
path_params,
{{/pathParams.size}}
{{#queryParams.size}}
query_params,
{{/queryParams.size}}
{{^x-consumes-multipart-related}}
{{^x-consumes-multipart}}
{{#bodyParam}}
body,
{{/bodyParam}}
{{/x-consumes-multipart}}
{{/x-consumes-multipart-related}}
)) = validation else {
return Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(validation.unwrap_err().to_string()))
.map_err(|_| StatusCode::BAD_REQUEST);
};
{{/disableValidator}}
let result = api_impl.as_ref().{{#vendorExtensions}}{{{x-operation-id}}}{{/vendorExtensions}}(
method,
host,
cookies,
{{#headerParams.size}}
header_params,
{{/headerParams.size}}
{{#pathParams.size}}
path_params,
{{/pathParams.size}}
{{#queryParams.size}}
query_params,
{{/queryParams.size}}
{{#vendorExtensions}}
{{^x-consumes-multipart-related}}
{{^x-consumes-multipart}}
{{#bodyParams}}
{{#-first}}
body,
{{/-first}}
{{/bodyParams}}
{{/x-consumes-multipart}}
{{/x-consumes-multipart-related}}
{{#x-consumes-multipart}}
body,
{{/x-consumes-multipart}}
{{#x-consumes-multipart-related}}
body,
{{/x-consumes-multipart-related}}
{{/vendorExtensions}}
).await;
let mut response = Response::builder();
let resp = match result {
Ok(rsp) => match rsp {
{{#responses}}
{{{operationId}}}Response::{{#vendorExtensions}}{{x-response-id}}{{/vendorExtensions}}
{{#dataType}}
{{^headers}}
(body)
{{/headers}}
{{#headers}}
{{#-first}}
{
body,
{{/-first}}
{{{name}}}{{^-last}},{{/-last}}
{{#-last}}
}
{{/-last}}
{{/headers}}
{{/dataType}}
{{^dataType}}
{{#headers}}
{{#-first}}
{
{{/-first}}
{{{name}}}{{^-last}},{{/-last}}
{{#-last}}
}
{{/-last}}
{{/headers}}
{{/dataType}}
=> {
{{#headers}}
{{^required}}
if let Some({{{name}}}) = {{{name}}} {
{{/required}}
let {{{name}}} = match header::IntoHeaderValue({{{name}}}).try_into() {
Ok(val) => val,
Err(e) => {
return Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::from(format!("An internal server error occurred handling {{name}} header - {}", e))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR });
}
};
{
let mut response_headers = response.headers_mut().unwrap();
response_headers.insert(
HeaderName::from_static("{{{nameInLowerCase}}}"),
{{name}}
);
}
{{^required}}
}
{{/required}}
{{/headers}}
let mut response = response.status({{{code}}});
{{#produces}}
{{#-first}}
{{#dataType}}
{{#vendorExtensions}}
{
let mut response_headers = response.headers_mut().unwrap();
response_headers.insert(
CONTENT_TYPE,
HeaderValue::from_str("{{{x-mime-type}}}").map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })?);
}
{{/vendorExtensions}}
{{/dataType}}
{{/-first}}
{{/produces}}
{{#dataType}}
{{#vendorExtensions}}
{{#x-produces-json}}
{{^allowBlockingResponseSerialize}}
let body_content = tokio::task::spawn_blocking(move ||
{{/allowBlockingResponseSerialize}}
{{#allowBlockingResponseSerialize}}
let body_content =
{{/allowBlockingResponseSerialize}}
serde_json::to_vec(&body).map_err(|e| {
error!(error = ?e);
StatusCode::INTERNAL_SERVER_ERROR
}){{^allowBlockingResponseSerialize}}).await.unwrap(){{/allowBlockingResponseSerialize}}?;
{{/x-produces-json}}
{{#x-produces-form-urlencoded}}
{{^allowBlockingResponseSerialize}}
let body_content = tokio::task::spawn_blocking(move ||
{{/allowBlockingResponseSerialize}}
{{#allowBlockingResponseSerialize}}
let body_content =
{{/allowBlockingResponseSerialize}}
serde_urlencoded::to_string(body).map_err(|e| {
error!(error = ?e);
StatusCode::INTERNAL_SERVER_ERROR
}){{^allowBlockingResponseSerialize}}).await.unwrap(){{/allowBlockingResponseSerialize}}?;
{{/x-produces-form-urlencoded}}
{{#x-produces-bytes}}
let body_content = body.0;
{{/x-produces-bytes}}
{{#x-produces-plain-text}}
let body_content = body;
{{/x-produces-plain-text}}
{{/vendorExtensions}}
response.body(Body::from(body_content))
{{/dataType}}
{{^dataType}}
response.body(Body::empty())
{{/dataType}}
},
{{/responses}}
},
Err(_) => {
// Application code returned an error. This should not happen, as the implementation should
// return a valid response.
response.status(500).body(Body::empty())
},
};
resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })
}

View File

@ -0,0 +1,15 @@
/// Setup API Server.
pub fn new<I, A>(api_impl: I) -> Router
where
I: AsRef<A> + Clone + Send + Sync + 'static,
A: Api + 'static,
{
// build our application with a route
Router::new()
{{#pathMethodOps}}
.route("{{{basePathWithoutHost}}}{{{path}}}",
{{#methodOperations}}{{{method}}}({{{operationID}}}::<I, A>){{^-last}}.{{/-last}}{{/methodOperations}}
)
{{/pathMethodOps}}
.with_state(api_impl)
}

View File

@ -0,0 +1,665 @@
use std::{mem, str::FromStr};
use base64::{engine::general_purpose, Engine};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[allow(dead_code)]
pub struct Object(serde_json::Value);
impl validator::Validate for Object {
fn validate(&self) -> Result<(), validator::ValidationErrors> {
Ok(())
}
}
impl FromStr for Object {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(serde_json::Value::String(s.to_owned())))
}
}
/// Serde helper function to create a default `Option<Nullable<T>>` while
/// deserializing
pub fn default_optional_nullable<T>() -> Option<Nullable<T>> {
None
}
/// Serde helper function to deserialize into an `Option<Nullable<T>>`
pub fn deserialize_optional_nullable<'de, D, T>(
deserializer: D,
) -> Result<Option<Nullable<T>>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
Option::<T>::deserialize(deserializer).map(|val| match val {
Some(inner) => Some(Nullable::Present(inner)),
None => Some(Nullable::Null),
})
}
/// The Nullable type. Represents a value which may be specified as null on an API.
/// Note that this is distinct from a value that is optional and not present!
///
/// Nullable implements many of the same methods as the Option type (map, unwrap, etc).
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum Nullable<T> {
/// Null value
Null,
/// Value is present
Present(T),
}
impl<T> Nullable<T> {
/////////////////////////////////////////////////////////////////////////
// Querying the contained values
/////////////////////////////////////////////////////////////////////////
/// Returns `true` if the Nullable is a `Present` value.
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let x: Nullable<u32> = Nullable::Present(2);
/// assert_eq!(x.is_present(), true);
///
/// let x: Nullable<u32> = Nullable::Null;
/// assert_eq!(x.is_present(), false);
/// ```
#[inline]
pub fn is_present(&self) -> bool {
match *self {
Nullable::Present(_) => true,
Nullable::Null => false,
}
}
/// Returns `true` if the Nullable is a `Null` value.
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let x: Nullable<u32> = Nullable::Present(2);
/// assert_eq!(x.is_null(), false);
///
/// let x: Nullable<u32> = Nullable::Null;
/// assert_eq!(x.is_null(), true);
/// ```
#[inline]
pub fn is_null(&self) -> bool {
!self.is_present()
}
/////////////////////////////////////////////////////////////////////////
// Adapter for working with references
/////////////////////////////////////////////////////////////////////////
/// Converts from `Nullable<T>` to `Nullable<&T>`.
///
/// # Examples
///
/// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original.
/// The [`map`] method takes the `self` argument by value, consuming the original,
/// so this technique uses `as_ref` to first take a `Nullable` to a reference
/// to the value inside the original.
///
/// [`map`]: enum.Nullable.html#method.map
/// [`String`]: ../../std/string/struct.String.html
/// [`usize`]: ../../std/primitive.usize.html
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let num_as_str: Nullable<String> = Nullable::Present("10".to_string());
/// // First, cast `Nullable<String>` to `Nullable<&String>` with `as_ref`,
/// // then consume *that* with `map`, leaving `num_as_str` on the stack.
/// let num_as_int: Nullable<usize> = num_as_str.as_ref().map(|n| n.len());
/// println!("still can print num_as_str: {:?}", num_as_str);
/// ```
#[inline]
pub fn as_ref(&self) -> Nullable<&T> {
match *self {
Nullable::Present(ref x) => Nullable::Present(x),
Nullable::Null => Nullable::Null,
}
}
/// Converts from `Nullable<T>` to `Nullable<&mut T>`.
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let mut x = Nullable::Present(2);
/// match x.as_mut() {
/// Nullable::Present(v) => *v = 42,
/// Nullable::Null => {},
/// }
/// assert_eq!(x, Nullable::Present(42));
/// ```
#[inline]
pub fn as_mut(&mut self) -> Nullable<&mut T> {
match *self {
Nullable::Present(ref mut x) => Nullable::Present(x),
Nullable::Null => Nullable::Null,
}
}
/////////////////////////////////////////////////////////////////////////
// Getting to contained values
/////////////////////////////////////////////////////////////////////////
/// Unwraps a Nullable, yielding the content of a `Nullable::Present`.
///
/// # Panics
///
/// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by
/// `msg`.
///
/// [`Nullable::Null`]: #variant.Null
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let x = Nullable::Present("value");
/// assert_eq!(x.expect("the world is ending"), "value");
/// ```
///
/// ```{.should_panic}
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let x: Nullable<&str> = Nullable::Null;
/// x.expect("the world is ending"); // panics with `the world is ending`
/// ```
#[inline]
pub fn expect(self, msg: &str) -> T {
match self {
Nullable::Present(val) => val,
Nullable::Null => expect_failed(msg),
}
}
/// Moves the value `v` out of the `Nullable<T>` if it is `Nullable::Present(v)`.
///
/// In general, because this function may panic, its use is discouraged.
/// Instead, prefer to use pattern matching and handle the `Nullable::Null`
/// case explicitly.
///
/// # Panics
///
/// Panics if the self value equals [`Nullable::Null`].
///
/// [`Nullable::Null`]: #variant.Null
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let x = Nullable::Present("air");
/// assert_eq!(x.unwrap(), "air");
/// ```
///
/// ```{.should_panic}
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.unwrap(), "air"); // fails
/// ```
#[inline]
pub fn unwrap(self) -> T {
match self {
Nullable::Present(val) => val,
Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"),
}
}
/// Returns the contained value or a default.
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car");
/// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike");
/// ```
#[inline]
pub fn unwrap_or(self, def: T) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => def,
}
}
/// Returns the contained value or computes it from a closure.
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let k = 10;
/// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4);
/// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20);
/// ```
#[inline]
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => f(),
}
}
/////////////////////////////////////////////////////////////////////////
// Transforming contained values
/////////////////////////////////////////////////////////////////////////
/// Maps a `Nullable<T>` to `Nullable<U>` by applying a function to a contained value.
///
/// # Examples
///
/// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original:
///
/// [`String`]: ../../std/string/struct.String.html
/// [`usize`]: ../../std/primitive.usize.html
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let maybe_some_string = Nullable::Present(String::from("Hello, World!"));
/// // `Nullable::map` takes self *by value*, consuming `maybe_some_string`
/// let maybe_some_len = maybe_some_string.map(|s| s.len());
///
/// assert_eq!(maybe_some_len, Nullable::Present(13));
/// ```
#[inline]
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Nullable<U> {
match self {
Nullable::Present(x) => Nullable::Present(f(x)),
Nullable::Null => Nullable::Null,
}
}
/// Applies a function to the contained value (if any),
/// or returns a `default` (if not).
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.map_or(42, |v| v.len()), 3);
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.map_or(42, |v| v.len()), 42);
/// ```
#[inline]
pub fn map_or<U, F: FnOnce(T) -> U>(self, default: U, f: F) -> U {
match self {
Nullable::Present(t) => f(t),
Nullable::Null => default,
}
}
/// Applies a function to the contained value (if any),
/// or computes a `default` (if not).
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let k = 21;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3);
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42);
/// ```
#[inline]
pub fn map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
match self {
Nullable::Present(t) => f(t),
Nullable::Null => default(),
}
}
/// Transforms the `Nullable<T>` into a [`Result<T, E>`], mapping `Nullable::Present(v)` to
/// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err].
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [Err]: ../../std/result/enum.Result.html#variant.Err
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.ok_or(0), Ok("foo"));
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.ok_or(0), Err(0));
/// ```
#[inline]
pub fn ok_or<E>(self, err: E) -> Result<T, E> {
match self {
Nullable::Present(v) => Ok(v),
Nullable::Null => Err(err),
}
}
/// Transforms the `Nullable<T>` into a [`Result<T, E>`], mapping `Nullable::Present(v)` to
/// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err].
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [Err]: ../../std/result/enum.Result.html#variant.Err
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.ok_or_else(|| 0), Ok("foo"));
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.ok_or_else(|| 0), Err(0));
/// ```
#[inline]
pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
match self {
Nullable::Present(v) => Ok(v),
Nullable::Null => Err(err()),
}
}
/////////////////////////////////////////////////////////////////////////
// Boolean operations on the values, eager and lazy
/////////////////////////////////////////////////////////////////////////
/// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`.
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let x = Nullable::Present(2);
/// let y: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.and(y), Nullable::Null);
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y = Nullable::Present("foo");
/// assert_eq!(x.and(y), Nullable::Null);
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Present("foo");
/// assert_eq!(x.and(y), Nullable::Present("foo"));
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.and(y), Nullable::Null);
/// ```
#[inline]
pub fn and<U>(self, optb: Nullable<U>) -> Nullable<U> {
match self {
Nullable::Present(_) => optb,
Nullable::Null => Nullable::Null,
}
}
/// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the
/// wrapped value and returns the result.
///
/// Some languages call this operation flatmap.
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// fn sq(x: u32) -> Nullable<u32> { Nullable::Present(x * x) }
/// fn nope(_: u32) -> Nullable<u32> { Nullable::Null }
///
/// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16));
/// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null);
/// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null);
/// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null);
/// ```
#[inline]
pub fn and_then<U, F: FnOnce(T) -> Nullable<U>>(self, f: F) -> Nullable<U> {
match self {
Nullable::Present(x) => f(x),
Nullable::Null => Nullable::Null,
}
}
/// Returns the Nullable if it contains a value, otherwise returns `optb`.
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Null;
/// assert_eq!(x.or(y), Nullable::Present(2));
///
/// let x = Nullable::Null;
/// let y = Nullable::Present(100);
/// assert_eq!(x.or(y), Nullable::Present(100));
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Present(100);
/// assert_eq!(x.or(y), Nullable::Present(2));
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y = Nullable::Null;
/// assert_eq!(x.or(y), Nullable::Null);
/// ```
#[inline]
pub fn or(self, optb: Nullable<T>) -> Nullable<T> {
match self {
Nullable::Present(_) => self,
Nullable::Null => optb,
}
}
/// Returns the Nullable if it contains a value, otherwise calls `f` and
/// returns the result.
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// fn nobody() -> Nullable<&'static str> { Nullable::Null }
/// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") }
///
/// assert_eq!(Nullable::Present("barbarians").or_else(vikings),
/// Nullable::Present("barbarians"));
/// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings"));
/// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null);
/// ```
#[inline]
pub fn or_else<F: FnOnce() -> Nullable<T>>(self, f: F) -> Nullable<T> {
match self {
Nullable::Present(_) => self,
Nullable::Null => f(),
}
}
/////////////////////////////////////////////////////////////////////////
// Misc
/////////////////////////////////////////////////////////////////////////
/// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place.
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let mut x = Nullable::Present(2);
/// x.take();
/// assert_eq!(x, Nullable::Null);
///
/// let mut x: Nullable<u32> = Nullable::Null;
/// x.take();
/// assert_eq!(x, Nullable::Null);
/// ```
#[inline]
pub fn take(&mut self) -> Nullable<T> {
mem::replace(self, Nullable::Null)
}
}
impl<'a, T: Clone> Nullable<&'a T> {
/// Maps an `Nullable<&T>` to an `Nullable<T>` by cloning the contents of the
/// Nullable.
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let x = 12;
/// let opt_x = Nullable::Present(&x);
/// assert_eq!(opt_x, Nullable::Present(&12));
/// let cloned = opt_x.cloned();
/// assert_eq!(cloned, Nullable::Present(12));
/// ```
pub fn cloned(self) -> Nullable<T> {
self.map(Clone::clone)
}
}
impl<T: Default> Nullable<T> {
/// Returns the contained value or a default
///
/// Consumes the `self` argument then, if `Nullable::Present`, returns the contained
/// value, otherwise if `Nullable::Null`, returns the default value for that
/// type.
///
/// # Examples
///
/// ```
/// # use {{{externCrateName}}}::types::Nullable;
///
/// let x = Nullable::Present(42);
/// assert_eq!(42, x.unwrap_or_default());
///
/// let y: Nullable<i32> = Nullable::Null;
/// assert_eq!(0, y.unwrap_or_default());
/// ```
#[inline]
pub fn unwrap_or_default(self) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => Default::default(),
}
}
}
impl<T> Default for Nullable<T> {
/// Returns None.
#[inline]
fn default() -> Nullable<T> {
Nullable::Null
}
}
impl<T> From<T> for Nullable<T> {
fn from(val: T) -> Nullable<T> {
Nullable::Present(val)
}
}
impl<T> Serialize for Nullable<T>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
Nullable::Present(ref inner) => serializer.serialize_some(&inner),
Nullable::Null => serializer.serialize_none(),
}
}
}
impl<'de, T> Deserialize<'de> for Nullable<T>
where
T: serde::de::DeserializeOwned,
{
fn deserialize<D>(deserializer: D) -> Result<Nullable<T>, D::Error>
where
D: Deserializer<'de>,
{
// In order to deserialize a required, but nullable, value, we first have to check whether
// the value is present at all. To do this, we deserialize to a serde_json::Value, which
// fails if the value is missing, or gives serde_json::Value::Null if the value is present.
// If that succeeds as null, we can easily return a Null.
// If that succeeds as some value, we deserialize that value and return a Present.
// If that errors, we return the error.
let presence: Result<::serde_json::Value, _> =
serde::Deserialize::deserialize(deserializer);
match presence {
Ok(serde_json::Value::Null) => Ok(Nullable::Null),
Ok(some_value) => serde_json::from_value(some_value)
.map(Nullable::Present)
.map_err(serde::de::Error::custom),
Err(x) => Err(x),
}
}
}
#[inline(never)]
#[cold]
fn expect_failed(msg: &str) -> ! {
panic!("{}", msg)
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
/// Base64-encoded byte array
pub struct ByteArray(pub Vec<u8>);
impl Serialize for ByteArray {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0))
}
}
impl<'de> Deserialize<'de> for ByteArray {
fn deserialize<D>(deserializer: D) -> Result<ByteArray, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match general_purpose::STANDARD.decode(s) {
Ok(bin) => Ok(ByteArray(bin)),
_ => Err(serde::de::Error::custom("invalid base64")),
}
}
}

View File

@ -0,0 +1,2 @@
target
Cargo.lock

View File

@ -0,0 +1,3 @@
[workspace]
members = ["output/*"]
resolver = "2"

View File

@ -0,0 +1,2 @@
target
Cargo.lock

View File

@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@ -0,0 +1,8 @@
.gitignore
Cargo.toml
README.md
src/header.rs
src/lib.rs
src/models.rs
src/server/mod.rs
src/types.rs

View File

@ -0,0 +1 @@
7.3.0-SNAPSHOT

View File

@ -0,0 +1,46 @@
[package]
name = "multipart-v3"
version = "1.0.7"
authors = ["OpenAPI Generator team and contributors"]
description = "API under test"
edition = "2021"
[features]
default = ["server"]
server = []
conversion = [
"frunk",
"frunk_derives",
"frunk_core",
"frunk-enum-core",
"frunk-enum-derive",
]
[dependencies]
async-trait = "0.1"
axum = { version = "0.7" }
axum-extra = { version = "0.9", features = ["cookie", "multipart"] }
base64 = "0.21"
bytes = "1"
chrono = { version = "0.4", features = ["serde"] }
frunk = { version = "0.4", optional = true }
frunk-enum-core = { version = "0.3", optional = true }
frunk-enum-derive = { version = "0.3", optional = true }
frunk_core = { version = "0.4", optional = true }
frunk_derives = { version = "0.4", optional = true }
http = "1"
lazy_static = "1"
regex = "1"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["raw_value"] }
serde_urlencoded = "0.7"
tokio = { version = "1", default-features = false, features = [
"signal",
"rt-multi-thread",
] }
tracing = { version = "0.1", features = ["attributes"] }
uuid = { version = "1", features = ["serde"] }
validator = { version = "0.16", features = ["derive"] }
[dev-dependencies]
tracing-subscriber = "0.3"

View File

@ -0,0 +1,91 @@
# Rust API for multipart-v3
API under test
## Overview
This server was generated by the [openapi-generator]
(https://openapi-generator.tech) project. By using the
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
server, you can easily generate a server stub.
To see how to make this your own, look here: [README]((https://openapi-generator.tech))
- API version: 1.0.7
This autogenerated project defines an API crate `multipart-v3` which contains:
* An `Api` trait defining the API in Rust.
* Data types representing the underlying data model.
* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation.
* Request validations (path, query, body params) are included.
## Using the generated library
The generated library has a few optional features that can be activated through Cargo.
* `server`
* This defaults to enabled and creates the basic skeleton of a server implementation based on Axum.
* To create the server stack you'll need to provide an implementation of the API trait to provide the server function.
* `conversions`
* This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types.
See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`.
### Example
```rust
struct ServerImpl {
// database: sea_orm::DbConn,
}
#[allow(unused_variables)]
#[async_trait]
impl multipart-v3::Api for ServerImpl {
// API implementation goes here
}
pub async fn start_server(addr: &str) {
// initialize tracing
tracing_subscriber::fmt::init();
// Init Axum router
let app = multipart-v3::server::new(Arc::new(ServerImpl));
// Add layers to the router
let app = app.layer(...);
// Run the server with graceful shutdown
let listener = TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await
.unwrap();
}
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
}
```

View File

@ -0,0 +1,197 @@
use std::{convert::TryFrom, fmt, ops::Deref};
use chrono::{DateTime, Utc};
use http::HeaderValue;
/// A struct to allow homogeneous conversion into a HeaderValue. We can't
/// implement the From/Into trait on HeaderValue because we don't own
/// either of the types.
#[derive(Debug, Clone)]
pub(crate) struct IntoHeaderValue<T>(pub T);
// Generic implementations
impl<T> Deref for IntoHeaderValue<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
// Derive for each TryFrom<T> in http::HeaderValue
macro_rules! ihv_generate {
($t:ident) => {
impl TryFrom<HeaderValue> for IntoHeaderValue<$t> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match hdr_value.parse::<$t>() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)),
Err(e) => Err(format!(
"Unable to parse {} as a string: {}",
stringify!($t),
e
)),
},
Err(e) => Err(format!(
"Unable to parse header {:?} as a string - {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<$t>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result<Self, Self::Error> {
Ok(hdr_value.0.into())
}
}
};
}
ihv_generate!(u64);
ihv_generate!(i64);
ihv_generate!(i16);
ihv_generate!(u16);
ihv_generate!(u32);
ihv_generate!(usize);
ihv_generate!(isize);
ihv_generate!(i32);
// Custom derivations
// Vec<String>
impl TryFrom<HeaderValue> for IntoHeaderValue<Vec<String>> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => Ok(IntoHeaderValue(
hdr_value
.split(',')
.filter_map(|x| match x.trim() {
"" => None,
y => Some(y.to_string()),
})
.collect(),
)),
Err(e) => Err(format!(
"Unable to parse header: {:?} as a string - {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<Vec<String>>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<Vec<String>>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0.join(", ")) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} into a header - {}",
hdr_value, e
)),
}
}
}
// String
impl TryFrom<HeaderValue> for IntoHeaderValue<String> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())),
Err(e) => Err(format!("Unable to convert header {:?} to {}", hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<String>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<String>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} from a header {}",
hdr_value, e
)),
}
}
}
// Bool
impl TryFrom<HeaderValue> for IntoHeaderValue<bool> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match hdr_value.parse() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)),
Err(e) => Err(format!("Unable to parse bool from {} - {}", hdr_value, e)),
},
Err(e) => Err(format!(
"Unable to convert {:?} from a header {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<bool>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<bool>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0.to_string()) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert: {:?} into a header: {}",
hdr_value, e
)),
}
}
}
// DateTime
impl TryFrom<HeaderValue> for IntoHeaderValue<DateTime<Utc>> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) {
Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))),
Err(e) => Err(format!("Unable to parse: {} as date - {}", hdr_value, e)),
},
Err(e) => Err(format!(
"Unable to convert header {:?} to string {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<DateTime<Utc>>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<DateTime<Utc>>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} to a header: {}",
hdr_value, e
)),
}
}
}

View File

@ -0,0 +1,82 @@
#![allow(
missing_docs,
trivial_casts,
unused_variables,
unused_mut,
unused_imports,
unused_extern_crates,
non_camel_case_types
)]
#![allow(unused_imports, unused_attributes)]
#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)]
use async_trait::async_trait;
use axum::extract::*;
use axum_extra::extract::{CookieJar, Multipart};
use bytes::Bytes;
use http::Method;
use serde::{Deserialize, Serialize};
use types::*;
pub const BASE_PATH: &str = "";
pub const API_VERSION: &str = "1.0.7";
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum MultipartRelatedRequestPostResponse {
/// OK
OK,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum MultipartRequestPostResponse {
/// OK
OK,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum MultipleIdenticalMimeTypesPostResponse {
/// OK
OK,
}
/// API
#[async_trait]
#[allow(clippy::ptr_arg)]
pub trait Api {
/// MultipartRelatedRequestPost - POST /multipart_related_request
async fn multipart_related_request_post(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: axum::body::Body,
) -> Result<MultipartRelatedRequestPostResponse, String>;
/// MultipartRequestPost - POST /multipart_request
async fn multipart_request_post(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Multipart,
) -> Result<MultipartRequestPostResponse, String>;
/// MultipleIdenticalMimeTypesPost - POST /multiple-identical-mime-types
async fn multiple_identical_mime_types_post(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: axum::body::Body,
) -> Result<MultipleIdenticalMimeTypesPostResponse, String>;
}
#[cfg(feature = "server")]
pub mod server;
pub mod models;
pub mod types;
#[cfg(feature = "server")]
pub(crate) mod header;

View File

@ -0,0 +1,446 @@
#![allow(unused_qualifications)]
use http::HeaderValue;
use validator::Validate;
#[cfg(feature = "server")]
use crate::header;
use crate::{models, types::*};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct MultipartRelatedRequest {
#[serde(rename = "object_field")]
#[serde(skip_serializing_if = "Option::is_none")]
pub object_field: Option<models::MultipartRequestObjectField>,
#[serde(rename = "optional_binary_field")]
#[serde(skip_serializing_if = "Option::is_none")]
pub optional_binary_field: Option<ByteArray>,
#[serde(rename = "required_binary_field")]
pub required_binary_field: ByteArray,
}
impl MultipartRelatedRequest {
#[allow(clippy::new_without_default, clippy::too_many_arguments)]
pub fn new(required_binary_field: ByteArray) -> MultipartRelatedRequest {
MultipartRelatedRequest {
object_field: None,
optional_binary_field: None,
required_binary_field,
}
}
}
/// Converts the MultipartRelatedRequest value to the Query Parameters representation (style=form, explode=false)
/// specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde serializer
impl std::string::ToString for MultipartRelatedRequest {
fn to_string(&self) -> String {
let params: Vec<Option<String>> = vec![
// Skipping object_field in query parameter serialization
// Skipping optional_binary_field in query parameter serialization
// Skipping optional_binary_field in query parameter serialization
// Skipping required_binary_field in query parameter serialization
// Skipping required_binary_field in query parameter serialization
];
params.into_iter().flatten().collect::<Vec<_>>().join(",")
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a MultipartRelatedRequest value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl std::str::FromStr for MultipartRelatedRequest {
type Err = String;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
/// An intermediate representation of the struct to use for parsing.
#[derive(Default)]
#[allow(dead_code)]
struct IntermediateRep {
pub object_field: Vec<models::MultipartRequestObjectField>,
pub optional_binary_field: Vec<ByteArray>,
pub required_binary_field: Vec<ByteArray>,
}
let mut intermediate_rep = IntermediateRep::default();
// Parse into intermediate representation
let mut string_iter = s.split(',');
let mut key_result = string_iter.next();
while key_result.is_some() {
let val = match string_iter.next() {
Some(x) => x,
None => {
return std::result::Result::Err(
"Missing value while parsing MultipartRelatedRequest".to_string(),
)
}
};
if let Some(key) = key_result {
#[allow(clippy::match_single_binding)]
match key {
#[allow(clippy::redundant_clone)]
"object_field" => intermediate_rep.object_field.push(<models::MultipartRequestObjectField as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?),
"optional_binary_field" => return std::result::Result::Err("Parsing binary data in this style is not supported in MultipartRelatedRequest".to_string()),
"required_binary_field" => return std::result::Result::Err("Parsing binary data in this style is not supported in MultipartRelatedRequest".to_string()),
_ => return std::result::Result::Err("Unexpected key while parsing MultipartRelatedRequest".to_string())
}
}
// Get the next key
key_result = string_iter.next();
}
// Use the intermediate representation to return the struct
std::result::Result::Ok(MultipartRelatedRequest {
object_field: intermediate_rep.object_field.into_iter().next(),
optional_binary_field: intermediate_rep.optional_binary_field.into_iter().next(),
required_binary_field: intermediate_rep
.required_binary_field
.into_iter()
.next()
.ok_or_else(|| {
"required_binary_field missing in MultipartRelatedRequest".to_string()
})?,
})
}
}
// Methods for converting between header::IntoHeaderValue<MultipartRelatedRequest> and HeaderValue
#[cfg(feature = "server")]
impl std::convert::TryFrom<header::IntoHeaderValue<MultipartRelatedRequest>> for HeaderValue {
type Error = String;
fn try_from(
hdr_value: header::IntoHeaderValue<MultipartRelatedRequest>,
) -> std::result::Result<Self, Self::Error> {
let hdr_value = hdr_value.to_string();
match HeaderValue::from_str(&hdr_value) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(e) => std::result::Result::Err(format!(
"Invalid header value for MultipartRelatedRequest - value: {} is invalid {}",
hdr_value, e
)),
}
}
}
#[cfg(feature = "server")]
impl std::convert::TryFrom<HeaderValue> for header::IntoHeaderValue<MultipartRelatedRequest> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> std::result::Result<Self, Self::Error> {
match hdr_value.to_str() {
std::result::Result::Ok(value) => {
match <MultipartRelatedRequest as std::str::FromStr>::from_str(value) {
std::result::Result::Ok(value) => {
std::result::Result::Ok(header::IntoHeaderValue(value))
}
std::result::Result::Err(err) => std::result::Result::Err(format!(
"Unable to convert header value '{}' into MultipartRelatedRequest - {}",
value, err
)),
}
}
std::result::Result::Err(e) => std::result::Result::Err(format!(
"Unable to convert header: {:?} to string: {}",
hdr_value, e
)),
}
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct MultipartRequestObjectField {
#[serde(rename = "field_a")]
pub field_a: String,
#[serde(rename = "field_b")]
#[serde(skip_serializing_if = "Option::is_none")]
pub field_b: Option<Vec<String>>,
}
impl MultipartRequestObjectField {
#[allow(clippy::new_without_default, clippy::too_many_arguments)]
pub fn new(field_a: String) -> MultipartRequestObjectField {
MultipartRequestObjectField {
field_a,
field_b: None,
}
}
}
/// Converts the MultipartRequestObjectField value to the Query Parameters representation (style=form, explode=false)
/// specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde serializer
impl std::string::ToString for MultipartRequestObjectField {
fn to_string(&self) -> String {
let params: Vec<Option<String>> = vec![
Some("field_a".to_string()),
Some(self.field_a.to_string()),
self.field_b.as_ref().map(|field_b| {
[
"field_b".to_string(),
field_b
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(","),
]
.join(",")
}),
];
params.into_iter().flatten().collect::<Vec<_>>().join(",")
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a MultipartRequestObjectField value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl std::str::FromStr for MultipartRequestObjectField {
type Err = String;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
/// An intermediate representation of the struct to use for parsing.
#[derive(Default)]
#[allow(dead_code)]
struct IntermediateRep {
pub field_a: Vec<String>,
pub field_b: Vec<Vec<String>>,
}
let mut intermediate_rep = IntermediateRep::default();
// Parse into intermediate representation
let mut string_iter = s.split(',');
let mut key_result = string_iter.next();
while key_result.is_some() {
let val = match string_iter.next() {
Some(x) => x,
None => {
return std::result::Result::Err(
"Missing value while parsing MultipartRequestObjectField".to_string(),
)
}
};
if let Some(key) = key_result {
#[allow(clippy::match_single_binding)]
match key {
#[allow(clippy::redundant_clone)]
"field_a" => intermediate_rep.field_a.push(<String as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?),
"field_b" => return std::result::Result::Err("Parsing a container in this style is not supported in MultipartRequestObjectField".to_string()),
_ => return std::result::Result::Err("Unexpected key while parsing MultipartRequestObjectField".to_string())
}
}
// Get the next key
key_result = string_iter.next();
}
// Use the intermediate representation to return the struct
std::result::Result::Ok(MultipartRequestObjectField {
field_a: intermediate_rep
.field_a
.into_iter()
.next()
.ok_or_else(|| "field_a missing in MultipartRequestObjectField".to_string())?,
field_b: intermediate_rep.field_b.into_iter().next(),
})
}
}
// Methods for converting between header::IntoHeaderValue<MultipartRequestObjectField> and HeaderValue
#[cfg(feature = "server")]
impl std::convert::TryFrom<header::IntoHeaderValue<MultipartRequestObjectField>> for HeaderValue {
type Error = String;
fn try_from(
hdr_value: header::IntoHeaderValue<MultipartRequestObjectField>,
) -> std::result::Result<Self, Self::Error> {
let hdr_value = hdr_value.to_string();
match HeaderValue::from_str(&hdr_value) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(e) => std::result::Result::Err(format!(
"Invalid header value for MultipartRequestObjectField - value: {} is invalid {}",
hdr_value, e
)),
}
}
}
#[cfg(feature = "server")]
impl std::convert::TryFrom<HeaderValue> for header::IntoHeaderValue<MultipartRequestObjectField> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> std::result::Result<Self, Self::Error> {
match hdr_value.to_str() {
std::result::Result::Ok(value) => {
match <MultipartRequestObjectField as std::str::FromStr>::from_str(value) {
std::result::Result::Ok(value) => {
std::result::Result::Ok(header::IntoHeaderValue(value))
}
std::result::Result::Err(err) => std::result::Result::Err(format!(
"Unable to convert header value '{}' into MultipartRequestObjectField - {}",
value, err
)),
}
}
std::result::Result::Err(e) => std::result::Result::Err(format!(
"Unable to convert header: {:?} to string: {}",
hdr_value, e
)),
}
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct MultipleIdenticalMimeTypesPostRequest {
#[serde(rename = "binary1")]
#[serde(skip_serializing_if = "Option::is_none")]
pub binary1: Option<ByteArray>,
#[serde(rename = "binary2")]
#[serde(skip_serializing_if = "Option::is_none")]
pub binary2: Option<ByteArray>,
}
impl MultipleIdenticalMimeTypesPostRequest {
#[allow(clippy::new_without_default, clippy::too_many_arguments)]
pub fn new() -> MultipleIdenticalMimeTypesPostRequest {
MultipleIdenticalMimeTypesPostRequest {
binary1: None,
binary2: None,
}
}
}
/// Converts the MultipleIdenticalMimeTypesPostRequest value to the Query Parameters representation (style=form, explode=false)
/// specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde serializer
impl std::string::ToString for MultipleIdenticalMimeTypesPostRequest {
fn to_string(&self) -> String {
let params: Vec<Option<String>> = vec![
// Skipping binary1 in query parameter serialization
// Skipping binary1 in query parameter serialization
// Skipping binary2 in query parameter serialization
// Skipping binary2 in query parameter serialization
];
params.into_iter().flatten().collect::<Vec<_>>().join(",")
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a MultipleIdenticalMimeTypesPostRequest value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl std::str::FromStr for MultipleIdenticalMimeTypesPostRequest {
type Err = String;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
/// An intermediate representation of the struct to use for parsing.
#[derive(Default)]
#[allow(dead_code)]
struct IntermediateRep {
pub binary1: Vec<ByteArray>,
pub binary2: Vec<ByteArray>,
}
let mut intermediate_rep = IntermediateRep::default();
// Parse into intermediate representation
let mut string_iter = s.split(',');
let mut key_result = string_iter.next();
while key_result.is_some() {
let val = match string_iter.next() {
Some(x) => x,
None => {
return std::result::Result::Err(
"Missing value while parsing MultipleIdenticalMimeTypesPostRequest"
.to_string(),
)
}
};
if let Some(key) = key_result {
#[allow(clippy::match_single_binding)]
match key {
"binary1" => return std::result::Result::Err("Parsing binary data in this style is not supported in MultipleIdenticalMimeTypesPostRequest".to_string()),
"binary2" => return std::result::Result::Err("Parsing binary data in this style is not supported in MultipleIdenticalMimeTypesPostRequest".to_string()),
_ => return std::result::Result::Err("Unexpected key while parsing MultipleIdenticalMimeTypesPostRequest".to_string())
}
}
// Get the next key
key_result = string_iter.next();
}
// Use the intermediate representation to return the struct
std::result::Result::Ok(MultipleIdenticalMimeTypesPostRequest {
binary1: intermediate_rep.binary1.into_iter().next(),
binary2: intermediate_rep.binary2.into_iter().next(),
})
}
}
// Methods for converting between header::IntoHeaderValue<MultipleIdenticalMimeTypesPostRequest> and HeaderValue
#[cfg(feature = "server")]
impl std::convert::TryFrom<header::IntoHeaderValue<MultipleIdenticalMimeTypesPostRequest>>
for HeaderValue
{
type Error = String;
fn try_from(
hdr_value: header::IntoHeaderValue<MultipleIdenticalMimeTypesPostRequest>,
) -> std::result::Result<Self, Self::Error> {
let hdr_value = hdr_value.to_string();
match HeaderValue::from_str(&hdr_value) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(e) => std::result::Result::Err(
format!("Invalid header value for MultipleIdenticalMimeTypesPostRequest - value: {} is invalid {}",
hdr_value, e))
}
}
}
#[cfg(feature = "server")]
impl std::convert::TryFrom<HeaderValue>
for header::IntoHeaderValue<MultipleIdenticalMimeTypesPostRequest>
{
type Error = String;
fn try_from(hdr_value: HeaderValue) -> std::result::Result<Self, Self::Error> {
match hdr_value.to_str() {
std::result::Result::Ok(value) => {
match <MultipleIdenticalMimeTypesPostRequest as std::str::FromStr>::from_str(value) {
std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)),
std::result::Result::Err(err) => std::result::Result::Err(
format!("Unable to convert header value '{}' into MultipleIdenticalMimeTypesPostRequest - {}",
value, err))
}
},
std::result::Result::Err(e) => std::result::Result::Err(
format!("Unable to convert header: {:?} to string: {}",
hdr_value, e))
}
}
}

View File

@ -0,0 +1,211 @@
use std::collections::HashMap;
use axum::{body::Body, extract::*, response::Response, routing::*};
use axum_extra::extract::{CookieJar, Multipart};
use bytes::Bytes;
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
use tracing::error;
use validator::{Validate, ValidationErrors};
use crate::{header, types::*};
#[allow(unused_imports)]
use crate::models;
use crate::{
Api, MultipartRelatedRequestPostResponse, MultipartRequestPostResponse,
MultipleIdenticalMimeTypesPostResponse,
};
/// Setup API Server.
pub fn new<I, A>(api_impl: I) -> Router
where
I: AsRef<A> + Clone + Send + Sync + 'static,
A: Api + 'static,
{
// build our application with a route
Router::new()
.route(
"/multipart_related_request",
post(multipart_related_request_post::<I, A>),
)
.route("/multipart_request", post(multipart_request_post::<I, A>))
.route(
"/multiple-identical-mime-types",
post(multiple_identical_mime_types_post::<I, A>),
)
.with_state(api_impl)
}
#[tracing::instrument(skip_all)]
fn multipart_related_request_post_validation() -> std::result::Result<(), ValidationErrors> {
Ok(())
}
/// MultipartRelatedRequestPost - POST /multipart_related_request
#[tracing::instrument(skip_all)]
async fn multipart_related_request_post<I, A>(
method: Method,
host: Host,
cookies: CookieJar,
State(api_impl): State<I>,
body: axum::body::Body,
) -> Result<Response, StatusCode>
where
I: AsRef<A> + Send + Sync,
A: Api,
{
#[allow(clippy::redundant_closure)]
let validation =
tokio::task::spawn_blocking(move || multipart_related_request_post_validation())
.await
.unwrap();
let Ok(()) = validation else {
return Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(validation.unwrap_err().to_string()))
.map_err(|_| StatusCode::BAD_REQUEST);
};
let result = api_impl
.as_ref()
.multipart_related_request_post(method, host, cookies, body)
.await;
let mut response = Response::builder();
let resp = match result {
Ok(rsp) => match rsp {
MultipartRelatedRequestPostResponse::OK => {
let mut response = response.status(201);
response.body(Body::empty())
}
},
Err(_) => {
// Application code returned an error. This should not happen, as the implementation should
// return a valid response.
response.status(500).body(Body::empty())
}
};
resp.map_err(|e| {
error!(error = ?e);
StatusCode::INTERNAL_SERVER_ERROR
})
}
#[tracing::instrument(skip_all)]
fn multipart_request_post_validation() -> std::result::Result<(), ValidationErrors> {
Ok(())
}
/// MultipartRequestPost - POST /multipart_request
#[tracing::instrument(skip_all)]
async fn multipart_request_post<I, A>(
method: Method,
host: Host,
cookies: CookieJar,
State(api_impl): State<I>,
body: Multipart,
) -> Result<Response, StatusCode>
where
I: AsRef<A> + Send + Sync,
A: Api,
{
#[allow(clippy::redundant_closure)]
let validation = tokio::task::spawn_blocking(move || multipart_request_post_validation())
.await
.unwrap();
let Ok(()) = validation else {
return Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(validation.unwrap_err().to_string()))
.map_err(|_| StatusCode::BAD_REQUEST);
};
let result = api_impl
.as_ref()
.multipart_request_post(method, host, cookies, body)
.await;
let mut response = Response::builder();
let resp = match result {
Ok(rsp) => match rsp {
MultipartRequestPostResponse::OK => {
let mut response = response.status(201);
response.body(Body::empty())
}
},
Err(_) => {
// Application code returned an error. This should not happen, as the implementation should
// return a valid response.
response.status(500).body(Body::empty())
}
};
resp.map_err(|e| {
error!(error = ?e);
StatusCode::INTERNAL_SERVER_ERROR
})
}
#[tracing::instrument(skip_all)]
fn multiple_identical_mime_types_post_validation() -> std::result::Result<(), ValidationErrors> {
Ok(())
}
/// MultipleIdenticalMimeTypesPost - POST /multiple-identical-mime-types
#[tracing::instrument(skip_all)]
async fn multiple_identical_mime_types_post<I, A>(
method: Method,
host: Host,
cookies: CookieJar,
State(api_impl): State<I>,
body: axum::body::Body,
) -> Result<Response, StatusCode>
where
I: AsRef<A> + Send + Sync,
A: Api,
{
#[allow(clippy::redundant_closure)]
let validation =
tokio::task::spawn_blocking(move || multiple_identical_mime_types_post_validation())
.await
.unwrap();
let Ok(()) = validation else {
return Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(validation.unwrap_err().to_string()))
.map_err(|_| StatusCode::BAD_REQUEST);
};
let result = api_impl
.as_ref()
.multiple_identical_mime_types_post(method, host, cookies, body)
.await;
let mut response = Response::builder();
let resp = match result {
Ok(rsp) => match rsp {
MultipleIdenticalMimeTypesPostResponse::OK => {
let mut response = response.status(200);
response.body(Body::empty())
}
},
Err(_) => {
// Application code returned an error. This should not happen, as the implementation should
// return a valid response.
response.status(500).body(Body::empty())
}
};
resp.map_err(|e| {
error!(error = ?e);
StatusCode::INTERNAL_SERVER_ERROR
})
}

View File

@ -0,0 +1,665 @@
use std::{mem, str::FromStr};
use base64::{engine::general_purpose, Engine};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[allow(dead_code)]
pub struct Object(serde_json::Value);
impl validator::Validate for Object {
fn validate(&self) -> Result<(), validator::ValidationErrors> {
Ok(())
}
}
impl FromStr for Object {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(serde_json::Value::String(s.to_owned())))
}
}
/// Serde helper function to create a default `Option<Nullable<T>>` while
/// deserializing
pub fn default_optional_nullable<T>() -> Option<Nullable<T>> {
None
}
/// Serde helper function to deserialize into an `Option<Nullable<T>>`
pub fn deserialize_optional_nullable<'de, D, T>(
deserializer: D,
) -> Result<Option<Nullable<T>>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
Option::<T>::deserialize(deserializer).map(|val| match val {
Some(inner) => Some(Nullable::Present(inner)),
None => Some(Nullable::Null),
})
}
/// The Nullable type. Represents a value which may be specified as null on an API.
/// Note that this is distinct from a value that is optional and not present!
///
/// Nullable implements many of the same methods as the Option type (map, unwrap, etc).
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum Nullable<T> {
/// Null value
Null,
/// Value is present
Present(T),
}
impl<T> Nullable<T> {
/////////////////////////////////////////////////////////////////////////
// Querying the contained values
/////////////////////////////////////////////////////////////////////////
/// Returns `true` if the Nullable is a `Present` value.
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let x: Nullable<u32> = Nullable::Present(2);
/// assert_eq!(x.is_present(), true);
///
/// let x: Nullable<u32> = Nullable::Null;
/// assert_eq!(x.is_present(), false);
/// ```
#[inline]
pub fn is_present(&self) -> bool {
match *self {
Nullable::Present(_) => true,
Nullable::Null => false,
}
}
/// Returns `true` if the Nullable is a `Null` value.
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let x: Nullable<u32> = Nullable::Present(2);
/// assert_eq!(x.is_null(), false);
///
/// let x: Nullable<u32> = Nullable::Null;
/// assert_eq!(x.is_null(), true);
/// ```
#[inline]
pub fn is_null(&self) -> bool {
!self.is_present()
}
/////////////////////////////////////////////////////////////////////////
// Adapter for working with references
/////////////////////////////////////////////////////////////////////////
/// Converts from `Nullable<T>` to `Nullable<&T>`.
///
/// # Examples
///
/// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original.
/// The [`map`] method takes the `self` argument by value, consuming the original,
/// so this technique uses `as_ref` to first take a `Nullable` to a reference
/// to the value inside the original.
///
/// [`map`]: enum.Nullable.html#method.map
/// [`String`]: ../../std/string/struct.String.html
/// [`usize`]: ../../std/primitive.usize.html
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let num_as_str: Nullable<String> = Nullable::Present("10".to_string());
/// // First, cast `Nullable<String>` to `Nullable<&String>` with `as_ref`,
/// // then consume *that* with `map`, leaving `num_as_str` on the stack.
/// let num_as_int: Nullable<usize> = num_as_str.as_ref().map(|n| n.len());
/// println!("still can print num_as_str: {:?}", num_as_str);
/// ```
#[inline]
pub fn as_ref(&self) -> Nullable<&T> {
match *self {
Nullable::Present(ref x) => Nullable::Present(x),
Nullable::Null => Nullable::Null,
}
}
/// Converts from `Nullable<T>` to `Nullable<&mut T>`.
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let mut x = Nullable::Present(2);
/// match x.as_mut() {
/// Nullable::Present(v) => *v = 42,
/// Nullable::Null => {},
/// }
/// assert_eq!(x, Nullable::Present(42));
/// ```
#[inline]
pub fn as_mut(&mut self) -> Nullable<&mut T> {
match *self {
Nullable::Present(ref mut x) => Nullable::Present(x),
Nullable::Null => Nullable::Null,
}
}
/////////////////////////////////////////////////////////////////////////
// Getting to contained values
/////////////////////////////////////////////////////////////////////////
/// Unwraps a Nullable, yielding the content of a `Nullable::Present`.
///
/// # Panics
///
/// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by
/// `msg`.
///
/// [`Nullable::Null`]: #variant.Null
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let x = Nullable::Present("value");
/// assert_eq!(x.expect("the world is ending"), "value");
/// ```
///
/// ```{.should_panic}
/// # use multipart_v3::types::Nullable;
///
/// let x: Nullable<&str> = Nullable::Null;
/// x.expect("the world is ending"); // panics with `the world is ending`
/// ```
#[inline]
pub fn expect(self, msg: &str) -> T {
match self {
Nullable::Present(val) => val,
Nullable::Null => expect_failed(msg),
}
}
/// Moves the value `v` out of the `Nullable<T>` if it is `Nullable::Present(v)`.
///
/// In general, because this function may panic, its use is discouraged.
/// Instead, prefer to use pattern matching and handle the `Nullable::Null`
/// case explicitly.
///
/// # Panics
///
/// Panics if the self value equals [`Nullable::Null`].
///
/// [`Nullable::Null`]: #variant.Null
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let x = Nullable::Present("air");
/// assert_eq!(x.unwrap(), "air");
/// ```
///
/// ```{.should_panic}
/// # use multipart_v3::types::Nullable;
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.unwrap(), "air"); // fails
/// ```
#[inline]
pub fn unwrap(self) -> T {
match self {
Nullable::Present(val) => val,
Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"),
}
}
/// Returns the contained value or a default.
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car");
/// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike");
/// ```
#[inline]
pub fn unwrap_or(self, def: T) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => def,
}
}
/// Returns the contained value or computes it from a closure.
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let k = 10;
/// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4);
/// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20);
/// ```
#[inline]
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => f(),
}
}
/////////////////////////////////////////////////////////////////////////
// Transforming contained values
/////////////////////////////////////////////////////////////////////////
/// Maps a `Nullable<T>` to `Nullable<U>` by applying a function to a contained value.
///
/// # Examples
///
/// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original:
///
/// [`String`]: ../../std/string/struct.String.html
/// [`usize`]: ../../std/primitive.usize.html
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let maybe_some_string = Nullable::Present(String::from("Hello, World!"));
/// // `Nullable::map` takes self *by value*, consuming `maybe_some_string`
/// let maybe_some_len = maybe_some_string.map(|s| s.len());
///
/// assert_eq!(maybe_some_len, Nullable::Present(13));
/// ```
#[inline]
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Nullable<U> {
match self {
Nullable::Present(x) => Nullable::Present(f(x)),
Nullable::Null => Nullable::Null,
}
}
/// Applies a function to the contained value (if any),
/// or returns a `default` (if not).
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.map_or(42, |v| v.len()), 3);
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.map_or(42, |v| v.len()), 42);
/// ```
#[inline]
pub fn map_or<U, F: FnOnce(T) -> U>(self, default: U, f: F) -> U {
match self {
Nullable::Present(t) => f(t),
Nullable::Null => default,
}
}
/// Applies a function to the contained value (if any),
/// or computes a `default` (if not).
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let k = 21;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3);
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42);
/// ```
#[inline]
pub fn map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
match self {
Nullable::Present(t) => f(t),
Nullable::Null => default(),
}
}
/// Transforms the `Nullable<T>` into a [`Result<T, E>`], mapping `Nullable::Present(v)` to
/// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err].
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [Err]: ../../std/result/enum.Result.html#variant.Err
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.ok_or(0), Ok("foo"));
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.ok_or(0), Err(0));
/// ```
#[inline]
pub fn ok_or<E>(self, err: E) -> Result<T, E> {
match self {
Nullable::Present(v) => Ok(v),
Nullable::Null => Err(err),
}
}
/// Transforms the `Nullable<T>` into a [`Result<T, E>`], mapping `Nullable::Present(v)` to
/// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err].
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [Err]: ../../std/result/enum.Result.html#variant.Err
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.ok_or_else(|| 0), Ok("foo"));
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.ok_or_else(|| 0), Err(0));
/// ```
#[inline]
pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
match self {
Nullable::Present(v) => Ok(v),
Nullable::Null => Err(err()),
}
}
/////////////////////////////////////////////////////////////////////////
// Boolean operations on the values, eager and lazy
/////////////////////////////////////////////////////////////////////////
/// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`.
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let x = Nullable::Present(2);
/// let y: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.and(y), Nullable::Null);
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y = Nullable::Present("foo");
/// assert_eq!(x.and(y), Nullable::Null);
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Present("foo");
/// assert_eq!(x.and(y), Nullable::Present("foo"));
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.and(y), Nullable::Null);
/// ```
#[inline]
pub fn and<U>(self, optb: Nullable<U>) -> Nullable<U> {
match self {
Nullable::Present(_) => optb,
Nullable::Null => Nullable::Null,
}
}
/// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the
/// wrapped value and returns the result.
///
/// Some languages call this operation flatmap.
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// fn sq(x: u32) -> Nullable<u32> { Nullable::Present(x * x) }
/// fn nope(_: u32) -> Nullable<u32> { Nullable::Null }
///
/// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16));
/// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null);
/// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null);
/// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null);
/// ```
#[inline]
pub fn and_then<U, F: FnOnce(T) -> Nullable<U>>(self, f: F) -> Nullable<U> {
match self {
Nullable::Present(x) => f(x),
Nullable::Null => Nullable::Null,
}
}
/// Returns the Nullable if it contains a value, otherwise returns `optb`.
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Null;
/// assert_eq!(x.or(y), Nullable::Present(2));
///
/// let x = Nullable::Null;
/// let y = Nullable::Present(100);
/// assert_eq!(x.or(y), Nullable::Present(100));
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Present(100);
/// assert_eq!(x.or(y), Nullable::Present(2));
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y = Nullable::Null;
/// assert_eq!(x.or(y), Nullable::Null);
/// ```
#[inline]
pub fn or(self, optb: Nullable<T>) -> Nullable<T> {
match self {
Nullable::Present(_) => self,
Nullable::Null => optb,
}
}
/// Returns the Nullable if it contains a value, otherwise calls `f` and
/// returns the result.
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// fn nobody() -> Nullable<&'static str> { Nullable::Null }
/// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") }
///
/// assert_eq!(Nullable::Present("barbarians").or_else(vikings),
/// Nullable::Present("barbarians"));
/// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings"));
/// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null);
/// ```
#[inline]
pub fn or_else<F: FnOnce() -> Nullable<T>>(self, f: F) -> Nullable<T> {
match self {
Nullable::Present(_) => self,
Nullable::Null => f(),
}
}
/////////////////////////////////////////////////////////////////////////
// Misc
/////////////////////////////////////////////////////////////////////////
/// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place.
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let mut x = Nullable::Present(2);
/// x.take();
/// assert_eq!(x, Nullable::Null);
///
/// let mut x: Nullable<u32> = Nullable::Null;
/// x.take();
/// assert_eq!(x, Nullable::Null);
/// ```
#[inline]
pub fn take(&mut self) -> Nullable<T> {
mem::replace(self, Nullable::Null)
}
}
impl<'a, T: Clone> Nullable<&'a T> {
/// Maps an `Nullable<&T>` to an `Nullable<T>` by cloning the contents of the
/// Nullable.
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let x = 12;
/// let opt_x = Nullable::Present(&x);
/// assert_eq!(opt_x, Nullable::Present(&12));
/// let cloned = opt_x.cloned();
/// assert_eq!(cloned, Nullable::Present(12));
/// ```
pub fn cloned(self) -> Nullable<T> {
self.map(Clone::clone)
}
}
impl<T: Default> Nullable<T> {
/// Returns the contained value or a default
///
/// Consumes the `self` argument then, if `Nullable::Present`, returns the contained
/// value, otherwise if `Nullable::Null`, returns the default value for that
/// type.
///
/// # Examples
///
/// ```
/// # use multipart_v3::types::Nullable;
///
/// let x = Nullable::Present(42);
/// assert_eq!(42, x.unwrap_or_default());
///
/// let y: Nullable<i32> = Nullable::Null;
/// assert_eq!(0, y.unwrap_or_default());
/// ```
#[inline]
pub fn unwrap_or_default(self) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => Default::default(),
}
}
}
impl<T> Default for Nullable<T> {
/// Returns None.
#[inline]
fn default() -> Nullable<T> {
Nullable::Null
}
}
impl<T> From<T> for Nullable<T> {
fn from(val: T) -> Nullable<T> {
Nullable::Present(val)
}
}
impl<T> Serialize for Nullable<T>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
Nullable::Present(ref inner) => serializer.serialize_some(&inner),
Nullable::Null => serializer.serialize_none(),
}
}
}
impl<'de, T> Deserialize<'de> for Nullable<T>
where
T: serde::de::DeserializeOwned,
{
fn deserialize<D>(deserializer: D) -> Result<Nullable<T>, D::Error>
where
D: Deserializer<'de>,
{
// In order to deserialize a required, but nullable, value, we first have to check whether
// the value is present at all. To do this, we deserialize to a serde_json::Value, which
// fails if the value is missing, or gives serde_json::Value::Null if the value is present.
// If that succeeds as null, we can easily return a Null.
// If that succeeds as some value, we deserialize that value and return a Present.
// If that errors, we return the error.
let presence: Result<::serde_json::Value, _> =
serde::Deserialize::deserialize(deserializer);
match presence {
Ok(serde_json::Value::Null) => Ok(Nullable::Null),
Ok(some_value) => serde_json::from_value(some_value)
.map(Nullable::Present)
.map_err(serde::de::Error::custom),
Err(x) => Err(x),
}
}
}
#[inline(never)]
#[cold]
fn expect_failed(msg: &str) -> ! {
panic!("{}", msg)
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
/// Base64-encoded byte array
pub struct ByteArray(pub Vec<u8>);
impl Serialize for ByteArray {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0))
}
}
impl<'de> Deserialize<'de> for ByteArray {
fn deserialize<D>(deserializer: D) -> Result<ByteArray, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match general_purpose::STANDARD.decode(s) {
Ok(bin) => Ok(ByteArray(bin)),
_ => Err(serde::de::Error::custom("invalid base64")),
}
}
}

View File

@ -0,0 +1,2 @@
target
Cargo.lock

View File

@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@ -0,0 +1,8 @@
.gitignore
Cargo.toml
README.md
src/header.rs
src/lib.rs
src/models.rs
src/server/mod.rs
src/types.rs

View File

@ -0,0 +1 @@
7.3.0-SNAPSHOT

View File

@ -0,0 +1,46 @@
[package]
name = "openapi-v3"
version = "1.0.7"
authors = ["OpenAPI Generator team and contributors"]
description = "API under test"
edition = "2021"
[features]
default = ["server"]
server = []
conversion = [
"frunk",
"frunk_derives",
"frunk_core",
"frunk-enum-core",
"frunk-enum-derive",
]
[dependencies]
async-trait = "0.1"
axum = { version = "0.7" }
axum-extra = { version = "0.9", features = ["cookie", "multipart"] }
base64 = "0.21"
bytes = "1"
chrono = { version = "0.4", features = ["serde"] }
frunk = { version = "0.4", optional = true }
frunk-enum-core = { version = "0.3", optional = true }
frunk-enum-derive = { version = "0.3", optional = true }
frunk_core = { version = "0.4", optional = true }
frunk_derives = { version = "0.4", optional = true }
http = "1"
lazy_static = "1"
regex = "1"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["raw_value"] }
serde_urlencoded = "0.7"
tokio = { version = "1", default-features = false, features = [
"signal",
"rt-multi-thread",
] }
tracing = { version = "0.1", features = ["attributes"] }
uuid = { version = "1", features = ["serde"] }
validator = { version = "0.16", features = ["derive"] }
[dev-dependencies]
tracing-subscriber = "0.3"

View File

@ -0,0 +1,91 @@
# Rust API for openapi-v3
API under test
## Overview
This server was generated by the [openapi-generator]
(https://openapi-generator.tech) project. By using the
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
server, you can easily generate a server stub.
To see how to make this your own, look here: [README]((https://openapi-generator.tech))
- API version: 1.0.7
This autogenerated project defines an API crate `openapi-v3` which contains:
* An `Api` trait defining the API in Rust.
* Data types representing the underlying data model.
* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation.
* Request validations (path, query, body params) are included.
## Using the generated library
The generated library has a few optional features that can be activated through Cargo.
* `server`
* This defaults to enabled and creates the basic skeleton of a server implementation based on Axum.
* To create the server stack you'll need to provide an implementation of the API trait to provide the server function.
* `conversions`
* This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types.
See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`.
### Example
```rust
struct ServerImpl {
// database: sea_orm::DbConn,
}
#[allow(unused_variables)]
#[async_trait]
impl openapi-v3::Api for ServerImpl {
// API implementation goes here
}
pub async fn start_server(addr: &str) {
// initialize tracing
tracing_subscriber::fmt::init();
// Init Axum router
let app = openapi-v3::server::new(Arc::new(ServerImpl));
// Add layers to the router
let app = app.layer(...);
// Run the server with graceful shutdown
let listener = TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await
.unwrap();
}
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
}
```

View File

@ -0,0 +1,197 @@
use std::{convert::TryFrom, fmt, ops::Deref};
use chrono::{DateTime, Utc};
use http::HeaderValue;
/// A struct to allow homogeneous conversion into a HeaderValue. We can't
/// implement the From/Into trait on HeaderValue because we don't own
/// either of the types.
#[derive(Debug, Clone)]
pub(crate) struct IntoHeaderValue<T>(pub T);
// Generic implementations
impl<T> Deref for IntoHeaderValue<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
// Derive for each TryFrom<T> in http::HeaderValue
macro_rules! ihv_generate {
($t:ident) => {
impl TryFrom<HeaderValue> for IntoHeaderValue<$t> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match hdr_value.parse::<$t>() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)),
Err(e) => Err(format!(
"Unable to parse {} as a string: {}",
stringify!($t),
e
)),
},
Err(e) => Err(format!(
"Unable to parse header {:?} as a string - {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<$t>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result<Self, Self::Error> {
Ok(hdr_value.0.into())
}
}
};
}
ihv_generate!(u64);
ihv_generate!(i64);
ihv_generate!(i16);
ihv_generate!(u16);
ihv_generate!(u32);
ihv_generate!(usize);
ihv_generate!(isize);
ihv_generate!(i32);
// Custom derivations
// Vec<String>
impl TryFrom<HeaderValue> for IntoHeaderValue<Vec<String>> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => Ok(IntoHeaderValue(
hdr_value
.split(',')
.filter_map(|x| match x.trim() {
"" => None,
y => Some(y.to_string()),
})
.collect(),
)),
Err(e) => Err(format!(
"Unable to parse header: {:?} as a string - {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<Vec<String>>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<Vec<String>>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0.join(", ")) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} into a header - {}",
hdr_value, e
)),
}
}
}
// String
impl TryFrom<HeaderValue> for IntoHeaderValue<String> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())),
Err(e) => Err(format!("Unable to convert header {:?} to {}", hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<String>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<String>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} from a header {}",
hdr_value, e
)),
}
}
}
// Bool
impl TryFrom<HeaderValue> for IntoHeaderValue<bool> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match hdr_value.parse() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)),
Err(e) => Err(format!("Unable to parse bool from {} - {}", hdr_value, e)),
},
Err(e) => Err(format!(
"Unable to convert {:?} from a header {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<bool>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<bool>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0.to_string()) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert: {:?} into a header: {}",
hdr_value, e
)),
}
}
}
// DateTime
impl TryFrom<HeaderValue> for IntoHeaderValue<DateTime<Utc>> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) {
Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))),
Err(e) => Err(format!("Unable to parse: {} as date - {}", hdr_value, e)),
},
Err(e) => Err(format!(
"Unable to convert header {:?} to string {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<DateTime<Utc>>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<DateTime<Utc>>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} to a header: {}",
hdr_value, e
)),
}
}
}

View File

@ -0,0 +1,482 @@
#![allow(
missing_docs,
trivial_casts,
unused_variables,
unused_mut,
unused_imports,
unused_extern_crates,
non_camel_case_types
)]
#![allow(unused_imports, unused_attributes)]
#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)]
use async_trait::async_trait;
use axum::extract::*;
use axum_extra::extract::{CookieJar, Multipart};
use bytes::Bytes;
use http::Method;
use serde::{Deserialize, Serialize};
use types::*;
pub const BASE_PATH: &str = "";
pub const API_VERSION: &str = "1.0.7";
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum AnyOfGetResponse {
/// Success
Success(models::AnyOfObject),
/// AlternateSuccess
AlternateSuccess(models::Model12345AnyOfObject),
/// AnyOfSuccess
AnyOfSuccess(models::AnyOfGet202Response),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum CallbackWithHeaderPostResponse {
/// OK
OK,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum ComplexQueryParamGetResponse {
/// Success
Success,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum EnumInPathPathParamGetResponse {
/// Success
Success,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum JsonComplexQueryParamGetResponse {
/// Success
Success,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum MandatoryRequestHeaderGetResponse {
/// Success
Success,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum MergePatchJsonGetResponse {
/// merge-patch+json-encoded response
Merge(models::AnotherXmlObject),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum MultigetGetResponse {
/// JSON rsp
JSONRsp(models::AnotherXmlObject),
/// XML rsp
XMLRsp(String),
/// octet rsp
OctetRsp(ByteArray),
/// string rsp
StringRsp(String),
/// Duplicate Response long text. One.
DuplicateResponseLongText(models::AnotherXmlObject),
/// Duplicate Response long text. Two.
DuplicateResponseLongText_2(models::AnotherXmlObject),
/// Duplicate Response long text. Three.
DuplicateResponseLongText_3(models::AnotherXmlObject),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum MultipleAuthSchemeGetResponse {
/// Check that limiting to multiple required auth schemes works
CheckThatLimitingToMultipleRequiredAuthSchemesWorks,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum OneOfGetResponse {
/// Success
Success(models::OneOfGet200Response),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum OverrideServerGetResponse {
/// Success.
Success,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum ParamgetGetResponse {
/// JSON rsp
JSONRsp(models::AnotherXmlObject),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum ReadonlyAuthSchemeGetResponse {
/// Check that limiting to a single required auth scheme works
CheckThatLimitingToASingleRequiredAuthSchemeWorks,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum RegisterCallbackPostResponse {
/// OK
OK,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum RequiredOctetStreamPutResponse {
/// OK
OK,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum ResponsesWithHeadersGetResponse {
/// Success
Success {
body: String,
success_info: String,
bool_header: Option<bool>,
object_header: Option<models::ObjectHeader>,
},
/// Precondition Failed
PreconditionFailed {
further_info: Option<String>,
failure_info: Option<String>,
},
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum Rfc7807GetResponse {
/// OK
OK(models::ObjectWithArrayOfObjects),
/// NotFound
NotFound(models::ObjectWithArrayOfObjects),
/// NotAcceptable
NotAcceptable(String),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum UntypedPropertyGetResponse {
/// Check that untyped properties works
CheckThatUntypedPropertiesWorks,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum UuidGetResponse {
/// Duplicate Response long text. One.
DuplicateResponseLongText(uuid::Uuid),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum XmlExtraPostResponse {
/// OK
OK,
/// Bad Request
BadRequest,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum XmlOtherPostResponse {
/// OK
OK(String),
/// Bad Request
BadRequest,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum XmlOtherPutResponse {
/// OK
OK,
/// Bad Request
BadRequest,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum XmlPostResponse {
/// OK
OK,
/// Bad Request
BadRequest,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum XmlPutResponse {
/// OK
OK,
/// Bad Request
BadRequest,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum CreateRepoResponse {
/// Success
Success,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum GetRepoInfoResponse {
/// OK
OK(String),
}
/// API
#[async_trait]
#[allow(clippy::ptr_arg)]
pub trait Api {
/// AnyOfGet - GET /any-of
async fn any_of_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
query_params: models::AnyOfGetQueryParams,
) -> Result<AnyOfGetResponse, String>;
/// CallbackWithHeaderPost - POST /callback-with-header
async fn callback_with_header_post(
&self,
method: Method,
host: Host,
cookies: CookieJar,
query_params: models::CallbackWithHeaderPostQueryParams,
) -> Result<CallbackWithHeaderPostResponse, String>;
/// ComplexQueryParamGet - GET /complex-query-param
async fn complex_query_param_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
query_params: models::ComplexQueryParamGetQueryParams,
) -> Result<ComplexQueryParamGetResponse, String>;
/// EnumInPathPathParamGet - GET /enum_in_path/{path_param}
async fn enum_in_path_path_param_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::EnumInPathPathParamGetPathParams,
) -> Result<EnumInPathPathParamGetResponse, String>;
/// JsonComplexQueryParamGet - GET /json-complex-query-param
async fn json_complex_query_param_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
query_params: models::JsonComplexQueryParamGetQueryParams,
) -> Result<JsonComplexQueryParamGetResponse, String>;
/// MandatoryRequestHeaderGet - GET /mandatory-request-header
async fn mandatory_request_header_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
header_params: models::MandatoryRequestHeaderGetHeaderParams,
) -> Result<MandatoryRequestHeaderGetResponse, String>;
/// MergePatchJsonGet - GET /merge-patch-json
async fn merge_patch_json_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<MergePatchJsonGetResponse, String>;
/// Get some stuff..
///
/// MultigetGet - GET /multiget
async fn multiget_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<MultigetGetResponse, String>;
/// MultipleAuthSchemeGet - GET /multiple_auth_scheme
async fn multiple_auth_scheme_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<MultipleAuthSchemeGetResponse, String>;
/// OneOfGet - GET /one-of
async fn one_of_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<OneOfGetResponse, String>;
/// OverrideServerGet - GET /override-server
async fn override_server_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<OverrideServerGetResponse, String>;
/// Get some stuff with parameters..
///
/// ParamgetGet - GET /paramget
async fn paramget_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
query_params: models::ParamgetGetQueryParams,
) -> Result<ParamgetGetResponse, String>;
/// ReadonlyAuthSchemeGet - GET /readonly_auth_scheme
async fn readonly_auth_scheme_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<ReadonlyAuthSchemeGetResponse, String>;
/// RegisterCallbackPost - POST /register-callback
async fn register_callback_post(
&self,
method: Method,
host: Host,
cookies: CookieJar,
query_params: models::RegisterCallbackPostQueryParams,
) -> Result<RegisterCallbackPostResponse, String>;
/// RequiredOctetStreamPut - PUT /required_octet_stream
async fn required_octet_stream_put(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Bytes,
) -> Result<RequiredOctetStreamPutResponse, String>;
/// ResponsesWithHeadersGet - GET /responses_with_headers
async fn responses_with_headers_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<ResponsesWithHeadersGetResponse, String>;
/// Rfc7807Get - GET /rfc7807
async fn rfc7807_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Rfc7807GetResponse, String>;
/// UntypedPropertyGet - GET /untyped_property
async fn untyped_property_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Option<models::ObjectUntypedProps>,
) -> Result<UntypedPropertyGetResponse, String>;
/// UuidGet - GET /uuid
async fn uuid_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<UuidGetResponse, String>;
/// XmlExtraPost - POST /xml_extra
async fn xml_extra_post(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Bytes,
) -> Result<XmlExtraPostResponse, String>;
/// XmlOtherPost - POST /xml_other
async fn xml_other_post(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Bytes,
) -> Result<XmlOtherPostResponse, String>;
/// XmlOtherPut - PUT /xml_other
async fn xml_other_put(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Bytes,
) -> Result<XmlOtherPutResponse, String>;
/// Post an array.
///
/// XmlPost - POST /xml
async fn xml_post(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Bytes,
) -> Result<XmlPostResponse, String>;
/// XmlPut - PUT /xml
async fn xml_put(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Bytes,
) -> Result<XmlPutResponse, String>;
/// CreateRepo - POST /repos
async fn create_repo(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: models::ObjectParam,
) -> Result<CreateRepoResponse, String>;
/// GetRepoInfo - GET /repos/{repoId}
async fn get_repo_info(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::GetRepoInfoPathParams,
) -> Result<GetRepoInfoResponse, String>;
}
#[cfg(feature = "server")]
pub mod server;
pub mod models;
pub mod types;
#[cfg(feature = "server")]
pub(crate) mod header;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,665 @@
use std::{mem, str::FromStr};
use base64::{engine::general_purpose, Engine};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[allow(dead_code)]
pub struct Object(serde_json::Value);
impl validator::Validate for Object {
fn validate(&self) -> Result<(), validator::ValidationErrors> {
Ok(())
}
}
impl FromStr for Object {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(serde_json::Value::String(s.to_owned())))
}
}
/// Serde helper function to create a default `Option<Nullable<T>>` while
/// deserializing
pub fn default_optional_nullable<T>() -> Option<Nullable<T>> {
None
}
/// Serde helper function to deserialize into an `Option<Nullable<T>>`
pub fn deserialize_optional_nullable<'de, D, T>(
deserializer: D,
) -> Result<Option<Nullable<T>>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
Option::<T>::deserialize(deserializer).map(|val| match val {
Some(inner) => Some(Nullable::Present(inner)),
None => Some(Nullable::Null),
})
}
/// The Nullable type. Represents a value which may be specified as null on an API.
/// Note that this is distinct from a value that is optional and not present!
///
/// Nullable implements many of the same methods as the Option type (map, unwrap, etc).
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum Nullable<T> {
/// Null value
Null,
/// Value is present
Present(T),
}
impl<T> Nullable<T> {
/////////////////////////////////////////////////////////////////////////
// Querying the contained values
/////////////////////////////////////////////////////////////////////////
/// Returns `true` if the Nullable is a `Present` value.
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let x: Nullable<u32> = Nullable::Present(2);
/// assert_eq!(x.is_present(), true);
///
/// let x: Nullable<u32> = Nullable::Null;
/// assert_eq!(x.is_present(), false);
/// ```
#[inline]
pub fn is_present(&self) -> bool {
match *self {
Nullable::Present(_) => true,
Nullable::Null => false,
}
}
/// Returns `true` if the Nullable is a `Null` value.
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let x: Nullable<u32> = Nullable::Present(2);
/// assert_eq!(x.is_null(), false);
///
/// let x: Nullable<u32> = Nullable::Null;
/// assert_eq!(x.is_null(), true);
/// ```
#[inline]
pub fn is_null(&self) -> bool {
!self.is_present()
}
/////////////////////////////////////////////////////////////////////////
// Adapter for working with references
/////////////////////////////////////////////////////////////////////////
/// Converts from `Nullable<T>` to `Nullable<&T>`.
///
/// # Examples
///
/// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original.
/// The [`map`] method takes the `self` argument by value, consuming the original,
/// so this technique uses `as_ref` to first take a `Nullable` to a reference
/// to the value inside the original.
///
/// [`map`]: enum.Nullable.html#method.map
/// [`String`]: ../../std/string/struct.String.html
/// [`usize`]: ../../std/primitive.usize.html
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let num_as_str: Nullable<String> = Nullable::Present("10".to_string());
/// // First, cast `Nullable<String>` to `Nullable<&String>` with `as_ref`,
/// // then consume *that* with `map`, leaving `num_as_str` on the stack.
/// let num_as_int: Nullable<usize> = num_as_str.as_ref().map(|n| n.len());
/// println!("still can print num_as_str: {:?}", num_as_str);
/// ```
#[inline]
pub fn as_ref(&self) -> Nullable<&T> {
match *self {
Nullable::Present(ref x) => Nullable::Present(x),
Nullable::Null => Nullable::Null,
}
}
/// Converts from `Nullable<T>` to `Nullable<&mut T>`.
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let mut x = Nullable::Present(2);
/// match x.as_mut() {
/// Nullable::Present(v) => *v = 42,
/// Nullable::Null => {},
/// }
/// assert_eq!(x, Nullable::Present(42));
/// ```
#[inline]
pub fn as_mut(&mut self) -> Nullable<&mut T> {
match *self {
Nullable::Present(ref mut x) => Nullable::Present(x),
Nullable::Null => Nullable::Null,
}
}
/////////////////////////////////////////////////////////////////////////
// Getting to contained values
/////////////////////////////////////////////////////////////////////////
/// Unwraps a Nullable, yielding the content of a `Nullable::Present`.
///
/// # Panics
///
/// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by
/// `msg`.
///
/// [`Nullable::Null`]: #variant.Null
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let x = Nullable::Present("value");
/// assert_eq!(x.expect("the world is ending"), "value");
/// ```
///
/// ```{.should_panic}
/// # use openapi_v3::types::Nullable;
///
/// let x: Nullable<&str> = Nullable::Null;
/// x.expect("the world is ending"); // panics with `the world is ending`
/// ```
#[inline]
pub fn expect(self, msg: &str) -> T {
match self {
Nullable::Present(val) => val,
Nullable::Null => expect_failed(msg),
}
}
/// Moves the value `v` out of the `Nullable<T>` if it is `Nullable::Present(v)`.
///
/// In general, because this function may panic, its use is discouraged.
/// Instead, prefer to use pattern matching and handle the `Nullable::Null`
/// case explicitly.
///
/// # Panics
///
/// Panics if the self value equals [`Nullable::Null`].
///
/// [`Nullable::Null`]: #variant.Null
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let x = Nullable::Present("air");
/// assert_eq!(x.unwrap(), "air");
/// ```
///
/// ```{.should_panic}
/// # use openapi_v3::types::Nullable;
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.unwrap(), "air"); // fails
/// ```
#[inline]
pub fn unwrap(self) -> T {
match self {
Nullable::Present(val) => val,
Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"),
}
}
/// Returns the contained value or a default.
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car");
/// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike");
/// ```
#[inline]
pub fn unwrap_or(self, def: T) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => def,
}
}
/// Returns the contained value or computes it from a closure.
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let k = 10;
/// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4);
/// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20);
/// ```
#[inline]
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => f(),
}
}
/////////////////////////////////////////////////////////////////////////
// Transforming contained values
/////////////////////////////////////////////////////////////////////////
/// Maps a `Nullable<T>` to `Nullable<U>` by applying a function to a contained value.
///
/// # Examples
///
/// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original:
///
/// [`String`]: ../../std/string/struct.String.html
/// [`usize`]: ../../std/primitive.usize.html
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let maybe_some_string = Nullable::Present(String::from("Hello, World!"));
/// // `Nullable::map` takes self *by value*, consuming `maybe_some_string`
/// let maybe_some_len = maybe_some_string.map(|s| s.len());
///
/// assert_eq!(maybe_some_len, Nullable::Present(13));
/// ```
#[inline]
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Nullable<U> {
match self {
Nullable::Present(x) => Nullable::Present(f(x)),
Nullable::Null => Nullable::Null,
}
}
/// Applies a function to the contained value (if any),
/// or returns a `default` (if not).
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.map_or(42, |v| v.len()), 3);
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.map_or(42, |v| v.len()), 42);
/// ```
#[inline]
pub fn map_or<U, F: FnOnce(T) -> U>(self, default: U, f: F) -> U {
match self {
Nullable::Present(t) => f(t),
Nullable::Null => default,
}
}
/// Applies a function to the contained value (if any),
/// or computes a `default` (if not).
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let k = 21;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3);
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42);
/// ```
#[inline]
pub fn map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
match self {
Nullable::Present(t) => f(t),
Nullable::Null => default(),
}
}
/// Transforms the `Nullable<T>` into a [`Result<T, E>`], mapping `Nullable::Present(v)` to
/// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err].
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [Err]: ../../std/result/enum.Result.html#variant.Err
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.ok_or(0), Ok("foo"));
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.ok_or(0), Err(0));
/// ```
#[inline]
pub fn ok_or<E>(self, err: E) -> Result<T, E> {
match self {
Nullable::Present(v) => Ok(v),
Nullable::Null => Err(err),
}
}
/// Transforms the `Nullable<T>` into a [`Result<T, E>`], mapping `Nullable::Present(v)` to
/// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err].
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [Err]: ../../std/result/enum.Result.html#variant.Err
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.ok_or_else(|| 0), Ok("foo"));
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.ok_or_else(|| 0), Err(0));
/// ```
#[inline]
pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
match self {
Nullable::Present(v) => Ok(v),
Nullable::Null => Err(err()),
}
}
/////////////////////////////////////////////////////////////////////////
// Boolean operations on the values, eager and lazy
/////////////////////////////////////////////////////////////////////////
/// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`.
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let x = Nullable::Present(2);
/// let y: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.and(y), Nullable::Null);
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y = Nullable::Present("foo");
/// assert_eq!(x.and(y), Nullable::Null);
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Present("foo");
/// assert_eq!(x.and(y), Nullable::Present("foo"));
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.and(y), Nullable::Null);
/// ```
#[inline]
pub fn and<U>(self, optb: Nullable<U>) -> Nullable<U> {
match self {
Nullable::Present(_) => optb,
Nullable::Null => Nullable::Null,
}
}
/// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the
/// wrapped value and returns the result.
///
/// Some languages call this operation flatmap.
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// fn sq(x: u32) -> Nullable<u32> { Nullable::Present(x * x) }
/// fn nope(_: u32) -> Nullable<u32> { Nullable::Null }
///
/// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16));
/// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null);
/// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null);
/// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null);
/// ```
#[inline]
pub fn and_then<U, F: FnOnce(T) -> Nullable<U>>(self, f: F) -> Nullable<U> {
match self {
Nullable::Present(x) => f(x),
Nullable::Null => Nullable::Null,
}
}
/// Returns the Nullable if it contains a value, otherwise returns `optb`.
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Null;
/// assert_eq!(x.or(y), Nullable::Present(2));
///
/// let x = Nullable::Null;
/// let y = Nullable::Present(100);
/// assert_eq!(x.or(y), Nullable::Present(100));
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Present(100);
/// assert_eq!(x.or(y), Nullable::Present(2));
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y = Nullable::Null;
/// assert_eq!(x.or(y), Nullable::Null);
/// ```
#[inline]
pub fn or(self, optb: Nullable<T>) -> Nullable<T> {
match self {
Nullable::Present(_) => self,
Nullable::Null => optb,
}
}
/// Returns the Nullable if it contains a value, otherwise calls `f` and
/// returns the result.
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// fn nobody() -> Nullable<&'static str> { Nullable::Null }
/// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") }
///
/// assert_eq!(Nullable::Present("barbarians").or_else(vikings),
/// Nullable::Present("barbarians"));
/// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings"));
/// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null);
/// ```
#[inline]
pub fn or_else<F: FnOnce() -> Nullable<T>>(self, f: F) -> Nullable<T> {
match self {
Nullable::Present(_) => self,
Nullable::Null => f(),
}
}
/////////////////////////////////////////////////////////////////////////
// Misc
/////////////////////////////////////////////////////////////////////////
/// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place.
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let mut x = Nullable::Present(2);
/// x.take();
/// assert_eq!(x, Nullable::Null);
///
/// let mut x: Nullable<u32> = Nullable::Null;
/// x.take();
/// assert_eq!(x, Nullable::Null);
/// ```
#[inline]
pub fn take(&mut self) -> Nullable<T> {
mem::replace(self, Nullable::Null)
}
}
impl<'a, T: Clone> Nullable<&'a T> {
/// Maps an `Nullable<&T>` to an `Nullable<T>` by cloning the contents of the
/// Nullable.
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let x = 12;
/// let opt_x = Nullable::Present(&x);
/// assert_eq!(opt_x, Nullable::Present(&12));
/// let cloned = opt_x.cloned();
/// assert_eq!(cloned, Nullable::Present(12));
/// ```
pub fn cloned(self) -> Nullable<T> {
self.map(Clone::clone)
}
}
impl<T: Default> Nullable<T> {
/// Returns the contained value or a default
///
/// Consumes the `self` argument then, if `Nullable::Present`, returns the contained
/// value, otherwise if `Nullable::Null`, returns the default value for that
/// type.
///
/// # Examples
///
/// ```
/// # use openapi_v3::types::Nullable;
///
/// let x = Nullable::Present(42);
/// assert_eq!(42, x.unwrap_or_default());
///
/// let y: Nullable<i32> = Nullable::Null;
/// assert_eq!(0, y.unwrap_or_default());
/// ```
#[inline]
pub fn unwrap_or_default(self) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => Default::default(),
}
}
}
impl<T> Default for Nullable<T> {
/// Returns None.
#[inline]
fn default() -> Nullable<T> {
Nullable::Null
}
}
impl<T> From<T> for Nullable<T> {
fn from(val: T) -> Nullable<T> {
Nullable::Present(val)
}
}
impl<T> Serialize for Nullable<T>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
Nullable::Present(ref inner) => serializer.serialize_some(&inner),
Nullable::Null => serializer.serialize_none(),
}
}
}
impl<'de, T> Deserialize<'de> for Nullable<T>
where
T: serde::de::DeserializeOwned,
{
fn deserialize<D>(deserializer: D) -> Result<Nullable<T>, D::Error>
where
D: Deserializer<'de>,
{
// In order to deserialize a required, but nullable, value, we first have to check whether
// the value is present at all. To do this, we deserialize to a serde_json::Value, which
// fails if the value is missing, or gives serde_json::Value::Null if the value is present.
// If that succeeds as null, we can easily return a Null.
// If that succeeds as some value, we deserialize that value and return a Present.
// If that errors, we return the error.
let presence: Result<::serde_json::Value, _> =
serde::Deserialize::deserialize(deserializer);
match presence {
Ok(serde_json::Value::Null) => Ok(Nullable::Null),
Ok(some_value) => serde_json::from_value(some_value)
.map(Nullable::Present)
.map_err(serde::de::Error::custom),
Err(x) => Err(x),
}
}
}
#[inline(never)]
#[cold]
fn expect_failed(msg: &str) -> ! {
panic!("{}", msg)
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
/// Base64-encoded byte array
pub struct ByteArray(pub Vec<u8>);
impl Serialize for ByteArray {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0))
}
}
impl<'de> Deserialize<'de> for ByteArray {
fn deserialize<D>(deserializer: D) -> Result<ByteArray, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match general_purpose::STANDARD.decode(s) {
Ok(bin) => Ok(ByteArray(bin)),
_ => Err(serde::de::Error::custom("invalid base64")),
}
}
}

View File

@ -0,0 +1,2 @@
target
Cargo.lock

View File

@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@ -0,0 +1,8 @@
.gitignore
Cargo.toml
README.md
src/header.rs
src/lib.rs
src/models.rs
src/server/mod.rs
src/types.rs

View File

@ -0,0 +1 @@
7.3.0-SNAPSHOT

View File

@ -0,0 +1,46 @@
[package]
name = "ops-v3"
version = "0.0.1"
authors = ["OpenAPI Generator team and contributors"]
description = "No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)"
edition = "2021"
[features]
default = ["server"]
server = []
conversion = [
"frunk",
"frunk_derives",
"frunk_core",
"frunk-enum-core",
"frunk-enum-derive",
]
[dependencies]
async-trait = "0.1"
axum = { version = "0.7" }
axum-extra = { version = "0.9", features = ["cookie", "multipart"] }
base64 = "0.21"
bytes = "1"
chrono = { version = "0.4", features = ["serde"] }
frunk = { version = "0.4", optional = true }
frunk-enum-core = { version = "0.3", optional = true }
frunk-enum-derive = { version = "0.3", optional = true }
frunk_core = { version = "0.4", optional = true }
frunk_derives = { version = "0.4", optional = true }
http = "1"
lazy_static = "1"
regex = "1"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["raw_value"] }
serde_urlencoded = "0.7"
tokio = { version = "1", default-features = false, features = [
"signal",
"rt-multi-thread",
] }
tracing = { version = "0.1", features = ["attributes"] }
uuid = { version = "1", features = ["serde"] }
validator = { version = "0.16", features = ["derive"] }
[dev-dependencies]
tracing-subscriber = "0.3"

View File

@ -0,0 +1,91 @@
# Rust API for ops-v3
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
## Overview
This server was generated by the [openapi-generator]
(https://openapi-generator.tech) project. By using the
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
server, you can easily generate a server stub.
To see how to make this your own, look here: [README]((https://openapi-generator.tech))
- API version: 0.0.1
This autogenerated project defines an API crate `ops-v3` which contains:
* An `Api` trait defining the API in Rust.
* Data types representing the underlying data model.
* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation.
* Request validations (path, query, body params) are included.
## Using the generated library
The generated library has a few optional features that can be activated through Cargo.
* `server`
* This defaults to enabled and creates the basic skeleton of a server implementation based on Axum.
* To create the server stack you'll need to provide an implementation of the API trait to provide the server function.
* `conversions`
* This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types.
See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`.
### Example
```rust
struct ServerImpl {
// database: sea_orm::DbConn,
}
#[allow(unused_variables)]
#[async_trait]
impl ops-v3::Api for ServerImpl {
// API implementation goes here
}
pub async fn start_server(addr: &str) {
// initialize tracing
tracing_subscriber::fmt::init();
// Init Axum router
let app = ops-v3::server::new(Arc::new(ServerImpl));
// Add layers to the router
let app = app.layer(...);
// Run the server with graceful shutdown
let listener = TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await
.unwrap();
}
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
}
```

View File

@ -0,0 +1,180 @@
use std::{convert::TryFrom, fmt, ops::Deref};
use chrono::{DateTime, Utc};
use http::HeaderValue;
/// A struct to allow homogeneous conversion into a HeaderValue. We can't
/// implement the From/Into trait on HeaderValue because we don't own
/// either of the types.
#[derive(Debug, Clone)]
pub(crate) struct IntoHeaderValue<T>(pub T);
// Generic implementations
impl<T> Deref for IntoHeaderValue<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
// Derive for each TryFrom<T> in http::HeaderValue
macro_rules! ihv_generate {
($t:ident) => {
impl TryFrom<HeaderValue> for IntoHeaderValue<$t> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match hdr_value.parse::<$t>() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)),
Err(e) => Err(format!("Unable to parse {} as a string: {}",
stringify!($t), e)),
},
Err(e) => Err(format!("Unable to parse header {:?} as a string - {}",
hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<$t>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result<Self, Self::Error> {
Ok(hdr_value.0.into())
}
}
};
}
ihv_generate!(u64);
ihv_generate!(i64);
ihv_generate!(i16);
ihv_generate!(u16);
ihv_generate!(u32);
ihv_generate!(usize);
ihv_generate!(isize);
ihv_generate!(i32);
// Custom derivations
// Vec<String>
impl TryFrom<HeaderValue> for IntoHeaderValue<Vec<String>> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => Ok(IntoHeaderValue(
hdr_value
.split(',')
.filter_map(|x| match x.trim() {
"" => None,
y => Some(y.to_string()),
})
.collect())),
Err(e) => Err(format!("Unable to parse header: {:?} as a string - {}",
hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<Vec<String>>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<Vec<String>>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0.join(", ")) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!("Unable to convert {:?} into a header - {}",
hdr_value, e))
}
}
}
// String
impl TryFrom<HeaderValue> for IntoHeaderValue<String> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())),
Err(e) => Err(format!("Unable to convert header {:?} to {}",
hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<String>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<String>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!("Unable to convert {:?} from a header {}",
hdr_value, e))
}
}
}
// Bool
impl TryFrom<HeaderValue> for IntoHeaderValue<bool> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match hdr_value.parse() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)),
Err(e) => Err(format!("Unable to parse bool from {} - {}",
hdr_value, e)),
},
Err(e) => Err(format!("Unable to convert {:?} from a header {}",
hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<bool>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<bool>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0.to_string()) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!("Unable to convert: {:?} into a header: {}",
hdr_value, e))
}
}
}
// DateTime
impl TryFrom<HeaderValue> for IntoHeaderValue<DateTime<Utc>> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) {
Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))),
Err(e) => Err(format!("Unable to parse: {} as date - {}",
hdr_value, e)),
},
Err(e) => Err(format!("Unable to convert header {:?} to string {}",
hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<DateTime<Utc>>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<DateTime<Utc>>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!("Unable to convert {:?} to a header: {}",
hdr_value, e)),
}
}
}

View File

@ -0,0 +1,586 @@
#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)]
#![allow(unused_imports, unused_attributes)]
#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)]
use async_trait::async_trait;
use axum::extract::*;
use axum_extra::extract::{CookieJar, Multipart};
use bytes::Bytes;
use http::Method;
use serde::{Deserialize, Serialize};
use types::*;
pub const BASE_PATH: &str = "";
pub const API_VERSION: &str = "0.0.1";
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op10GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op11GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op12GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op13GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op14GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op15GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op16GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op17GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op18GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op19GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op1GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op20GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op21GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op22GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op23GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op24GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op25GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op26GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op27GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op28GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op29GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op2GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op30GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op31GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op32GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op33GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op34GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op35GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op36GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op37GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op3GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op4GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op5GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op6GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op7GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op8GetResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Op9GetResponse {
/// OK
OK
}
/// API
#[async_trait]
#[allow(clippy::ptr_arg)]
pub trait Api {
/// Op10Get - GET /op10
async fn op10_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op10GetResponse, String>;
/// Op11Get - GET /op11
async fn op11_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op11GetResponse, String>;
/// Op12Get - GET /op12
async fn op12_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op12GetResponse, String>;
/// Op13Get - GET /op13
async fn op13_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op13GetResponse, String>;
/// Op14Get - GET /op14
async fn op14_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op14GetResponse, String>;
/// Op15Get - GET /op15
async fn op15_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op15GetResponse, String>;
/// Op16Get - GET /op16
async fn op16_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op16GetResponse, String>;
/// Op17Get - GET /op17
async fn op17_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op17GetResponse, String>;
/// Op18Get - GET /op18
async fn op18_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op18GetResponse, String>;
/// Op19Get - GET /op19
async fn op19_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op19GetResponse, String>;
/// Op1Get - GET /op1
async fn op1_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op1GetResponse, String>;
/// Op20Get - GET /op20
async fn op20_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op20GetResponse, String>;
/// Op21Get - GET /op21
async fn op21_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op21GetResponse, String>;
/// Op22Get - GET /op22
async fn op22_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op22GetResponse, String>;
/// Op23Get - GET /op23
async fn op23_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op23GetResponse, String>;
/// Op24Get - GET /op24
async fn op24_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op24GetResponse, String>;
/// Op25Get - GET /op25
async fn op25_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op25GetResponse, String>;
/// Op26Get - GET /op26
async fn op26_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op26GetResponse, String>;
/// Op27Get - GET /op27
async fn op27_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op27GetResponse, String>;
/// Op28Get - GET /op28
async fn op28_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op28GetResponse, String>;
/// Op29Get - GET /op29
async fn op29_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op29GetResponse, String>;
/// Op2Get - GET /op2
async fn op2_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op2GetResponse, String>;
/// Op30Get - GET /op30
async fn op30_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op30GetResponse, String>;
/// Op31Get - GET /op31
async fn op31_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op31GetResponse, String>;
/// Op32Get - GET /op32
async fn op32_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op32GetResponse, String>;
/// Op33Get - GET /op33
async fn op33_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op33GetResponse, String>;
/// Op34Get - GET /op34
async fn op34_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op34GetResponse, String>;
/// Op35Get - GET /op35
async fn op35_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op35GetResponse, String>;
/// Op36Get - GET /op36
async fn op36_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op36GetResponse, String>;
/// Op37Get - GET /op37
async fn op37_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op37GetResponse, String>;
/// Op3Get - GET /op3
async fn op3_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op3GetResponse, String>;
/// Op4Get - GET /op4
async fn op4_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op4GetResponse, String>;
/// Op5Get - GET /op5
async fn op5_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op5GetResponse, String>;
/// Op6Get - GET /op6
async fn op6_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op6GetResponse, String>;
/// Op7Get - GET /op7
async fn op7_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op7GetResponse, String>;
/// Op8Get - GET /op8
async fn op8_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op8GetResponse, String>;
/// Op9Get - GET /op9
async fn op9_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Op9GetResponse, String>;
}
#[cfg(feature = "server")]
pub mod server;
pub mod models;
pub mod types;
#[cfg(feature = "server")]
pub(crate) mod header;

View File

@ -0,0 +1,47 @@
#![allow(unused_qualifications)]
use http::HeaderValue;
use validator::Validate;
#[cfg(feature = "server")]
use crate::header;
use crate::{models, types::*};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,665 @@
use std::{mem, str::FromStr};
use base64::{engine::general_purpose, Engine};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[allow(dead_code)]
pub struct Object(serde_json::Value);
impl validator::Validate for Object {
fn validate(&self) -> Result<(), validator::ValidationErrors> {
Ok(())
}
}
impl FromStr for Object {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(serde_json::Value::String(s.to_owned())))
}
}
/// Serde helper function to create a default `Option<Nullable<T>>` while
/// deserializing
pub fn default_optional_nullable<T>() -> Option<Nullable<T>> {
None
}
/// Serde helper function to deserialize into an `Option<Nullable<T>>`
pub fn deserialize_optional_nullable<'de, D, T>(
deserializer: D,
) -> Result<Option<Nullable<T>>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
Option::<T>::deserialize(deserializer).map(|val| match val {
Some(inner) => Some(Nullable::Present(inner)),
None => Some(Nullable::Null),
})
}
/// The Nullable type. Represents a value which may be specified as null on an API.
/// Note that this is distinct from a value that is optional and not present!
///
/// Nullable implements many of the same methods as the Option type (map, unwrap, etc).
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum Nullable<T> {
/// Null value
Null,
/// Value is present
Present(T),
}
impl<T> Nullable<T> {
/////////////////////////////////////////////////////////////////////////
// Querying the contained values
/////////////////////////////////////////////////////////////////////////
/// Returns `true` if the Nullable is a `Present` value.
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let x: Nullable<u32> = Nullable::Present(2);
/// assert_eq!(x.is_present(), true);
///
/// let x: Nullable<u32> = Nullable::Null;
/// assert_eq!(x.is_present(), false);
/// ```
#[inline]
pub fn is_present(&self) -> bool {
match *self {
Nullable::Present(_) => true,
Nullable::Null => false,
}
}
/// Returns `true` if the Nullable is a `Null` value.
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let x: Nullable<u32> = Nullable::Present(2);
/// assert_eq!(x.is_null(), false);
///
/// let x: Nullable<u32> = Nullable::Null;
/// assert_eq!(x.is_null(), true);
/// ```
#[inline]
pub fn is_null(&self) -> bool {
!self.is_present()
}
/////////////////////////////////////////////////////////////////////////
// Adapter for working with references
/////////////////////////////////////////////////////////////////////////
/// Converts from `Nullable<T>` to `Nullable<&T>`.
///
/// # Examples
///
/// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original.
/// The [`map`] method takes the `self` argument by value, consuming the original,
/// so this technique uses `as_ref` to first take a `Nullable` to a reference
/// to the value inside the original.
///
/// [`map`]: enum.Nullable.html#method.map
/// [`String`]: ../../std/string/struct.String.html
/// [`usize`]: ../../std/primitive.usize.html
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let num_as_str: Nullable<String> = Nullable::Present("10".to_string());
/// // First, cast `Nullable<String>` to `Nullable<&String>` with `as_ref`,
/// // then consume *that* with `map`, leaving `num_as_str` on the stack.
/// let num_as_int: Nullable<usize> = num_as_str.as_ref().map(|n| n.len());
/// println!("still can print num_as_str: {:?}", num_as_str);
/// ```
#[inline]
pub fn as_ref(&self) -> Nullable<&T> {
match *self {
Nullable::Present(ref x) => Nullable::Present(x),
Nullable::Null => Nullable::Null,
}
}
/// Converts from `Nullable<T>` to `Nullable<&mut T>`.
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let mut x = Nullable::Present(2);
/// match x.as_mut() {
/// Nullable::Present(v) => *v = 42,
/// Nullable::Null => {},
/// }
/// assert_eq!(x, Nullable::Present(42));
/// ```
#[inline]
pub fn as_mut(&mut self) -> Nullable<&mut T> {
match *self {
Nullable::Present(ref mut x) => Nullable::Present(x),
Nullable::Null => Nullable::Null,
}
}
/////////////////////////////////////////////////////////////////////////
// Getting to contained values
/////////////////////////////////////////////////////////////////////////
/// Unwraps a Nullable, yielding the content of a `Nullable::Present`.
///
/// # Panics
///
/// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by
/// `msg`.
///
/// [`Nullable::Null`]: #variant.Null
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let x = Nullable::Present("value");
/// assert_eq!(x.expect("the world is ending"), "value");
/// ```
///
/// ```{.should_panic}
/// # use ops_v3::types::Nullable;
///
/// let x: Nullable<&str> = Nullable::Null;
/// x.expect("the world is ending"); // panics with `the world is ending`
/// ```
#[inline]
pub fn expect(self, msg: &str) -> T {
match self {
Nullable::Present(val) => val,
Nullable::Null => expect_failed(msg),
}
}
/// Moves the value `v` out of the `Nullable<T>` if it is `Nullable::Present(v)`.
///
/// In general, because this function may panic, its use is discouraged.
/// Instead, prefer to use pattern matching and handle the `Nullable::Null`
/// case explicitly.
///
/// # Panics
///
/// Panics if the self value equals [`Nullable::Null`].
///
/// [`Nullable::Null`]: #variant.Null
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let x = Nullable::Present("air");
/// assert_eq!(x.unwrap(), "air");
/// ```
///
/// ```{.should_panic}
/// # use ops_v3::types::Nullable;
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.unwrap(), "air"); // fails
/// ```
#[inline]
pub fn unwrap(self) -> T {
match self {
Nullable::Present(val) => val,
Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"),
}
}
/// Returns the contained value or a default.
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car");
/// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike");
/// ```
#[inline]
pub fn unwrap_or(self, def: T) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => def,
}
}
/// Returns the contained value or computes it from a closure.
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let k = 10;
/// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4);
/// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20);
/// ```
#[inline]
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => f(),
}
}
/////////////////////////////////////////////////////////////////////////
// Transforming contained values
/////////////////////////////////////////////////////////////////////////
/// Maps a `Nullable<T>` to `Nullable<U>` by applying a function to a contained value.
///
/// # Examples
///
/// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original:
///
/// [`String`]: ../../std/string/struct.String.html
/// [`usize`]: ../../std/primitive.usize.html
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let maybe_some_string = Nullable::Present(String::from("Hello, World!"));
/// // `Nullable::map` takes self *by value*, consuming `maybe_some_string`
/// let maybe_some_len = maybe_some_string.map(|s| s.len());
///
/// assert_eq!(maybe_some_len, Nullable::Present(13));
/// ```
#[inline]
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Nullable<U> {
match self {
Nullable::Present(x) => Nullable::Present(f(x)),
Nullable::Null => Nullable::Null,
}
}
/// Applies a function to the contained value (if any),
/// or returns a `default` (if not).
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.map_or(42, |v| v.len()), 3);
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.map_or(42, |v| v.len()), 42);
/// ```
#[inline]
pub fn map_or<U, F: FnOnce(T) -> U>(self, default: U, f: F) -> U {
match self {
Nullable::Present(t) => f(t),
Nullable::Null => default,
}
}
/// Applies a function to the contained value (if any),
/// or computes a `default` (if not).
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let k = 21;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3);
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42);
/// ```
#[inline]
pub fn map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
match self {
Nullable::Present(t) => f(t),
Nullable::Null => default(),
}
}
/// Transforms the `Nullable<T>` into a [`Result<T, E>`], mapping `Nullable::Present(v)` to
/// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err].
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [Err]: ../../std/result/enum.Result.html#variant.Err
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.ok_or(0), Ok("foo"));
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.ok_or(0), Err(0));
/// ```
#[inline]
pub fn ok_or<E>(self, err: E) -> Result<T, E> {
match self {
Nullable::Present(v) => Ok(v),
Nullable::Null => Err(err),
}
}
/// Transforms the `Nullable<T>` into a [`Result<T, E>`], mapping `Nullable::Present(v)` to
/// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err].
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [Err]: ../../std/result/enum.Result.html#variant.Err
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.ok_or_else(|| 0), Ok("foo"));
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.ok_or_else(|| 0), Err(0));
/// ```
#[inline]
pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
match self {
Nullable::Present(v) => Ok(v),
Nullable::Null => Err(err()),
}
}
/////////////////////////////////////////////////////////////////////////
// Boolean operations on the values, eager and lazy
/////////////////////////////////////////////////////////////////////////
/// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`.
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let x = Nullable::Present(2);
/// let y: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.and(y), Nullable::Null);
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y = Nullable::Present("foo");
/// assert_eq!(x.and(y), Nullable::Null);
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Present("foo");
/// assert_eq!(x.and(y), Nullable::Present("foo"));
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.and(y), Nullable::Null);
/// ```
#[inline]
pub fn and<U>(self, optb: Nullable<U>) -> Nullable<U> {
match self {
Nullable::Present(_) => optb,
Nullable::Null => Nullable::Null,
}
}
/// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the
/// wrapped value and returns the result.
///
/// Some languages call this operation flatmap.
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// fn sq(x: u32) -> Nullable<u32> { Nullable::Present(x * x) }
/// fn nope(_: u32) -> Nullable<u32> { Nullable::Null }
///
/// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16));
/// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null);
/// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null);
/// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null);
/// ```
#[inline]
pub fn and_then<U, F: FnOnce(T) -> Nullable<U>>(self, f: F) -> Nullable<U> {
match self {
Nullable::Present(x) => f(x),
Nullable::Null => Nullable::Null,
}
}
/// Returns the Nullable if it contains a value, otherwise returns `optb`.
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Null;
/// assert_eq!(x.or(y), Nullable::Present(2));
///
/// let x = Nullable::Null;
/// let y = Nullable::Present(100);
/// assert_eq!(x.or(y), Nullable::Present(100));
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Present(100);
/// assert_eq!(x.or(y), Nullable::Present(2));
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y = Nullable::Null;
/// assert_eq!(x.or(y), Nullable::Null);
/// ```
#[inline]
pub fn or(self, optb: Nullable<T>) -> Nullable<T> {
match self {
Nullable::Present(_) => self,
Nullable::Null => optb,
}
}
/// Returns the Nullable if it contains a value, otherwise calls `f` and
/// returns the result.
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// fn nobody() -> Nullable<&'static str> { Nullable::Null }
/// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") }
///
/// assert_eq!(Nullable::Present("barbarians").or_else(vikings),
/// Nullable::Present("barbarians"));
/// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings"));
/// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null);
/// ```
#[inline]
pub fn or_else<F: FnOnce() -> Nullable<T>>(self, f: F) -> Nullable<T> {
match self {
Nullable::Present(_) => self,
Nullable::Null => f(),
}
}
/////////////////////////////////////////////////////////////////////////
// Misc
/////////////////////////////////////////////////////////////////////////
/// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place.
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let mut x = Nullable::Present(2);
/// x.take();
/// assert_eq!(x, Nullable::Null);
///
/// let mut x: Nullable<u32> = Nullable::Null;
/// x.take();
/// assert_eq!(x, Nullable::Null);
/// ```
#[inline]
pub fn take(&mut self) -> Nullable<T> {
mem::replace(self, Nullable::Null)
}
}
impl<'a, T: Clone> Nullable<&'a T> {
/// Maps an `Nullable<&T>` to an `Nullable<T>` by cloning the contents of the
/// Nullable.
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let x = 12;
/// let opt_x = Nullable::Present(&x);
/// assert_eq!(opt_x, Nullable::Present(&12));
/// let cloned = opt_x.cloned();
/// assert_eq!(cloned, Nullable::Present(12));
/// ```
pub fn cloned(self) -> Nullable<T> {
self.map(Clone::clone)
}
}
impl<T: Default> Nullable<T> {
/// Returns the contained value or a default
///
/// Consumes the `self` argument then, if `Nullable::Present`, returns the contained
/// value, otherwise if `Nullable::Null`, returns the default value for that
/// type.
///
/// # Examples
///
/// ```
/// # use ops_v3::types::Nullable;
///
/// let x = Nullable::Present(42);
/// assert_eq!(42, x.unwrap_or_default());
///
/// let y: Nullable<i32> = Nullable::Null;
/// assert_eq!(0, y.unwrap_or_default());
/// ```
#[inline]
pub fn unwrap_or_default(self) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => Default::default(),
}
}
}
impl<T> Default for Nullable<T> {
/// Returns None.
#[inline]
fn default() -> Nullable<T> {
Nullable::Null
}
}
impl<T> From<T> for Nullable<T> {
fn from(val: T) -> Nullable<T> {
Nullable::Present(val)
}
}
impl<T> Serialize for Nullable<T>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
Nullable::Present(ref inner) => serializer.serialize_some(&inner),
Nullable::Null => serializer.serialize_none(),
}
}
}
impl<'de, T> Deserialize<'de> for Nullable<T>
where
T: serde::de::DeserializeOwned,
{
fn deserialize<D>(deserializer: D) -> Result<Nullable<T>, D::Error>
where
D: Deserializer<'de>,
{
// In order to deserialize a required, but nullable, value, we first have to check whether
// the value is present at all. To do this, we deserialize to a serde_json::Value, which
// fails if the value is missing, or gives serde_json::Value::Null if the value is present.
// If that succeeds as null, we can easily return a Null.
// If that succeeds as some value, we deserialize that value and return a Present.
// If that errors, we return the error.
let presence: Result<::serde_json::Value, _> =
serde::Deserialize::deserialize(deserializer);
match presence {
Ok(serde_json::Value::Null) => Ok(Nullable::Null),
Ok(some_value) => serde_json::from_value(some_value)
.map(Nullable::Present)
.map_err(serde::de::Error::custom),
Err(x) => Err(x),
}
}
}
#[inline(never)]
#[cold]
fn expect_failed(msg: &str) -> ! {
panic!("{}", msg)
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
/// Base64-encoded byte array
pub struct ByteArray(pub Vec<u8>);
impl Serialize for ByteArray {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0))
}
}
impl<'de> Deserialize<'de> for ByteArray {
fn deserialize<D>(deserializer: D) -> Result<ByteArray, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match general_purpose::STANDARD.decode(s) {
Ok(bin) => Ok(ByteArray(bin)),
_ => Err(serde::de::Error::custom("invalid base64")),
}
}
}

View File

@ -0,0 +1,2 @@
target
Cargo.lock

View File

@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@ -0,0 +1,8 @@
.gitignore
Cargo.toml
README.md
src/header.rs
src/lib.rs
src/models.rs
src/server/mod.rs
src/types.rs

View File

@ -0,0 +1,48 @@
[package]
name = "petstore-with-fake-endpoints-models-for-testing"
version = "1.0.0"
authors = ["OpenAPI Generator team and contributors"]
description = "This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\"
license = "Apache-2.0"
edition = "2021"
publish = ["crates-io"]
[features]
default = ["server"]
server = []
conversion = [
"frunk",
"frunk_derives",
"frunk_core",
"frunk-enum-core",
"frunk-enum-derive",
]
[dependencies]
async-trait = "0.1"
axum = { version = "0.7" }
axum-extra = { version = "0.9", features = ["cookie", "multipart"] }
base64 = "0.21"
bytes = "1"
chrono = { version = "0.4", features = ["serde"] }
frunk = { version = "0.4", optional = true }
frunk-enum-core = { version = "0.3", optional = true }
frunk-enum-derive = { version = "0.3", optional = true }
frunk_core = { version = "0.4", optional = true }
frunk_derives = { version = "0.4", optional = true }
http = "1"
lazy_static = "1"
regex = "1"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["raw_value"] }
serde_urlencoded = "0.7"
tokio = { version = "1", default-features = false, features = [
"signal",
"rt-multi-thread",
] }
tracing = { version = "0.1", features = ["attributes"] }
uuid = { version = "1", features = ["serde"] }
validator = { version = "0.16", features = ["derive"] }
[dev-dependencies]
tracing-subscriber = "0.3"

View File

@ -0,0 +1,91 @@
# Rust API for petstore-with-fake-endpoints-models-for-testing
This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\
## Overview
This server was generated by the [openapi-generator]
(https://openapi-generator.tech) project. By using the
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
server, you can easily generate a server stub.
To see how to make this your own, look here: [README]((https://openapi-generator.tech))
- API version: 1.0.0
This autogenerated project defines an API crate `petstore-with-fake-endpoints-models-for-testing` which contains:
* An `Api` trait defining the API in Rust.
* Data types representing the underlying data model.
* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation.
* Request validations (path, query, body params) are included.
## Using the generated library
The generated library has a few optional features that can be activated through Cargo.
* `server`
* This defaults to enabled and creates the basic skeleton of a server implementation based on Axum.
* To create the server stack you'll need to provide an implementation of the API trait to provide the server function.
* `conversions`
* This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types.
See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`.
### Example
```rust
struct ServerImpl {
// database: sea_orm::DbConn,
}
#[allow(unused_variables)]
#[async_trait]
impl petstore-with-fake-endpoints-models-for-testing::Api for ServerImpl {
// API implementation goes here
}
pub async fn start_server(addr: &str) {
// initialize tracing
tracing_subscriber::fmt::init();
// Init Axum router
let app = petstore-with-fake-endpoints-models-for-testing::server::new(Arc::new(ServerImpl));
// Add layers to the router
let app = app.layer(...);
// Run the server with graceful shutdown
let listener = TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await
.unwrap();
}
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
}
```

View File

@ -0,0 +1,197 @@
use std::{convert::TryFrom, fmt, ops::Deref};
use chrono::{DateTime, Utc};
use http::HeaderValue;
/// A struct to allow homogeneous conversion into a HeaderValue. We can't
/// implement the From/Into trait on HeaderValue because we don't own
/// either of the types.
#[derive(Debug, Clone)]
pub(crate) struct IntoHeaderValue<T>(pub T);
// Generic implementations
impl<T> Deref for IntoHeaderValue<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
// Derive for each TryFrom<T> in http::HeaderValue
macro_rules! ihv_generate {
($t:ident) => {
impl TryFrom<HeaderValue> for IntoHeaderValue<$t> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match hdr_value.parse::<$t>() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)),
Err(e) => Err(format!(
"Unable to parse {} as a string: {}",
stringify!($t),
e
)),
},
Err(e) => Err(format!(
"Unable to parse header {:?} as a string - {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<$t>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result<Self, Self::Error> {
Ok(hdr_value.0.into())
}
}
};
}
ihv_generate!(u64);
ihv_generate!(i64);
ihv_generate!(i16);
ihv_generate!(u16);
ihv_generate!(u32);
ihv_generate!(usize);
ihv_generate!(isize);
ihv_generate!(i32);
// Custom derivations
// Vec<String>
impl TryFrom<HeaderValue> for IntoHeaderValue<Vec<String>> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => Ok(IntoHeaderValue(
hdr_value
.split(',')
.filter_map(|x| match x.trim() {
"" => None,
y => Some(y.to_string()),
})
.collect(),
)),
Err(e) => Err(format!(
"Unable to parse header: {:?} as a string - {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<Vec<String>>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<Vec<String>>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0.join(", ")) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} into a header - {}",
hdr_value, e
)),
}
}
}
// String
impl TryFrom<HeaderValue> for IntoHeaderValue<String> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())),
Err(e) => Err(format!("Unable to convert header {:?} to {}", hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<String>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<String>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} from a header {}",
hdr_value, e
)),
}
}
}
// Bool
impl TryFrom<HeaderValue> for IntoHeaderValue<bool> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match hdr_value.parse() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)),
Err(e) => Err(format!("Unable to parse bool from {} - {}", hdr_value, e)),
},
Err(e) => Err(format!(
"Unable to convert {:?} from a header {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<bool>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<bool>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0.to_string()) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert: {:?} into a header: {}",
hdr_value, e
)),
}
}
}
// DateTime
impl TryFrom<HeaderValue> for IntoHeaderValue<DateTime<Utc>> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) {
Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))),
Err(e) => Err(format!("Unable to parse: {} as date - {}", hdr_value, e)),
},
Err(e) => Err(format!(
"Unable to convert header {:?} to string {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<DateTime<Utc>>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<DateTime<Utc>>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} to a header: {}",
hdr_value, e
)),
}
}
}

View File

@ -0,0 +1,679 @@
#![allow(
missing_docs,
trivial_casts,
unused_variables,
unused_mut,
unused_imports,
unused_extern_crates,
non_camel_case_types
)]
#![allow(unused_imports, unused_attributes)]
#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)]
use async_trait::async_trait;
use axum::extract::*;
use axum_extra::extract::{CookieJar, Multipart};
use bytes::Bytes;
use http::Method;
use serde::{Deserialize, Serialize};
use types::*;
pub const BASE_PATH: &str = "/v2";
pub const API_VERSION: &str = "1.0.0";
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum TestSpecialTagsResponse {
/// successful operation
SuccessfulOperation(models::Client),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Call123exampleResponse {
/// success
Success,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum FakeOuterBooleanSerializeResponse {
/// Output boolean
OutputBoolean(bool),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum FakeOuterCompositeSerializeResponse {
/// Output composite
OutputComposite(models::OuterComposite),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum FakeOuterNumberSerializeResponse {
/// Output number
OutputNumber(f64),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum FakeOuterStringSerializeResponse {
/// Output string
OutputString(String),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum FakeResponseWithNumericalDescriptionResponse {
/// 1234
Status200,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum HyphenParamResponse {
/// Success
Success,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum TestBodyWithQueryParamsResponse {
/// Success
Success,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum TestClientModelResponse {
/// successful operation
SuccessfulOperation(models::Client),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum TestEndpointParametersResponse {
/// Invalid username supplied
InvalidUsernameSupplied,
/// User not found
UserNotFound,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum TestEnumParametersResponse {
/// Invalid request
InvalidRequest,
/// Not found
NotFound,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum TestInlineAdditionalPropertiesResponse {
/// successful operation
SuccessfulOperation,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum TestJsonFormDataResponse {
/// successful operation
SuccessfulOperation,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum TestClassnameResponse {
/// successful operation
SuccessfulOperation(models::Client),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum AddPetResponse {
/// Invalid input
InvalidInput,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum DeletePetResponse {
/// Invalid pet value
InvalidPetValue,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum FindPetsByStatusResponse {
/// successful operation
SuccessfulOperation(String),
/// Invalid status value
InvalidStatusValue,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum FindPetsByTagsResponse {
/// successful operation
SuccessfulOperation(String),
/// Invalid tag value
InvalidTagValue,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum GetPetByIdResponse {
/// successful operation
SuccessfulOperation(String),
/// Invalid ID supplied
InvalidIDSupplied,
/// Pet not found
PetNotFound,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum UpdatePetResponse {
/// Invalid ID supplied
InvalidIDSupplied,
/// Pet not found
PetNotFound,
/// Validation exception
ValidationException,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum UpdatePetWithFormResponse {
/// Invalid input
InvalidInput,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum UploadFileResponse {
/// successful operation
SuccessfulOperation(models::ApiResponse),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum DeleteOrderResponse {
/// Invalid ID supplied
InvalidIDSupplied,
/// Order not found
OrderNotFound,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum GetInventoryResponse {
/// successful operation
SuccessfulOperation(std::collections::HashMap<String, i32>),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum GetOrderByIdResponse {
/// successful operation
SuccessfulOperation(String),
/// Invalid ID supplied
InvalidIDSupplied,
/// Order not found
OrderNotFound,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum PlaceOrderResponse {
/// successful operation
SuccessfulOperation(String),
/// Invalid Order
InvalidOrder,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum CreateUserResponse {
/// successful operation
SuccessfulOperation,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum CreateUsersWithArrayInputResponse {
/// successful operation
SuccessfulOperation,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum CreateUsersWithListInputResponse {
/// successful operation
SuccessfulOperation,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum DeleteUserResponse {
/// Invalid username supplied
InvalidUsernameSupplied,
/// User not found
UserNotFound,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum GetUserByNameResponse {
/// successful operation
SuccessfulOperation(String),
/// Invalid username supplied
InvalidUsernameSupplied,
/// User not found
UserNotFound,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum LoginUserResponse {
/// successful operation
SuccessfulOperation {
body: String,
x_rate_limit: Option<i32>,
x_expires_after: Option<chrono::DateTime<chrono::Utc>>,
},
/// Invalid username/password supplied
InvalidUsername,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum LogoutUserResponse {
/// successful operation
SuccessfulOperation,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum UpdateUserResponse {
/// Invalid user supplied
InvalidUserSupplied,
/// User not found
UserNotFound,
}
/// API
#[async_trait]
#[allow(clippy::ptr_arg)]
pub trait Api {
/// To test special tags.
///
/// TestSpecialTags - PATCH /v2/another-fake/dummy
async fn test_special_tags(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: models::Client,
) -> Result<TestSpecialTagsResponse, String>;
/// Call123example - GET /v2/fake/operation-with-numeric-id
async fn call123example(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<Call123exampleResponse, String>;
/// FakeOuterBooleanSerialize - POST /v2/fake/outer/boolean
async fn fake_outer_boolean_serialize(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Option<models::OuterBoolean>,
) -> Result<FakeOuterBooleanSerializeResponse, String>;
/// FakeOuterCompositeSerialize - POST /v2/fake/outer/composite
async fn fake_outer_composite_serialize(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Option<models::OuterComposite>,
) -> Result<FakeOuterCompositeSerializeResponse, String>;
/// FakeOuterNumberSerialize - POST /v2/fake/outer/number
async fn fake_outer_number_serialize(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Option<models::OuterNumber>,
) -> Result<FakeOuterNumberSerializeResponse, String>;
/// FakeOuterStringSerialize - POST /v2/fake/outer/string
async fn fake_outer_string_serialize(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Option<models::OuterString>,
) -> Result<FakeOuterStringSerializeResponse, String>;
/// FakeResponseWithNumericalDescription - GET /v2/fake/response-with-numerical-description
async fn fake_response_with_numerical_description(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<FakeResponseWithNumericalDescriptionResponse, String>;
/// HyphenParam - GET /v2/fake/hyphenParam/{hyphen-param}
async fn hyphen_param(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::HyphenParamPathParams,
) -> Result<HyphenParamResponse, String>;
/// TestBodyWithQueryParams - PUT /v2/fake/body-with-query-params
async fn test_body_with_query_params(
&self,
method: Method,
host: Host,
cookies: CookieJar,
query_params: models::TestBodyWithQueryParamsQueryParams,
body: models::User,
) -> Result<TestBodyWithQueryParamsResponse, String>;
/// To test \"client\" model.
///
/// TestClientModel - PATCH /v2/fake
async fn test_client_model(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: models::Client,
) -> Result<TestClientModelResponse, String>;
/// Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트.
///
/// TestEndpointParameters - POST /v2/fake
async fn test_endpoint_parameters(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<TestEndpointParametersResponse, String>;
/// To test enum parameters.
///
/// TestEnumParameters - GET /v2/fake
async fn test_enum_parameters(
&self,
method: Method,
host: Host,
cookies: CookieJar,
header_params: models::TestEnumParametersHeaderParams,
query_params: models::TestEnumParametersQueryParams,
) -> Result<TestEnumParametersResponse, String>;
/// test inline additionalProperties.
///
/// TestInlineAdditionalProperties - POST /v2/fake/inline-additionalProperties
async fn test_inline_additional_properties(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: std::collections::HashMap<String, String>,
) -> Result<TestInlineAdditionalPropertiesResponse, String>;
/// test json serialization of form data.
///
/// TestJsonFormData - GET /v2/fake/jsonFormData
async fn test_json_form_data(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<TestJsonFormDataResponse, String>;
/// To test class name in snake case.
///
/// TestClassname - PATCH /v2/fake_classname_test
async fn test_classname(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: models::Client,
) -> Result<TestClassnameResponse, String>;
/// Add a new pet to the store.
///
/// AddPet - POST /v2/pet
async fn add_pet(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: models::Pet,
) -> Result<AddPetResponse, String>;
/// Deletes a pet.
///
/// DeletePet - DELETE /v2/pet/{petId}
async fn delete_pet(
&self,
method: Method,
host: Host,
cookies: CookieJar,
header_params: models::DeletePetHeaderParams,
path_params: models::DeletePetPathParams,
) -> Result<DeletePetResponse, String>;
/// Finds Pets by status.
///
/// FindPetsByStatus - GET /v2/pet/findByStatus
async fn find_pets_by_status(
&self,
method: Method,
host: Host,
cookies: CookieJar,
query_params: models::FindPetsByStatusQueryParams,
) -> Result<FindPetsByStatusResponse, String>;
/// Finds Pets by tags.
///
/// FindPetsByTags - GET /v2/pet/findByTags
async fn find_pets_by_tags(
&self,
method: Method,
host: Host,
cookies: CookieJar,
query_params: models::FindPetsByTagsQueryParams,
) -> Result<FindPetsByTagsResponse, String>;
/// Find pet by ID.
///
/// GetPetById - GET /v2/pet/{petId}
async fn get_pet_by_id(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::GetPetByIdPathParams,
) -> Result<GetPetByIdResponse, String>;
/// Update an existing pet.
///
/// UpdatePet - PUT /v2/pet
async fn update_pet(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: models::Pet,
) -> Result<UpdatePetResponse, String>;
/// Updates a pet in the store with form data.
///
/// UpdatePetWithForm - POST /v2/pet/{petId}
async fn update_pet_with_form(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::UpdatePetWithFormPathParams,
) -> Result<UpdatePetWithFormResponse, String>;
/// uploads an image.
///
/// UploadFile - POST /v2/pet/{petId}/uploadImage
async fn upload_file(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::UploadFilePathParams,
body: Multipart,
) -> Result<UploadFileResponse, String>;
/// Delete purchase order by ID.
///
/// DeleteOrder - DELETE /v2/store/order/{order_id}
async fn delete_order(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::DeleteOrderPathParams,
) -> Result<DeleteOrderResponse, String>;
/// Returns pet inventories by status.
///
/// GetInventory - GET /v2/store/inventory
async fn get_inventory(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<GetInventoryResponse, String>;
/// Find purchase order by ID.
///
/// GetOrderById - GET /v2/store/order/{order_id}
async fn get_order_by_id(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::GetOrderByIdPathParams,
) -> Result<GetOrderByIdResponse, String>;
/// Place an order for a pet.
///
/// PlaceOrder - POST /v2/store/order
async fn place_order(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: models::Order,
) -> Result<PlaceOrderResponse, String>;
/// Create user.
///
/// CreateUser - POST /v2/user
async fn create_user(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: models::User,
) -> Result<CreateUserResponse, String>;
/// Creates list of users with given input array.
///
/// CreateUsersWithArrayInput - POST /v2/user/createWithArray
async fn create_users_with_array_input(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Vec<models::User>,
) -> Result<CreateUsersWithArrayInputResponse, String>;
/// Creates list of users with given input array.
///
/// CreateUsersWithListInput - POST /v2/user/createWithList
async fn create_users_with_list_input(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Vec<models::User>,
) -> Result<CreateUsersWithListInputResponse, String>;
/// Delete user.
///
/// DeleteUser - DELETE /v2/user/{username}
async fn delete_user(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::DeleteUserPathParams,
) -> Result<DeleteUserResponse, String>;
/// Get user by user name.
///
/// GetUserByName - GET /v2/user/{username}
async fn get_user_by_name(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::GetUserByNamePathParams,
) -> Result<GetUserByNameResponse, String>;
/// Logs user into the system.
///
/// LoginUser - GET /v2/user/login
async fn login_user(
&self,
method: Method,
host: Host,
cookies: CookieJar,
query_params: models::LoginUserQueryParams,
) -> Result<LoginUserResponse, String>;
/// Logs out current logged in user session.
///
/// LogoutUser - GET /v2/user/logout
async fn logout_user(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<LogoutUserResponse, String>;
/// Updated user.
///
/// UpdateUser - PUT /v2/user/{username}
async fn update_user(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::UpdateUserPathParams,
body: models::User,
) -> Result<UpdateUserResponse, String>;
}
#[cfg(feature = "server")]
pub mod server;
pub mod models;
pub mod types;
#[cfg(feature = "server")]
pub(crate) mod header;

View File

@ -0,0 +1,665 @@
use std::{mem, str::FromStr};
use base64::{engine::general_purpose, Engine};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[allow(dead_code)]
pub struct Object(serde_json::Value);
impl validator::Validate for Object {
fn validate(&self) -> Result<(), validator::ValidationErrors> {
Ok(())
}
}
impl FromStr for Object {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(serde_json::Value::String(s.to_owned())))
}
}
/// Serde helper function to create a default `Option<Nullable<T>>` while
/// deserializing
pub fn default_optional_nullable<T>() -> Option<Nullable<T>> {
None
}
/// Serde helper function to deserialize into an `Option<Nullable<T>>`
pub fn deserialize_optional_nullable<'de, D, T>(
deserializer: D,
) -> Result<Option<Nullable<T>>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
Option::<T>::deserialize(deserializer).map(|val| match val {
Some(inner) => Some(Nullable::Present(inner)),
None => Some(Nullable::Null),
})
}
/// The Nullable type. Represents a value which may be specified as null on an API.
/// Note that this is distinct from a value that is optional and not present!
///
/// Nullable implements many of the same methods as the Option type (map, unwrap, etc).
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum Nullable<T> {
/// Null value
Null,
/// Value is present
Present(T),
}
impl<T> Nullable<T> {
/////////////////////////////////////////////////////////////////////////
// Querying the contained values
/////////////////////////////////////////////////////////////////////////
/// Returns `true` if the Nullable is a `Present` value.
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let x: Nullable<u32> = Nullable::Present(2);
/// assert_eq!(x.is_present(), true);
///
/// let x: Nullable<u32> = Nullable::Null;
/// assert_eq!(x.is_present(), false);
/// ```
#[inline]
pub fn is_present(&self) -> bool {
match *self {
Nullable::Present(_) => true,
Nullable::Null => false,
}
}
/// Returns `true` if the Nullable is a `Null` value.
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let x: Nullable<u32> = Nullable::Present(2);
/// assert_eq!(x.is_null(), false);
///
/// let x: Nullable<u32> = Nullable::Null;
/// assert_eq!(x.is_null(), true);
/// ```
#[inline]
pub fn is_null(&self) -> bool {
!self.is_present()
}
/////////////////////////////////////////////////////////////////////////
// Adapter for working with references
/////////////////////////////////////////////////////////////////////////
/// Converts from `Nullable<T>` to `Nullable<&T>`.
///
/// # Examples
///
/// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original.
/// The [`map`] method takes the `self` argument by value, consuming the original,
/// so this technique uses `as_ref` to first take a `Nullable` to a reference
/// to the value inside the original.
///
/// [`map`]: enum.Nullable.html#method.map
/// [`String`]: ../../std/string/struct.String.html
/// [`usize`]: ../../std/primitive.usize.html
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let num_as_str: Nullable<String> = Nullable::Present("10".to_string());
/// // First, cast `Nullable<String>` to `Nullable<&String>` with `as_ref`,
/// // then consume *that* with `map`, leaving `num_as_str` on the stack.
/// let num_as_int: Nullable<usize> = num_as_str.as_ref().map(|n| n.len());
/// println!("still can print num_as_str: {:?}", num_as_str);
/// ```
#[inline]
pub fn as_ref(&self) -> Nullable<&T> {
match *self {
Nullable::Present(ref x) => Nullable::Present(x),
Nullable::Null => Nullable::Null,
}
}
/// Converts from `Nullable<T>` to `Nullable<&mut T>`.
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let mut x = Nullable::Present(2);
/// match x.as_mut() {
/// Nullable::Present(v) => *v = 42,
/// Nullable::Null => {},
/// }
/// assert_eq!(x, Nullable::Present(42));
/// ```
#[inline]
pub fn as_mut(&mut self) -> Nullable<&mut T> {
match *self {
Nullable::Present(ref mut x) => Nullable::Present(x),
Nullable::Null => Nullable::Null,
}
}
/////////////////////////////////////////////////////////////////////////
// Getting to contained values
/////////////////////////////////////////////////////////////////////////
/// Unwraps a Nullable, yielding the content of a `Nullable::Present`.
///
/// # Panics
///
/// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by
/// `msg`.
///
/// [`Nullable::Null`]: #variant.Null
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let x = Nullable::Present("value");
/// assert_eq!(x.expect("the world is ending"), "value");
/// ```
///
/// ```{.should_panic}
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let x: Nullable<&str> = Nullable::Null;
/// x.expect("the world is ending"); // panics with `the world is ending`
/// ```
#[inline]
pub fn expect(self, msg: &str) -> T {
match self {
Nullable::Present(val) => val,
Nullable::Null => expect_failed(msg),
}
}
/// Moves the value `v` out of the `Nullable<T>` if it is `Nullable::Present(v)`.
///
/// In general, because this function may panic, its use is discouraged.
/// Instead, prefer to use pattern matching and handle the `Nullable::Null`
/// case explicitly.
///
/// # Panics
///
/// Panics if the self value equals [`Nullable::Null`].
///
/// [`Nullable::Null`]: #variant.Null
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let x = Nullable::Present("air");
/// assert_eq!(x.unwrap(), "air");
/// ```
///
/// ```{.should_panic}
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.unwrap(), "air"); // fails
/// ```
#[inline]
pub fn unwrap(self) -> T {
match self {
Nullable::Present(val) => val,
Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"),
}
}
/// Returns the contained value or a default.
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car");
/// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike");
/// ```
#[inline]
pub fn unwrap_or(self, def: T) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => def,
}
}
/// Returns the contained value or computes it from a closure.
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let k = 10;
/// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4);
/// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20);
/// ```
#[inline]
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => f(),
}
}
/////////////////////////////////////////////////////////////////////////
// Transforming contained values
/////////////////////////////////////////////////////////////////////////
/// Maps a `Nullable<T>` to `Nullable<U>` by applying a function to a contained value.
///
/// # Examples
///
/// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original:
///
/// [`String`]: ../../std/string/struct.String.html
/// [`usize`]: ../../std/primitive.usize.html
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let maybe_some_string = Nullable::Present(String::from("Hello, World!"));
/// // `Nullable::map` takes self *by value*, consuming `maybe_some_string`
/// let maybe_some_len = maybe_some_string.map(|s| s.len());
///
/// assert_eq!(maybe_some_len, Nullable::Present(13));
/// ```
#[inline]
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Nullable<U> {
match self {
Nullable::Present(x) => Nullable::Present(f(x)),
Nullable::Null => Nullable::Null,
}
}
/// Applies a function to the contained value (if any),
/// or returns a `default` (if not).
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.map_or(42, |v| v.len()), 3);
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.map_or(42, |v| v.len()), 42);
/// ```
#[inline]
pub fn map_or<U, F: FnOnce(T) -> U>(self, default: U, f: F) -> U {
match self {
Nullable::Present(t) => f(t),
Nullable::Null => default,
}
}
/// Applies a function to the contained value (if any),
/// or computes a `default` (if not).
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let k = 21;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3);
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42);
/// ```
#[inline]
pub fn map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
match self {
Nullable::Present(t) => f(t),
Nullable::Null => default(),
}
}
/// Transforms the `Nullable<T>` into a [`Result<T, E>`], mapping `Nullable::Present(v)` to
/// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err].
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [Err]: ../../std/result/enum.Result.html#variant.Err
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.ok_or(0), Ok("foo"));
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.ok_or(0), Err(0));
/// ```
#[inline]
pub fn ok_or<E>(self, err: E) -> Result<T, E> {
match self {
Nullable::Present(v) => Ok(v),
Nullable::Null => Err(err),
}
}
/// Transforms the `Nullable<T>` into a [`Result<T, E>`], mapping `Nullable::Present(v)` to
/// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err].
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [Err]: ../../std/result/enum.Result.html#variant.Err
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.ok_or_else(|| 0), Ok("foo"));
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.ok_or_else(|| 0), Err(0));
/// ```
#[inline]
pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
match self {
Nullable::Present(v) => Ok(v),
Nullable::Null => Err(err()),
}
}
/////////////////////////////////////////////////////////////////////////
// Boolean operations on the values, eager and lazy
/////////////////////////////////////////////////////////////////////////
/// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`.
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let x = Nullable::Present(2);
/// let y: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.and(y), Nullable::Null);
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y = Nullable::Present("foo");
/// assert_eq!(x.and(y), Nullable::Null);
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Present("foo");
/// assert_eq!(x.and(y), Nullable::Present("foo"));
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.and(y), Nullable::Null);
/// ```
#[inline]
pub fn and<U>(self, optb: Nullable<U>) -> Nullable<U> {
match self {
Nullable::Present(_) => optb,
Nullable::Null => Nullable::Null,
}
}
/// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the
/// wrapped value and returns the result.
///
/// Some languages call this operation flatmap.
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// fn sq(x: u32) -> Nullable<u32> { Nullable::Present(x * x) }
/// fn nope(_: u32) -> Nullable<u32> { Nullable::Null }
///
/// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16));
/// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null);
/// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null);
/// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null);
/// ```
#[inline]
pub fn and_then<U, F: FnOnce(T) -> Nullable<U>>(self, f: F) -> Nullable<U> {
match self {
Nullable::Present(x) => f(x),
Nullable::Null => Nullable::Null,
}
}
/// Returns the Nullable if it contains a value, otherwise returns `optb`.
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Null;
/// assert_eq!(x.or(y), Nullable::Present(2));
///
/// let x = Nullable::Null;
/// let y = Nullable::Present(100);
/// assert_eq!(x.or(y), Nullable::Present(100));
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Present(100);
/// assert_eq!(x.or(y), Nullable::Present(2));
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y = Nullable::Null;
/// assert_eq!(x.or(y), Nullable::Null);
/// ```
#[inline]
pub fn or(self, optb: Nullable<T>) -> Nullable<T> {
match self {
Nullable::Present(_) => self,
Nullable::Null => optb,
}
}
/// Returns the Nullable if it contains a value, otherwise calls `f` and
/// returns the result.
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// fn nobody() -> Nullable<&'static str> { Nullable::Null }
/// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") }
///
/// assert_eq!(Nullable::Present("barbarians").or_else(vikings),
/// Nullable::Present("barbarians"));
/// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings"));
/// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null);
/// ```
#[inline]
pub fn or_else<F: FnOnce() -> Nullable<T>>(self, f: F) -> Nullable<T> {
match self {
Nullable::Present(_) => self,
Nullable::Null => f(),
}
}
/////////////////////////////////////////////////////////////////////////
// Misc
/////////////////////////////////////////////////////////////////////////
/// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place.
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let mut x = Nullable::Present(2);
/// x.take();
/// assert_eq!(x, Nullable::Null);
///
/// let mut x: Nullable<u32> = Nullable::Null;
/// x.take();
/// assert_eq!(x, Nullable::Null);
/// ```
#[inline]
pub fn take(&mut self) -> Nullable<T> {
mem::replace(self, Nullable::Null)
}
}
impl<'a, T: Clone> Nullable<&'a T> {
/// Maps an `Nullable<&T>` to an `Nullable<T>` by cloning the contents of the
/// Nullable.
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let x = 12;
/// let opt_x = Nullable::Present(&x);
/// assert_eq!(opt_x, Nullable::Present(&12));
/// let cloned = opt_x.cloned();
/// assert_eq!(cloned, Nullable::Present(12));
/// ```
pub fn cloned(self) -> Nullable<T> {
self.map(Clone::clone)
}
}
impl<T: Default> Nullable<T> {
/// Returns the contained value or a default
///
/// Consumes the `self` argument then, if `Nullable::Present`, returns the contained
/// value, otherwise if `Nullable::Null`, returns the default value for that
/// type.
///
/// # Examples
///
/// ```
/// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable;
///
/// let x = Nullable::Present(42);
/// assert_eq!(42, x.unwrap_or_default());
///
/// let y: Nullable<i32> = Nullable::Null;
/// assert_eq!(0, y.unwrap_or_default());
/// ```
#[inline]
pub fn unwrap_or_default(self) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => Default::default(),
}
}
}
impl<T> Default for Nullable<T> {
/// Returns None.
#[inline]
fn default() -> Nullable<T> {
Nullable::Null
}
}
impl<T> From<T> for Nullable<T> {
fn from(val: T) -> Nullable<T> {
Nullable::Present(val)
}
}
impl<T> Serialize for Nullable<T>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
Nullable::Present(ref inner) => serializer.serialize_some(&inner),
Nullable::Null => serializer.serialize_none(),
}
}
}
impl<'de, T> Deserialize<'de> for Nullable<T>
where
T: serde::de::DeserializeOwned,
{
fn deserialize<D>(deserializer: D) -> Result<Nullable<T>, D::Error>
where
D: Deserializer<'de>,
{
// In order to deserialize a required, but nullable, value, we first have to check whether
// the value is present at all. To do this, we deserialize to a serde_json::Value, which
// fails if the value is missing, or gives serde_json::Value::Null if the value is present.
// If that succeeds as null, we can easily return a Null.
// If that succeeds as some value, we deserialize that value and return a Present.
// If that errors, we return the error.
let presence: Result<::serde_json::Value, _> =
serde::Deserialize::deserialize(deserializer);
match presence {
Ok(serde_json::Value::Null) => Ok(Nullable::Null),
Ok(some_value) => serde_json::from_value(some_value)
.map(Nullable::Present)
.map_err(serde::de::Error::custom),
Err(x) => Err(x),
}
}
}
#[inline(never)]
#[cold]
fn expect_failed(msg: &str) -> ! {
panic!("{}", msg)
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
/// Base64-encoded byte array
pub struct ByteArray(pub Vec<u8>);
impl Serialize for ByteArray {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0))
}
}
impl<'de> Deserialize<'de> for ByteArray {
fn deserialize<D>(deserializer: D) -> Result<ByteArray, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match general_purpose::STANDARD.decode(s) {
Ok(bin) => Ok(ByteArray(bin)),
_ => Err(serde::de::Error::custom("invalid base64")),
}
}
}

View File

@ -0,0 +1,2 @@
target
Cargo.lock

View File

@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@ -0,0 +1,8 @@
.gitignore
Cargo.toml
README.md
src/header.rs
src/lib.rs
src/models.rs
src/server/mod.rs
src/types.rs

View File

@ -0,0 +1 @@
7.3.0-SNAPSHOT

View File

@ -0,0 +1,47 @@
[package]
name = "petstore"
version = "1.0.0"
authors = ["OpenAPI Generator team and contributors"]
description = "This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters."
license = "Apache-2.0"
edition = "2021"
[features]
default = ["server"]
server = []
conversion = [
"frunk",
"frunk_derives",
"frunk_core",
"frunk-enum-core",
"frunk-enum-derive",
]
[dependencies]
async-trait = "0.1"
axum = { version = "0.7" }
axum-extra = { version = "0.9", features = ["cookie", "multipart"] }
base64 = "0.21"
bytes = "1"
chrono = { version = "0.4", features = ["serde"] }
frunk = { version = "0.4", optional = true }
frunk-enum-core = { version = "0.3", optional = true }
frunk-enum-derive = { version = "0.3", optional = true }
frunk_core = { version = "0.4", optional = true }
frunk_derives = { version = "0.4", optional = true }
http = "1"
lazy_static = "1"
regex = "1"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["raw_value"] }
serde_urlencoded = "0.7"
tokio = { version = "1", default-features = false, features = [
"signal",
"rt-multi-thread",
] }
tracing = { version = "0.1", features = ["attributes"] }
uuid = { version = "1", features = ["serde"] }
validator = { version = "0.16", features = ["derive"] }
[dev-dependencies]
tracing-subscriber = "0.3"

View File

@ -0,0 +1,91 @@
# Rust API for petstore
This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
## Overview
This server was generated by the [openapi-generator]
(https://openapi-generator.tech) project. By using the
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
server, you can easily generate a server stub.
To see how to make this your own, look here: [README]((https://openapi-generator.tech))
- API version: 1.0.0
This autogenerated project defines an API crate `petstore` which contains:
* An `Api` trait defining the API in Rust.
* Data types representing the underlying data model.
* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation.
* Request validations (path, query, body params) are included.
## Using the generated library
The generated library has a few optional features that can be activated through Cargo.
* `server`
* This defaults to enabled and creates the basic skeleton of a server implementation based on Axum.
* To create the server stack you'll need to provide an implementation of the API trait to provide the server function.
* `conversions`
* This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types.
See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`.
### Example
```rust
struct ServerImpl {
// database: sea_orm::DbConn,
}
#[allow(unused_variables)]
#[async_trait]
impl petstore::Api for ServerImpl {
// API implementation goes here
}
pub async fn start_server(addr: &str) {
// initialize tracing
tracing_subscriber::fmt::init();
// Init Axum router
let app = petstore::server::new(Arc::new(ServerImpl));
// Add layers to the router
let app = app.layer(...);
// Run the server with graceful shutdown
let listener = TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await
.unwrap();
}
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
}
```

View File

@ -0,0 +1,197 @@
use std::{convert::TryFrom, fmt, ops::Deref};
use chrono::{DateTime, Utc};
use http::HeaderValue;
/// A struct to allow homogeneous conversion into a HeaderValue. We can't
/// implement the From/Into trait on HeaderValue because we don't own
/// either of the types.
#[derive(Debug, Clone)]
pub(crate) struct IntoHeaderValue<T>(pub T);
// Generic implementations
impl<T> Deref for IntoHeaderValue<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
// Derive for each TryFrom<T> in http::HeaderValue
macro_rules! ihv_generate {
($t:ident) => {
impl TryFrom<HeaderValue> for IntoHeaderValue<$t> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match hdr_value.parse::<$t>() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)),
Err(e) => Err(format!(
"Unable to parse {} as a string: {}",
stringify!($t),
e
)),
},
Err(e) => Err(format!(
"Unable to parse header {:?} as a string - {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<$t>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result<Self, Self::Error> {
Ok(hdr_value.0.into())
}
}
};
}
ihv_generate!(u64);
ihv_generate!(i64);
ihv_generate!(i16);
ihv_generate!(u16);
ihv_generate!(u32);
ihv_generate!(usize);
ihv_generate!(isize);
ihv_generate!(i32);
// Custom derivations
// Vec<String>
impl TryFrom<HeaderValue> for IntoHeaderValue<Vec<String>> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => Ok(IntoHeaderValue(
hdr_value
.split(',')
.filter_map(|x| match x.trim() {
"" => None,
y => Some(y.to_string()),
})
.collect(),
)),
Err(e) => Err(format!(
"Unable to parse header: {:?} as a string - {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<Vec<String>>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<Vec<String>>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0.join(", ")) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} into a header - {}",
hdr_value, e
)),
}
}
}
// String
impl TryFrom<HeaderValue> for IntoHeaderValue<String> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())),
Err(e) => Err(format!("Unable to convert header {:?} to {}", hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<String>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<String>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} from a header {}",
hdr_value, e
)),
}
}
}
// Bool
impl TryFrom<HeaderValue> for IntoHeaderValue<bool> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match hdr_value.parse() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)),
Err(e) => Err(format!("Unable to parse bool from {} - {}", hdr_value, e)),
},
Err(e) => Err(format!(
"Unable to convert {:?} from a header {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<bool>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<bool>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0.to_string()) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert: {:?} into a header: {}",
hdr_value, e
)),
}
}
}
// DateTime
impl TryFrom<HeaderValue> for IntoHeaderValue<DateTime<Utc>> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) {
Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))),
Err(e) => Err(format!("Unable to parse: {} as date - {}", hdr_value, e)),
},
Err(e) => Err(format!(
"Unable to convert header {:?} to string {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<DateTime<Utc>>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<DateTime<Utc>>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} to a header: {}",
hdr_value, e
)),
}
}
}

View File

@ -0,0 +1,441 @@
#![allow(
missing_docs,
trivial_casts,
unused_variables,
unused_mut,
unused_imports,
unused_extern_crates,
non_camel_case_types
)]
#![allow(unused_imports, unused_attributes)]
#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)]
use async_trait::async_trait;
use axum::extract::*;
use axum_extra::extract::{CookieJar, Multipart};
use bytes::Bytes;
use http::Method;
use serde::{Deserialize, Serialize};
use types::*;
pub const BASE_PATH: &str = "/v2";
pub const API_VERSION: &str = "1.0.0";
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum AddPetResponse {
/// successful operation
SuccessfulOperation(String),
/// Invalid input
InvalidInput,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum DeletePetResponse {
/// Invalid pet value
InvalidPetValue,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum FindPetsByStatusResponse {
/// successful operation
SuccessfulOperation(String),
/// Invalid status value
InvalidStatusValue,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum FindPetsByTagsResponse {
/// successful operation
SuccessfulOperation(String),
/// Invalid tag value
InvalidTagValue,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum GetPetByIdResponse {
/// successful operation
SuccessfulOperation(String),
/// Invalid ID supplied
InvalidIDSupplied,
/// Pet not found
PetNotFound,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum UpdatePetResponse {
/// successful operation
SuccessfulOperation(String),
/// Invalid ID supplied
InvalidIDSupplied,
/// Pet not found
PetNotFound,
/// Validation exception
ValidationException,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum UpdatePetWithFormResponse {
/// Invalid input
InvalidInput,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum UploadFileResponse {
/// successful operation
SuccessfulOperation(models::ApiResponse),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum DeleteOrderResponse {
/// Invalid ID supplied
InvalidIDSupplied,
/// Order not found
OrderNotFound,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum GetInventoryResponse {
/// successful operation
SuccessfulOperation(std::collections::HashMap<String, i32>),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum GetOrderByIdResponse {
/// successful operation
SuccessfulOperation(String),
/// Invalid ID supplied
InvalidIDSupplied,
/// Order not found
OrderNotFound,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum PlaceOrderResponse {
/// successful operation
SuccessfulOperation(String),
/// Invalid Order
InvalidOrder,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum CreateUserResponse {
/// successful operation
SuccessfulOperation,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum CreateUsersWithArrayInputResponse {
/// successful operation
SuccessfulOperation,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum CreateUsersWithListInputResponse {
/// successful operation
SuccessfulOperation,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum DeleteUserResponse {
/// Invalid username supplied
InvalidUsernameSupplied,
/// User not found
UserNotFound,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum GetUserByNameResponse {
/// successful operation
SuccessfulOperation(String),
/// Invalid username supplied
InvalidUsernameSupplied,
/// User not found
UserNotFound,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum LoginUserResponse {
/// successful operation
SuccessfulOperation {
body: String,
set_cookie: Option<String>,
x_rate_limit: Option<i32>,
x_expires_after: Option<chrono::DateTime<chrono::Utc>>,
},
/// Invalid username/password supplied
InvalidUsername,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum LogoutUserResponse {
/// successful operation
SuccessfulOperation,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
#[allow(clippy::large_enum_variant)]
pub enum UpdateUserResponse {
/// Invalid user supplied
InvalidUserSupplied,
/// User not found
UserNotFound,
}
/// API
#[async_trait]
#[allow(clippy::ptr_arg)]
pub trait Api {
/// Add a new pet to the store.
///
/// AddPet - POST /v2/pet
async fn add_pet(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: models::Pet,
) -> Result<AddPetResponse, String>;
/// Deletes a pet.
///
/// DeletePet - DELETE /v2/pet/{petId}
async fn delete_pet(
&self,
method: Method,
host: Host,
cookies: CookieJar,
header_params: models::DeletePetHeaderParams,
path_params: models::DeletePetPathParams,
) -> Result<DeletePetResponse, String>;
/// Finds Pets by status.
///
/// FindPetsByStatus - GET /v2/pet/findByStatus
async fn find_pets_by_status(
&self,
method: Method,
host: Host,
cookies: CookieJar,
query_params: models::FindPetsByStatusQueryParams,
) -> Result<FindPetsByStatusResponse, String>;
/// Finds Pets by tags.
///
/// FindPetsByTags - GET /v2/pet/findByTags
async fn find_pets_by_tags(
&self,
method: Method,
host: Host,
cookies: CookieJar,
query_params: models::FindPetsByTagsQueryParams,
) -> Result<FindPetsByTagsResponse, String>;
/// Find pet by ID.
///
/// GetPetById - GET /v2/pet/{petId}
async fn get_pet_by_id(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::GetPetByIdPathParams,
) -> Result<GetPetByIdResponse, String>;
/// Update an existing pet.
///
/// UpdatePet - PUT /v2/pet
async fn update_pet(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: models::Pet,
) -> Result<UpdatePetResponse, String>;
/// Updates a pet in the store with form data.
///
/// UpdatePetWithForm - POST /v2/pet/{petId}
async fn update_pet_with_form(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::UpdatePetWithFormPathParams,
) -> Result<UpdatePetWithFormResponse, String>;
/// uploads an image.
///
/// UploadFile - POST /v2/pet/{petId}/uploadImage
async fn upload_file(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::UploadFilePathParams,
body: Multipart,
) -> Result<UploadFileResponse, String>;
/// Delete purchase order by ID.
///
/// DeleteOrder - DELETE /v2/store/order/{orderId}
async fn delete_order(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::DeleteOrderPathParams,
) -> Result<DeleteOrderResponse, String>;
/// Returns pet inventories by status.
///
/// GetInventory - GET /v2/store/inventory
async fn get_inventory(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<GetInventoryResponse, String>;
/// Find purchase order by ID.
///
/// GetOrderById - GET /v2/store/order/{orderId}
async fn get_order_by_id(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::GetOrderByIdPathParams,
) -> Result<GetOrderByIdResponse, String>;
/// Place an order for a pet.
///
/// PlaceOrder - POST /v2/store/order
async fn place_order(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: models::Order,
) -> Result<PlaceOrderResponse, String>;
/// Create user.
///
/// CreateUser - POST /v2/user
async fn create_user(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: models::User,
) -> Result<CreateUserResponse, String>;
/// Creates list of users with given input array.
///
/// CreateUsersWithArrayInput - POST /v2/user/createWithArray
async fn create_users_with_array_input(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Vec<models::User>,
) -> Result<CreateUsersWithArrayInputResponse, String>;
/// Creates list of users with given input array.
///
/// CreateUsersWithListInput - POST /v2/user/createWithList
async fn create_users_with_list_input(
&self,
method: Method,
host: Host,
cookies: CookieJar,
body: Vec<models::User>,
) -> Result<CreateUsersWithListInputResponse, String>;
/// Delete user.
///
/// DeleteUser - DELETE /v2/user/{username}
async fn delete_user(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::DeleteUserPathParams,
) -> Result<DeleteUserResponse, String>;
/// Get user by user name.
///
/// GetUserByName - GET /v2/user/{username}
async fn get_user_by_name(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::GetUserByNamePathParams,
) -> Result<GetUserByNameResponse, String>;
/// Logs user into the system.
///
/// LoginUser - GET /v2/user/login
async fn login_user(
&self,
method: Method,
host: Host,
cookies: CookieJar,
query_params: models::LoginUserQueryParams,
) -> Result<LoginUserResponse, String>;
/// Logs out current logged in user session.
///
/// LogoutUser - GET /v2/user/logout
async fn logout_user(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<LogoutUserResponse, String>;
/// Updated user.
///
/// UpdateUser - PUT /v2/user/{username}
async fn update_user(
&self,
method: Method,
host: Host,
cookies: CookieJar,
path_params: models::UpdateUserPathParams,
body: models::User,
) -> Result<UpdateUserResponse, String>;
}
#[cfg(feature = "server")]
pub mod server;
pub mod models;
pub mod types;
#[cfg(feature = "server")]
pub(crate) mod header;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,665 @@
use std::{mem, str::FromStr};
use base64::{engine::general_purpose, Engine};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[allow(dead_code)]
pub struct Object(serde_json::Value);
impl validator::Validate for Object {
fn validate(&self) -> Result<(), validator::ValidationErrors> {
Ok(())
}
}
impl FromStr for Object {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(serde_json::Value::String(s.to_owned())))
}
}
/// Serde helper function to create a default `Option<Nullable<T>>` while
/// deserializing
pub fn default_optional_nullable<T>() -> Option<Nullable<T>> {
None
}
/// Serde helper function to deserialize into an `Option<Nullable<T>>`
pub fn deserialize_optional_nullable<'de, D, T>(
deserializer: D,
) -> Result<Option<Nullable<T>>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
Option::<T>::deserialize(deserializer).map(|val| match val {
Some(inner) => Some(Nullable::Present(inner)),
None => Some(Nullable::Null),
})
}
/// The Nullable type. Represents a value which may be specified as null on an API.
/// Note that this is distinct from a value that is optional and not present!
///
/// Nullable implements many of the same methods as the Option type (map, unwrap, etc).
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum Nullable<T> {
/// Null value
Null,
/// Value is present
Present(T),
}
impl<T> Nullable<T> {
/////////////////////////////////////////////////////////////////////////
// Querying the contained values
/////////////////////////////////////////////////////////////////////////
/// Returns `true` if the Nullable is a `Present` value.
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let x: Nullable<u32> = Nullable::Present(2);
/// assert_eq!(x.is_present(), true);
///
/// let x: Nullable<u32> = Nullable::Null;
/// assert_eq!(x.is_present(), false);
/// ```
#[inline]
pub fn is_present(&self) -> bool {
match *self {
Nullable::Present(_) => true,
Nullable::Null => false,
}
}
/// Returns `true` if the Nullable is a `Null` value.
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let x: Nullable<u32> = Nullable::Present(2);
/// assert_eq!(x.is_null(), false);
///
/// let x: Nullable<u32> = Nullable::Null;
/// assert_eq!(x.is_null(), true);
/// ```
#[inline]
pub fn is_null(&self) -> bool {
!self.is_present()
}
/////////////////////////////////////////////////////////////////////////
// Adapter for working with references
/////////////////////////////////////////////////////////////////////////
/// Converts from `Nullable<T>` to `Nullable<&T>`.
///
/// # Examples
///
/// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original.
/// The [`map`] method takes the `self` argument by value, consuming the original,
/// so this technique uses `as_ref` to first take a `Nullable` to a reference
/// to the value inside the original.
///
/// [`map`]: enum.Nullable.html#method.map
/// [`String`]: ../../std/string/struct.String.html
/// [`usize`]: ../../std/primitive.usize.html
///
/// ```
/// # use petstore::types::Nullable;
///
/// let num_as_str: Nullable<String> = Nullable::Present("10".to_string());
/// // First, cast `Nullable<String>` to `Nullable<&String>` with `as_ref`,
/// // then consume *that* with `map`, leaving `num_as_str` on the stack.
/// let num_as_int: Nullable<usize> = num_as_str.as_ref().map(|n| n.len());
/// println!("still can print num_as_str: {:?}", num_as_str);
/// ```
#[inline]
pub fn as_ref(&self) -> Nullable<&T> {
match *self {
Nullable::Present(ref x) => Nullable::Present(x),
Nullable::Null => Nullable::Null,
}
}
/// Converts from `Nullable<T>` to `Nullable<&mut T>`.
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let mut x = Nullable::Present(2);
/// match x.as_mut() {
/// Nullable::Present(v) => *v = 42,
/// Nullable::Null => {},
/// }
/// assert_eq!(x, Nullable::Present(42));
/// ```
#[inline]
pub fn as_mut(&mut self) -> Nullable<&mut T> {
match *self {
Nullable::Present(ref mut x) => Nullable::Present(x),
Nullable::Null => Nullable::Null,
}
}
/////////////////////////////////////////////////////////////////////////
// Getting to contained values
/////////////////////////////////////////////////////////////////////////
/// Unwraps a Nullable, yielding the content of a `Nullable::Present`.
///
/// # Panics
///
/// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by
/// `msg`.
///
/// [`Nullable::Null`]: #variant.Null
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let x = Nullable::Present("value");
/// assert_eq!(x.expect("the world is ending"), "value");
/// ```
///
/// ```{.should_panic}
/// # use petstore::types::Nullable;
///
/// let x: Nullable<&str> = Nullable::Null;
/// x.expect("the world is ending"); // panics with `the world is ending`
/// ```
#[inline]
pub fn expect(self, msg: &str) -> T {
match self {
Nullable::Present(val) => val,
Nullable::Null => expect_failed(msg),
}
}
/// Moves the value `v` out of the `Nullable<T>` if it is `Nullable::Present(v)`.
///
/// In general, because this function may panic, its use is discouraged.
/// Instead, prefer to use pattern matching and handle the `Nullable::Null`
/// case explicitly.
///
/// # Panics
///
/// Panics if the self value equals [`Nullable::Null`].
///
/// [`Nullable::Null`]: #variant.Null
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let x = Nullable::Present("air");
/// assert_eq!(x.unwrap(), "air");
/// ```
///
/// ```{.should_panic}
/// # use petstore::types::Nullable;
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.unwrap(), "air"); // fails
/// ```
#[inline]
pub fn unwrap(self) -> T {
match self {
Nullable::Present(val) => val,
Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"),
}
}
/// Returns the contained value or a default.
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car");
/// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike");
/// ```
#[inline]
pub fn unwrap_or(self, def: T) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => def,
}
}
/// Returns the contained value or computes it from a closure.
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let k = 10;
/// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4);
/// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20);
/// ```
#[inline]
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => f(),
}
}
/////////////////////////////////////////////////////////////////////////
// Transforming contained values
/////////////////////////////////////////////////////////////////////////
/// Maps a `Nullable<T>` to `Nullable<U>` by applying a function to a contained value.
///
/// # Examples
///
/// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original:
///
/// [`String`]: ../../std/string/struct.String.html
/// [`usize`]: ../../std/primitive.usize.html
///
/// ```
/// # use petstore::types::Nullable;
///
/// let maybe_some_string = Nullable::Present(String::from("Hello, World!"));
/// // `Nullable::map` takes self *by value*, consuming `maybe_some_string`
/// let maybe_some_len = maybe_some_string.map(|s| s.len());
///
/// assert_eq!(maybe_some_len, Nullable::Present(13));
/// ```
#[inline]
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Nullable<U> {
match self {
Nullable::Present(x) => Nullable::Present(f(x)),
Nullable::Null => Nullable::Null,
}
}
/// Applies a function to the contained value (if any),
/// or returns a `default` (if not).
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.map_or(42, |v| v.len()), 3);
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.map_or(42, |v| v.len()), 42);
/// ```
#[inline]
pub fn map_or<U, F: FnOnce(T) -> U>(self, default: U, f: F) -> U {
match self {
Nullable::Present(t) => f(t),
Nullable::Null => default,
}
}
/// Applies a function to the contained value (if any),
/// or computes a `default` (if not).
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let k = 21;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3);
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42);
/// ```
#[inline]
pub fn map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
match self {
Nullable::Present(t) => f(t),
Nullable::Null => default(),
}
}
/// Transforms the `Nullable<T>` into a [`Result<T, E>`], mapping `Nullable::Present(v)` to
/// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err].
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [Err]: ../../std/result/enum.Result.html#variant.Err
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.ok_or(0), Ok("foo"));
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.ok_or(0), Err(0));
/// ```
#[inline]
pub fn ok_or<E>(self, err: E) -> Result<T, E> {
match self {
Nullable::Present(v) => Ok(v),
Nullable::Null => Err(err),
}
}
/// Transforms the `Nullable<T>` into a [`Result<T, E>`], mapping `Nullable::Present(v)` to
/// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err].
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [Err]: ../../std/result/enum.Result.html#variant.Err
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.ok_or_else(|| 0), Ok("foo"));
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.ok_or_else(|| 0), Err(0));
/// ```
#[inline]
pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
match self {
Nullable::Present(v) => Ok(v),
Nullable::Null => Err(err()),
}
}
/////////////////////////////////////////////////////////////////////////
// Boolean operations on the values, eager and lazy
/////////////////////////////////////////////////////////////////////////
/// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`.
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let x = Nullable::Present(2);
/// let y: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.and(y), Nullable::Null);
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y = Nullable::Present("foo");
/// assert_eq!(x.and(y), Nullable::Null);
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Present("foo");
/// assert_eq!(x.and(y), Nullable::Present("foo"));
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.and(y), Nullable::Null);
/// ```
#[inline]
pub fn and<U>(self, optb: Nullable<U>) -> Nullable<U> {
match self {
Nullable::Present(_) => optb,
Nullable::Null => Nullable::Null,
}
}
/// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the
/// wrapped value and returns the result.
///
/// Some languages call this operation flatmap.
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// fn sq(x: u32) -> Nullable<u32> { Nullable::Present(x * x) }
/// fn nope(_: u32) -> Nullable<u32> { Nullable::Null }
///
/// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16));
/// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null);
/// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null);
/// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null);
/// ```
#[inline]
pub fn and_then<U, F: FnOnce(T) -> Nullable<U>>(self, f: F) -> Nullable<U> {
match self {
Nullable::Present(x) => f(x),
Nullable::Null => Nullable::Null,
}
}
/// Returns the Nullable if it contains a value, otherwise returns `optb`.
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Null;
/// assert_eq!(x.or(y), Nullable::Present(2));
///
/// let x = Nullable::Null;
/// let y = Nullable::Present(100);
/// assert_eq!(x.or(y), Nullable::Present(100));
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Present(100);
/// assert_eq!(x.or(y), Nullable::Present(2));
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y = Nullable::Null;
/// assert_eq!(x.or(y), Nullable::Null);
/// ```
#[inline]
pub fn or(self, optb: Nullable<T>) -> Nullable<T> {
match self {
Nullable::Present(_) => self,
Nullable::Null => optb,
}
}
/// Returns the Nullable if it contains a value, otherwise calls `f` and
/// returns the result.
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// fn nobody() -> Nullable<&'static str> { Nullable::Null }
/// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") }
///
/// assert_eq!(Nullable::Present("barbarians").or_else(vikings),
/// Nullable::Present("barbarians"));
/// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings"));
/// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null);
/// ```
#[inline]
pub fn or_else<F: FnOnce() -> Nullable<T>>(self, f: F) -> Nullable<T> {
match self {
Nullable::Present(_) => self,
Nullable::Null => f(),
}
}
/////////////////////////////////////////////////////////////////////////
// Misc
/////////////////////////////////////////////////////////////////////////
/// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place.
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let mut x = Nullable::Present(2);
/// x.take();
/// assert_eq!(x, Nullable::Null);
///
/// let mut x: Nullable<u32> = Nullable::Null;
/// x.take();
/// assert_eq!(x, Nullable::Null);
/// ```
#[inline]
pub fn take(&mut self) -> Nullable<T> {
mem::replace(self, Nullable::Null)
}
}
impl<'a, T: Clone> Nullable<&'a T> {
/// Maps an `Nullable<&T>` to an `Nullable<T>` by cloning the contents of the
/// Nullable.
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let x = 12;
/// let opt_x = Nullable::Present(&x);
/// assert_eq!(opt_x, Nullable::Present(&12));
/// let cloned = opt_x.cloned();
/// assert_eq!(cloned, Nullable::Present(12));
/// ```
pub fn cloned(self) -> Nullable<T> {
self.map(Clone::clone)
}
}
impl<T: Default> Nullable<T> {
/// Returns the contained value or a default
///
/// Consumes the `self` argument then, if `Nullable::Present`, returns the contained
/// value, otherwise if `Nullable::Null`, returns the default value for that
/// type.
///
/// # Examples
///
/// ```
/// # use petstore::types::Nullable;
///
/// let x = Nullable::Present(42);
/// assert_eq!(42, x.unwrap_or_default());
///
/// let y: Nullable<i32> = Nullable::Null;
/// assert_eq!(0, y.unwrap_or_default());
/// ```
#[inline]
pub fn unwrap_or_default(self) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => Default::default(),
}
}
}
impl<T> Default for Nullable<T> {
/// Returns None.
#[inline]
fn default() -> Nullable<T> {
Nullable::Null
}
}
impl<T> From<T> for Nullable<T> {
fn from(val: T) -> Nullable<T> {
Nullable::Present(val)
}
}
impl<T> Serialize for Nullable<T>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
Nullable::Present(ref inner) => serializer.serialize_some(&inner),
Nullable::Null => serializer.serialize_none(),
}
}
}
impl<'de, T> Deserialize<'de> for Nullable<T>
where
T: serde::de::DeserializeOwned,
{
fn deserialize<D>(deserializer: D) -> Result<Nullable<T>, D::Error>
where
D: Deserializer<'de>,
{
// In order to deserialize a required, but nullable, value, we first have to check whether
// the value is present at all. To do this, we deserialize to a serde_json::Value, which
// fails if the value is missing, or gives serde_json::Value::Null if the value is present.
// If that succeeds as null, we can easily return a Null.
// If that succeeds as some value, we deserialize that value and return a Present.
// If that errors, we return the error.
let presence: Result<::serde_json::Value, _> =
serde::Deserialize::deserialize(deserializer);
match presence {
Ok(serde_json::Value::Null) => Ok(Nullable::Null),
Ok(some_value) => serde_json::from_value(some_value)
.map(Nullable::Present)
.map_err(serde::de::Error::custom),
Err(x) => Err(x),
}
}
}
#[inline(never)]
#[cold]
fn expect_failed(msg: &str) -> ! {
panic!("{}", msg)
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
/// Base64-encoded byte array
pub struct ByteArray(pub Vec<u8>);
impl Serialize for ByteArray {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0))
}
}
impl<'de> Deserialize<'de> for ByteArray {
fn deserialize<D>(deserializer: D) -> Result<ByteArray, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match general_purpose::STANDARD.decode(s) {
Ok(bin) => Ok(ByteArray(bin)),
_ => Err(serde::de::Error::custom("invalid base64")),
}
}
}

View File

@ -0,0 +1,2 @@
target
Cargo.lock

View File

@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@ -0,0 +1,8 @@
.gitignore
Cargo.toml
README.md
src/header.rs
src/lib.rs
src/models.rs
src/server/mod.rs
src/types.rs

View File

@ -0,0 +1,46 @@
[package]
name = "ping-bearer-auth"
version = "1.0.0"
authors = ["OpenAPI Generator team and contributors"]
description = "No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)"
edition = "2021"
[features]
default = ["server"]
server = []
conversion = [
"frunk",
"frunk_derives",
"frunk_core",
"frunk-enum-core",
"frunk-enum-derive",
]
[dependencies]
async-trait = "0.1"
axum = { version = "0.7" }
axum-extra = { version = "0.9", features = ["cookie", "multipart"] }
base64 = "0.21"
bytes = "1"
chrono = { version = "0.4", features = ["serde"] }
frunk = { version = "0.4", optional = true }
frunk-enum-core = { version = "0.3", optional = true }
frunk-enum-derive = { version = "0.3", optional = true }
frunk_core = { version = "0.4", optional = true }
frunk_derives = { version = "0.4", optional = true }
http = "1"
lazy_static = "1"
regex = "1"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["raw_value"] }
serde_urlencoded = "0.7"
tokio = { version = "1", default-features = false, features = [
"signal",
"rt-multi-thread",
] }
tracing = { version = "0.1", features = ["attributes"] }
uuid = { version = "1", features = ["serde"] }
validator = { version = "0.16", features = ["derive"] }
[dev-dependencies]
tracing-subscriber = "0.3"

View File

@ -0,0 +1,91 @@
# Rust API for ping-bearer-auth
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
## Overview
This server was generated by the [openapi-generator]
(https://openapi-generator.tech) project. By using the
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
server, you can easily generate a server stub.
To see how to make this your own, look here: [README]((https://openapi-generator.tech))
- API version: 1.0
This autogenerated project defines an API crate `ping-bearer-auth` which contains:
* An `Api` trait defining the API in Rust.
* Data types representing the underlying data model.
* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation.
* Request validations (path, query, body params) are included.
## Using the generated library
The generated library has a few optional features that can be activated through Cargo.
* `server`
* This defaults to enabled and creates the basic skeleton of a server implementation based on Axum.
* To create the server stack you'll need to provide an implementation of the API trait to provide the server function.
* `conversions`
* This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types.
See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`.
### Example
```rust
struct ServerImpl {
// database: sea_orm::DbConn,
}
#[allow(unused_variables)]
#[async_trait]
impl ping-bearer-auth::Api for ServerImpl {
// API implementation goes here
}
pub async fn start_server(addr: &str) {
// initialize tracing
tracing_subscriber::fmt::init();
// Init Axum router
let app = ping-bearer-auth::server::new(Arc::new(ServerImpl));
// Add layers to the router
let app = app.layer(...);
// Run the server with graceful shutdown
let listener = TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await
.unwrap();
}
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
}
```

View File

@ -0,0 +1,197 @@
use std::{convert::TryFrom, fmt, ops::Deref};
use chrono::{DateTime, Utc};
use http::HeaderValue;
/// A struct to allow homogeneous conversion into a HeaderValue. We can't
/// implement the From/Into trait on HeaderValue because we don't own
/// either of the types.
#[derive(Debug, Clone)]
pub(crate) struct IntoHeaderValue<T>(pub T);
// Generic implementations
impl<T> Deref for IntoHeaderValue<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
// Derive for each TryFrom<T> in http::HeaderValue
macro_rules! ihv_generate {
($t:ident) => {
impl TryFrom<HeaderValue> for IntoHeaderValue<$t> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match hdr_value.parse::<$t>() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)),
Err(e) => Err(format!(
"Unable to parse {} as a string: {}",
stringify!($t),
e
)),
},
Err(e) => Err(format!(
"Unable to parse header {:?} as a string - {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<$t>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result<Self, Self::Error> {
Ok(hdr_value.0.into())
}
}
};
}
ihv_generate!(u64);
ihv_generate!(i64);
ihv_generate!(i16);
ihv_generate!(u16);
ihv_generate!(u32);
ihv_generate!(usize);
ihv_generate!(isize);
ihv_generate!(i32);
// Custom derivations
// Vec<String>
impl TryFrom<HeaderValue> for IntoHeaderValue<Vec<String>> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => Ok(IntoHeaderValue(
hdr_value
.split(',')
.filter_map(|x| match x.trim() {
"" => None,
y => Some(y.to_string()),
})
.collect(),
)),
Err(e) => Err(format!(
"Unable to parse header: {:?} as a string - {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<Vec<String>>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<Vec<String>>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0.join(", ")) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} into a header - {}",
hdr_value, e
)),
}
}
}
// String
impl TryFrom<HeaderValue> for IntoHeaderValue<String> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())),
Err(e) => Err(format!("Unable to convert header {:?} to {}", hdr_value, e)),
}
}
}
impl TryFrom<IntoHeaderValue<String>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<String>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} from a header {}",
hdr_value, e
)),
}
}
}
// Bool
impl TryFrom<HeaderValue> for IntoHeaderValue<bool> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match hdr_value.parse() {
Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)),
Err(e) => Err(format!("Unable to parse bool from {} - {}", hdr_value, e)),
},
Err(e) => Err(format!(
"Unable to convert {:?} from a header {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<bool>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<bool>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(&hdr_value.0.to_string()) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert: {:?} into a header: {}",
hdr_value, e
)),
}
}
}
// DateTime
impl TryFrom<HeaderValue> for IntoHeaderValue<DateTime<Utc>> {
type Error = String;
fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
match hdr_value.to_str() {
Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) {
Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))),
Err(e) => Err(format!("Unable to parse: {} as date - {}", hdr_value, e)),
},
Err(e) => Err(format!(
"Unable to convert header {:?} to string {}",
hdr_value, e
)),
}
}
}
impl TryFrom<IntoHeaderValue<DateTime<Utc>>> for HeaderValue {
type Error = String;
fn try_from(hdr_value: IntoHeaderValue<DateTime<Utc>>) -> Result<Self, Self::Error> {
match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) {
Ok(hdr_value) => Ok(hdr_value),
Err(e) => Err(format!(
"Unable to convert {:?} to a header: {}",
hdr_value, e
)),
}
}
}

View File

@ -0,0 +1,51 @@
#![allow(
missing_docs,
trivial_casts,
unused_variables,
unused_mut,
unused_imports,
unused_extern_crates,
non_camel_case_types
)]
#![allow(unused_imports, unused_attributes)]
#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)]
use async_trait::async_trait;
use axum::extract::*;
use axum_extra::extract::{CookieJar, Multipart};
use bytes::Bytes;
use http::Method;
use serde::{Deserialize, Serialize};
use types::*;
pub const BASE_PATH: &str = "";
pub const API_VERSION: &str = "1.0";
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum PingGetResponse {
/// OK
OK,
}
/// API
#[async_trait]
#[allow(clippy::ptr_arg)]
pub trait Api {
/// PingGet - GET /ping
async fn ping_get(
&self,
method: Method,
host: Host,
cookies: CookieJar,
) -> Result<PingGetResponse, String>;
}
#[cfg(feature = "server")]
pub mod server;
pub mod models;
pub mod types;
#[cfg(feature = "server")]
pub(crate) mod header;

View File

@ -0,0 +1,8 @@
#![allow(unused_qualifications)]
use http::HeaderValue;
use validator::Validate;
#[cfg(feature = "server")]
use crate::header;
use crate::{models, types::*};

View File

@ -0,0 +1,80 @@
use std::collections::HashMap;
use axum::{body::Body, extract::*, response::Response, routing::*};
use axum_extra::extract::{CookieJar, Multipart};
use bytes::Bytes;
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
use tracing::error;
use validator::{Validate, ValidationErrors};
use crate::{header, types::*};
#[allow(unused_imports)]
use crate::models;
use crate::{Api, PingGetResponse};
/// Setup API Server.
pub fn new<I, A>(api_impl: I) -> Router
where
I: AsRef<A> + Clone + Send + Sync + 'static,
A: Api + 'static,
{
// build our application with a route
Router::new()
.route("/ping", get(ping_get::<I, A>))
.with_state(api_impl)
}
#[tracing::instrument(skip_all)]
fn ping_get_validation() -> std::result::Result<(), ValidationErrors> {
Ok(())
}
/// PingGet - GET /ping
#[tracing::instrument(skip_all)]
async fn ping_get<I, A>(
method: Method,
host: Host,
cookies: CookieJar,
State(api_impl): State<I>,
) -> Result<Response, StatusCode>
where
I: AsRef<A> + Send + Sync,
A: Api,
{
#[allow(clippy::redundant_closure)]
let validation = tokio::task::spawn_blocking(move || ping_get_validation())
.await
.unwrap();
let Ok(()) = validation else {
return Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(validation.unwrap_err().to_string()))
.map_err(|_| StatusCode::BAD_REQUEST);
};
let result = api_impl.as_ref().ping_get(method, host, cookies).await;
let mut response = Response::builder();
let resp = match result {
Ok(rsp) => match rsp {
PingGetResponse::OK => {
let mut response = response.status(201);
response.body(Body::empty())
}
},
Err(_) => {
// Application code returned an error. This should not happen, as the implementation should
// return a valid response.
response.status(500).body(Body::empty())
}
};
resp.map_err(|e| {
error!(error = ?e);
StatusCode::INTERNAL_SERVER_ERROR
})
}

View File

@ -0,0 +1,665 @@
use std::{mem, str::FromStr};
use base64::{engine::general_purpose, Engine};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[allow(dead_code)]
pub struct Object(serde_json::Value);
impl validator::Validate for Object {
fn validate(&self) -> Result<(), validator::ValidationErrors> {
Ok(())
}
}
impl FromStr for Object {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(serde_json::Value::String(s.to_owned())))
}
}
/// Serde helper function to create a default `Option<Nullable<T>>` while
/// deserializing
pub fn default_optional_nullable<T>() -> Option<Nullable<T>> {
None
}
/// Serde helper function to deserialize into an `Option<Nullable<T>>`
pub fn deserialize_optional_nullable<'de, D, T>(
deserializer: D,
) -> Result<Option<Nullable<T>>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
Option::<T>::deserialize(deserializer).map(|val| match val {
Some(inner) => Some(Nullable::Present(inner)),
None => Some(Nullable::Null),
})
}
/// The Nullable type. Represents a value which may be specified as null on an API.
/// Note that this is distinct from a value that is optional and not present!
///
/// Nullable implements many of the same methods as the Option type (map, unwrap, etc).
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum Nullable<T> {
/// Null value
Null,
/// Value is present
Present(T),
}
impl<T> Nullable<T> {
/////////////////////////////////////////////////////////////////////////
// Querying the contained values
/////////////////////////////////////////////////////////////////////////
/// Returns `true` if the Nullable is a `Present` value.
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let x: Nullable<u32> = Nullable::Present(2);
/// assert_eq!(x.is_present(), true);
///
/// let x: Nullable<u32> = Nullable::Null;
/// assert_eq!(x.is_present(), false);
/// ```
#[inline]
pub fn is_present(&self) -> bool {
match *self {
Nullable::Present(_) => true,
Nullable::Null => false,
}
}
/// Returns `true` if the Nullable is a `Null` value.
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let x: Nullable<u32> = Nullable::Present(2);
/// assert_eq!(x.is_null(), false);
///
/// let x: Nullable<u32> = Nullable::Null;
/// assert_eq!(x.is_null(), true);
/// ```
#[inline]
pub fn is_null(&self) -> bool {
!self.is_present()
}
/////////////////////////////////////////////////////////////////////////
// Adapter for working with references
/////////////////////////////////////////////////////////////////////////
/// Converts from `Nullable<T>` to `Nullable<&T>`.
///
/// # Examples
///
/// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original.
/// The [`map`] method takes the `self` argument by value, consuming the original,
/// so this technique uses `as_ref` to first take a `Nullable` to a reference
/// to the value inside the original.
///
/// [`map`]: enum.Nullable.html#method.map
/// [`String`]: ../../std/string/struct.String.html
/// [`usize`]: ../../std/primitive.usize.html
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let num_as_str: Nullable<String> = Nullable::Present("10".to_string());
/// // First, cast `Nullable<String>` to `Nullable<&String>` with `as_ref`,
/// // then consume *that* with `map`, leaving `num_as_str` on the stack.
/// let num_as_int: Nullable<usize> = num_as_str.as_ref().map(|n| n.len());
/// println!("still can print num_as_str: {:?}", num_as_str);
/// ```
#[inline]
pub fn as_ref(&self) -> Nullable<&T> {
match *self {
Nullable::Present(ref x) => Nullable::Present(x),
Nullable::Null => Nullable::Null,
}
}
/// Converts from `Nullable<T>` to `Nullable<&mut T>`.
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let mut x = Nullable::Present(2);
/// match x.as_mut() {
/// Nullable::Present(v) => *v = 42,
/// Nullable::Null => {},
/// }
/// assert_eq!(x, Nullable::Present(42));
/// ```
#[inline]
pub fn as_mut(&mut self) -> Nullable<&mut T> {
match *self {
Nullable::Present(ref mut x) => Nullable::Present(x),
Nullable::Null => Nullable::Null,
}
}
/////////////////////////////////////////////////////////////////////////
// Getting to contained values
/////////////////////////////////////////////////////////////////////////
/// Unwraps a Nullable, yielding the content of a `Nullable::Present`.
///
/// # Panics
///
/// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by
/// `msg`.
///
/// [`Nullable::Null`]: #variant.Null
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let x = Nullable::Present("value");
/// assert_eq!(x.expect("the world is ending"), "value");
/// ```
///
/// ```{.should_panic}
/// # use ping_bearer_auth::types::Nullable;
///
/// let x: Nullable<&str> = Nullable::Null;
/// x.expect("the world is ending"); // panics with `the world is ending`
/// ```
#[inline]
pub fn expect(self, msg: &str) -> T {
match self {
Nullable::Present(val) => val,
Nullable::Null => expect_failed(msg),
}
}
/// Moves the value `v` out of the `Nullable<T>` if it is `Nullable::Present(v)`.
///
/// In general, because this function may panic, its use is discouraged.
/// Instead, prefer to use pattern matching and handle the `Nullable::Null`
/// case explicitly.
///
/// # Panics
///
/// Panics if the self value equals [`Nullable::Null`].
///
/// [`Nullable::Null`]: #variant.Null
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let x = Nullable::Present("air");
/// assert_eq!(x.unwrap(), "air");
/// ```
///
/// ```{.should_panic}
/// # use ping_bearer_auth::types::Nullable;
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.unwrap(), "air"); // fails
/// ```
#[inline]
pub fn unwrap(self) -> T {
match self {
Nullable::Present(val) => val,
Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"),
}
}
/// Returns the contained value or a default.
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car");
/// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike");
/// ```
#[inline]
pub fn unwrap_or(self, def: T) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => def,
}
}
/// Returns the contained value or computes it from a closure.
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let k = 10;
/// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4);
/// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20);
/// ```
#[inline]
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => f(),
}
}
/////////////////////////////////////////////////////////////////////////
// Transforming contained values
/////////////////////////////////////////////////////////////////////////
/// Maps a `Nullable<T>` to `Nullable<U>` by applying a function to a contained value.
///
/// # Examples
///
/// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original:
///
/// [`String`]: ../../std/string/struct.String.html
/// [`usize`]: ../../std/primitive.usize.html
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let maybe_some_string = Nullable::Present(String::from("Hello, World!"));
/// // `Nullable::map` takes self *by value*, consuming `maybe_some_string`
/// let maybe_some_len = maybe_some_string.map(|s| s.len());
///
/// assert_eq!(maybe_some_len, Nullable::Present(13));
/// ```
#[inline]
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Nullable<U> {
match self {
Nullable::Present(x) => Nullable::Present(f(x)),
Nullable::Null => Nullable::Null,
}
}
/// Applies a function to the contained value (if any),
/// or returns a `default` (if not).
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.map_or(42, |v| v.len()), 3);
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.map_or(42, |v| v.len()), 42);
/// ```
#[inline]
pub fn map_or<U, F: FnOnce(T) -> U>(self, default: U, f: F) -> U {
match self {
Nullable::Present(t) => f(t),
Nullable::Null => default,
}
}
/// Applies a function to the contained value (if any),
/// or computes a `default` (if not).
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let k = 21;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3);
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42);
/// ```
#[inline]
pub fn map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
match self {
Nullable::Present(t) => f(t),
Nullable::Null => default(),
}
}
/// Transforms the `Nullable<T>` into a [`Result<T, E>`], mapping `Nullable::Present(v)` to
/// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err].
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [Err]: ../../std/result/enum.Result.html#variant.Err
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.ok_or(0), Ok("foo"));
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.ok_or(0), Err(0));
/// ```
#[inline]
pub fn ok_or<E>(self, err: E) -> Result<T, E> {
match self {
Nullable::Present(v) => Ok(v),
Nullable::Null => Err(err),
}
}
/// Transforms the `Nullable<T>` into a [`Result<T, E>`], mapping `Nullable::Present(v)` to
/// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err].
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [Err]: ../../std/result/enum.Result.html#variant.Err
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let x = Nullable::Present("foo");
/// assert_eq!(x.ok_or_else(|| 0), Ok("foo"));
///
/// let x: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.ok_or_else(|| 0), Err(0));
/// ```
#[inline]
pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
match self {
Nullable::Present(v) => Ok(v),
Nullable::Null => Err(err()),
}
}
/////////////////////////////////////////////////////////////////////////
// Boolean operations on the values, eager and lazy
/////////////////////////////////////////////////////////////////////////
/// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`.
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let x = Nullable::Present(2);
/// let y: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.and(y), Nullable::Null);
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y = Nullable::Present("foo");
/// assert_eq!(x.and(y), Nullable::Null);
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Present("foo");
/// assert_eq!(x.and(y), Nullable::Present("foo"));
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y: Nullable<&str> = Nullable::Null;
/// assert_eq!(x.and(y), Nullable::Null);
/// ```
#[inline]
pub fn and<U>(self, optb: Nullable<U>) -> Nullable<U> {
match self {
Nullable::Present(_) => optb,
Nullable::Null => Nullable::Null,
}
}
/// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the
/// wrapped value and returns the result.
///
/// Some languages call this operation flatmap.
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// fn sq(x: u32) -> Nullable<u32> { Nullable::Present(x * x) }
/// fn nope(_: u32) -> Nullable<u32> { Nullable::Null }
///
/// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16));
/// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null);
/// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null);
/// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null);
/// ```
#[inline]
pub fn and_then<U, F: FnOnce(T) -> Nullable<U>>(self, f: F) -> Nullable<U> {
match self {
Nullable::Present(x) => f(x),
Nullable::Null => Nullable::Null,
}
}
/// Returns the Nullable if it contains a value, otherwise returns `optb`.
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Null;
/// assert_eq!(x.or(y), Nullable::Present(2));
///
/// let x = Nullable::Null;
/// let y = Nullable::Present(100);
/// assert_eq!(x.or(y), Nullable::Present(100));
///
/// let x = Nullable::Present(2);
/// let y = Nullable::Present(100);
/// assert_eq!(x.or(y), Nullable::Present(2));
///
/// let x: Nullable<u32> = Nullable::Null;
/// let y = Nullable::Null;
/// assert_eq!(x.or(y), Nullable::Null);
/// ```
#[inline]
pub fn or(self, optb: Nullable<T>) -> Nullable<T> {
match self {
Nullable::Present(_) => self,
Nullable::Null => optb,
}
}
/// Returns the Nullable if it contains a value, otherwise calls `f` and
/// returns the result.
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// fn nobody() -> Nullable<&'static str> { Nullable::Null }
/// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") }
///
/// assert_eq!(Nullable::Present("barbarians").or_else(vikings),
/// Nullable::Present("barbarians"));
/// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings"));
/// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null);
/// ```
#[inline]
pub fn or_else<F: FnOnce() -> Nullable<T>>(self, f: F) -> Nullable<T> {
match self {
Nullable::Present(_) => self,
Nullable::Null => f(),
}
}
/////////////////////////////////////////////////////////////////////////
// Misc
/////////////////////////////////////////////////////////////////////////
/// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place.
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let mut x = Nullable::Present(2);
/// x.take();
/// assert_eq!(x, Nullable::Null);
///
/// let mut x: Nullable<u32> = Nullable::Null;
/// x.take();
/// assert_eq!(x, Nullable::Null);
/// ```
#[inline]
pub fn take(&mut self) -> Nullable<T> {
mem::replace(self, Nullable::Null)
}
}
impl<'a, T: Clone> Nullable<&'a T> {
/// Maps an `Nullable<&T>` to an `Nullable<T>` by cloning the contents of the
/// Nullable.
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let x = 12;
/// let opt_x = Nullable::Present(&x);
/// assert_eq!(opt_x, Nullable::Present(&12));
/// let cloned = opt_x.cloned();
/// assert_eq!(cloned, Nullable::Present(12));
/// ```
pub fn cloned(self) -> Nullable<T> {
self.map(Clone::clone)
}
}
impl<T: Default> Nullable<T> {
/// Returns the contained value or a default
///
/// Consumes the `self` argument then, if `Nullable::Present`, returns the contained
/// value, otherwise if `Nullable::Null`, returns the default value for that
/// type.
///
/// # Examples
///
/// ```
/// # use ping_bearer_auth::types::Nullable;
///
/// let x = Nullable::Present(42);
/// assert_eq!(42, x.unwrap_or_default());
///
/// let y: Nullable<i32> = Nullable::Null;
/// assert_eq!(0, y.unwrap_or_default());
/// ```
#[inline]
pub fn unwrap_or_default(self) -> T {
match self {
Nullable::Present(x) => x,
Nullable::Null => Default::default(),
}
}
}
impl<T> Default for Nullable<T> {
/// Returns None.
#[inline]
fn default() -> Nullable<T> {
Nullable::Null
}
}
impl<T> From<T> for Nullable<T> {
fn from(val: T) -> Nullable<T> {
Nullable::Present(val)
}
}
impl<T> Serialize for Nullable<T>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
Nullable::Present(ref inner) => serializer.serialize_some(&inner),
Nullable::Null => serializer.serialize_none(),
}
}
}
impl<'de, T> Deserialize<'de> for Nullable<T>
where
T: serde::de::DeserializeOwned,
{
fn deserialize<D>(deserializer: D) -> Result<Nullable<T>, D::Error>
where
D: Deserializer<'de>,
{
// In order to deserialize a required, but nullable, value, we first have to check whether
// the value is present at all. To do this, we deserialize to a serde_json::Value, which
// fails if the value is missing, or gives serde_json::Value::Null if the value is present.
// If that succeeds as null, we can easily return a Null.
// If that succeeds as some value, we deserialize that value and return a Present.
// If that errors, we return the error.
let presence: Result<::serde_json::Value, _> =
serde::Deserialize::deserialize(deserializer);
match presence {
Ok(serde_json::Value::Null) => Ok(Nullable::Null),
Ok(some_value) => serde_json::from_value(some_value)
.map(Nullable::Present)
.map_err(serde::de::Error::custom),
Err(x) => Err(x),
}
}
}
#[inline(never)]
#[cold]
fn expect_failed(msg: &str) -> ! {
panic!("{}", msg)
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
/// Base64-encoded byte array
pub struct ByteArray(pub Vec<u8>);
impl Serialize for ByteArray {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0))
}
}
impl<'de> Deserialize<'de> for ByteArray {
fn deserialize<D>(deserializer: D) -> Result<ByteArray, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match general_purpose::STANDARD.decode(s) {
Ok(bin) => Ok(ByteArray(bin)),
_ => Err(serde::de::Error::custom("invalid base64")),
}
}
}

View File

@ -0,0 +1,2 @@
target
Cargo.lock

View File

@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@ -0,0 +1,8 @@
.gitignore
Cargo.toml
README.md
src/header.rs
src/lib.rs
src/models.rs
src/server/mod.rs
src/types.rs

View File

@ -0,0 +1 @@
7.3.0-SNAPSHOT

View File

@ -0,0 +1,46 @@
[package]
name = "rust-server-test"
version = "2.3.4"
authors = ["OpenAPI Generator team and contributors"]
description = "This spec is for testing rust-server-specific things"
edition = "2021"
[features]
default = ["server"]
server = []
conversion = [
"frunk",
"frunk_derives",
"frunk_core",
"frunk-enum-core",
"frunk-enum-derive",
]
[dependencies]
async-trait = "0.1"
axum = { version = "0.7" }
axum-extra = { version = "0.9", features = ["cookie", "multipart"] }
base64 = "0.21"
bytes = "1"
chrono = { version = "0.4", features = ["serde"] }
frunk = { version = "0.4", optional = true }
frunk-enum-core = { version = "0.3", optional = true }
frunk-enum-derive = { version = "0.3", optional = true }
frunk_core = { version = "0.4", optional = true }
frunk_derives = { version = "0.4", optional = true }
http = "1"
lazy_static = "1"
regex = "1"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["raw_value"] }
serde_urlencoded = "0.7"
tokio = { version = "1", default-features = false, features = [
"signal",
"rt-multi-thread",
] }
tracing = { version = "0.1", features = ["attributes"] }
uuid = { version = "1", features = ["serde"] }
validator = { version = "0.16", features = ["derive"] }
[dev-dependencies]
tracing-subscriber = "0.3"

Some files were not shown because too many files have changed in this diff Show More