forked from loafle/openapi-generator-original
[Rust-Axum] Allow use of array query params (#20861)
* [Rust-Axum] Allow use of array query params * Add sample per PR request
This commit is contained in:
parent
3b0bb0a73d
commit
56dd63525c
11
bin/configs/manual/rust-axum-array-params.yaml
Normal file
11
bin/configs/manual/rust-axum-array-params.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
generatorName: rust-axum
|
||||
outputDir: samples/server/petstore/rust-axum/output/rust-axum-array-params-test
|
||||
inputSpec: modules/openapi-generator/src/test/resources/3_0/rust/rust-axum-array-params-test.yaml
|
||||
templateDir: modules/openapi-generator/src/main/resources/rust-axum
|
||||
generateAliasAsModel: true
|
||||
additionalProperties:
|
||||
hideGenerationTimestamp: "true"
|
||||
packageName: rust-axum-array-params-test
|
||||
globalProperties:
|
||||
skipFormModel: false
|
||||
enablePostProcessFile: true
|
@ -41,7 +41,7 @@ conversion = [
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
axum = { version = "0.8", features = ["multipart"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie", "query"] }
|
||||
base64 = "0.22"
|
||||
bytes = "1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
@ -54,8 +54,8 @@ http = "1"
|
||||
lazy_static = "1"
|
||||
regex = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_html_form = "0.2"
|
||||
serde_json = { version = "1", features = ["raw_value"] }
|
||||
serde_urlencoded = "0.7"
|
||||
tokio = { version = "1", default-features = false, features = [
|
||||
"signal",
|
||||
"rt-multi-thread",
|
||||
|
@ -207,7 +207,6 @@ use crate::{models, types::*};
|
||||
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
|
||||
pub struct {{{operationIdCamelCase}}}QueryParams {
|
||||
{{#queryParams}}
|
||||
{{#vendorExtensions}}
|
||||
{{#description}}
|
||||
/// {{{.}}}
|
||||
{{/description}}
|
||||
@ -266,6 +265,16 @@ use crate::{models, types::*};
|
||||
{{/maxItems}}
|
||||
)]
|
||||
{{/hasValidation}}
|
||||
{{#isArray}}
|
||||
{{#isNullable}}
|
||||
// Nullable array query parameters are not fully supported.
|
||||
{{/isNullable}}
|
||||
{{^required}}
|
||||
#[serde(default)]
|
||||
{{/required}}
|
||||
pub {{{paramName}}}: {{{dataType}}},
|
||||
{{/isArray}}
|
||||
{{^isArray}}
|
||||
{{#required}}
|
||||
pub {{{paramName}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}},
|
||||
{{/required}}
|
||||
@ -277,7 +286,7 @@ use crate::{models, types::*};
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
pub {{{paramName}}}: Option<{{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}>,
|
||||
{{/required}}
|
||||
{{/vendorExtensions}}
|
||||
{{/isArray}}
|
||||
{{/queryParams}}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{body::Body, extract::*, response::Response, routing::*};
|
||||
use axum_extra::extract::{CookieJar, Host};
|
||||
use axum_extra::extract::{CookieJar, Host, Query as QueryExtra};
|
||||
use bytes::Bytes;
|
||||
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
|
||||
use tracing::error;
|
||||
|
@ -23,7 +23,7 @@ async fn {{#vendorExtensions}}{{{x-operation-id}}}{{/vendorExtensions}}<I, A, E{
|
||||
Path(path_params): Path<models::{{{operationIdCamelCase}}}PathParams>,
|
||||
{{/pathParams.size}}
|
||||
{{#queryParams.size}}
|
||||
Query(query_params): Query<models::{{{operationIdCamelCase}}}QueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::{{{operationIdCamelCase}}}QueryParams>,
|
||||
{{/queryParams.size}}
|
||||
State(api_impl): State<I>,
|
||||
{{#vendorExtensions}}
|
||||
@ -334,7 +334,7 @@ where
|
||||
{{#allowBlockingResponseSerialize}}
|
||||
let body_content =
|
||||
{{/allowBlockingResponseSerialize}}
|
||||
serde_urlencoded::to_string(body).map_err(|e| {
|
||||
serde_html_form::to_string(body).map_err(|e| {
|
||||
error!(error = ?e);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}){{^allowBlockingResponseSerialize}}).await.unwrap(){{/allowBlockingResponseSerialize}}?;
|
||||
|
@ -0,0 +1,24 @@
|
||||
openapi: 3.0.1
|
||||
info:
|
||||
title: Test to check that array query params are rendered correctly.
|
||||
version: 0.0.1
|
||||
paths:
|
||||
/endpoint:
|
||||
get:
|
||||
description: Some endpoint.
|
||||
parameters:
|
||||
- name: numbers
|
||||
in: query
|
||||
description: Some numbers.
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: number
|
||||
- name: multiplier
|
||||
in: query
|
||||
description: Multipler for sum.
|
||||
schema:
|
||||
type: number
|
||||
responses:
|
||||
'200':
|
||||
description: OK.
|
@ -1 +1 @@
|
||||
7.12.0-SNAPSHOT
|
||||
7.13.0-SNAPSHOT
|
||||
|
@ -19,7 +19,7 @@ conversion = [
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
axum = { version = "0.8", features = ["multipart"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie", "query"] }
|
||||
base64 = "0.22"
|
||||
bytes = "1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
@ -32,8 +32,8 @@ http = "1"
|
||||
lazy_static = "1"
|
||||
regex = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_html_form = "0.2"
|
||||
serde_json = { version = "1", features = ["raw_value"] }
|
||||
serde_urlencoded = "0.7"
|
||||
tokio = { version = "1", default-features = false, features = [
|
||||
"signal",
|
||||
"rt-multi-thread",
|
||||
|
@ -12,7 +12,7 @@ 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
|
||||
- Generator version: 7.12.0-SNAPSHOT
|
||||
- Generator version: 7.13.0-SNAPSHOT
|
||||
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{body::Body, extract::*, response::Response, routing::*};
|
||||
use axum_extra::extract::{CookieJar, Host};
|
||||
use axum_extra::extract::{CookieJar, Host, Query as QueryExtra};
|
||||
use bytes::Bytes;
|
||||
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
|
||||
use tracing::error;
|
||||
|
@ -1 +1 @@
|
||||
7.12.0-SNAPSHOT
|
||||
7.13.0-SNAPSHOT
|
||||
|
@ -19,7 +19,7 @@ conversion = [
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
axum = { version = "0.8", features = ["multipart"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie", "query"] }
|
||||
base64 = "0.22"
|
||||
bytes = "1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
@ -32,8 +32,8 @@ http = "1"
|
||||
lazy_static = "1"
|
||||
regex = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_html_form = "0.2"
|
||||
serde_json = { version = "1", features = ["raw_value"] }
|
||||
serde_urlencoded = "0.7"
|
||||
tokio = { version = "1", default-features = false, features = [
|
||||
"signal",
|
||||
"rt-multi-thread",
|
||||
|
@ -12,7 +12,7 @@ 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
|
||||
- Generator version: 7.12.0-SNAPSHOT
|
||||
- Generator version: 7.13.0-SNAPSHOT
|
||||
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{body::Body, extract::*, response::Response, routing::*};
|
||||
use axum_extra::extract::{CookieJar, Host};
|
||||
use axum_extra::extract::{CookieJar, Host, Query as QueryExtra};
|
||||
use bytes::Bytes;
|
||||
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
|
||||
use tracing::error;
|
||||
|
@ -1 +1 @@
|
||||
7.12.0-SNAPSHOT
|
||||
7.13.0-SNAPSHOT
|
||||
|
@ -19,7 +19,7 @@ conversion = [
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
axum = { version = "0.8", features = ["multipart"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie", "query"] }
|
||||
base64 = "0.22"
|
||||
bytes = "1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
@ -32,8 +32,8 @@ http = "1"
|
||||
lazy_static = "1"
|
||||
regex = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_html_form = "0.2"
|
||||
serde_json = { version = "1", features = ["raw_value"] }
|
||||
serde_urlencoded = "0.7"
|
||||
tokio = { version = "1", default-features = false, features = [
|
||||
"signal",
|
||||
"rt-multi-thread",
|
||||
|
@ -12,7 +12,7 @@ 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
|
||||
- Generator version: 7.12.0-SNAPSHOT
|
||||
- Generator version: 7.13.0-SNAPSHOT
|
||||
|
||||
|
||||
|
||||
|
@ -13,8 +13,8 @@ pub struct AnyOfGetQueryParams {
|
||||
/// list of any of objects
|
||||
#[serde(rename = "any-of")]
|
||||
#[validate(length(min = 1))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub any_of: Option<Vec<models::AnyOfObject>>,
|
||||
#[serde(default)]
|
||||
pub any_of: Vec<models::AnyOfObject>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
|
||||
@ -28,8 +28,8 @@ pub struct CallbackWithHeaderPostQueryParams {
|
||||
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
|
||||
pub struct ComplexQueryParamGetQueryParams {
|
||||
#[serde(rename = "list-of-strings")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub list_of_strings: Option<Vec<models::StringObject>>,
|
||||
#[serde(default)]
|
||||
pub list_of_strings: Vec<models::StringObject>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
|
||||
@ -50,8 +50,8 @@ pub struct GetWithBooleanParameterQueryParams {
|
||||
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
|
||||
pub struct JsonComplexQueryParamGetQueryParams {
|
||||
#[serde(rename = "list-of-strings")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub list_of_strings: Option<Vec<models::StringObject>>,
|
||||
#[serde(default)]
|
||||
pub list_of_strings: Vec<models::StringObject>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
|
||||
@ -80,8 +80,8 @@ pub struct ParamgetGetQueryParams {
|
||||
pub some_object: Option<crate::types::Object>,
|
||||
/// Some list to pass as query parameter
|
||||
#[serde(rename = "someList")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub some_list: Option<Vec<models::MyId>>,
|
||||
#[serde(default)]
|
||||
pub some_list: Vec<models::MyId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{body::Body, extract::*, response::Response, routing::*};
|
||||
use axum_extra::extract::{CookieJar, Host};
|
||||
use axum_extra::extract::{CookieJar, Host, Query as QueryExtra};
|
||||
use bytes::Bytes;
|
||||
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
|
||||
use tracing::error;
|
||||
@ -127,7 +127,7 @@ async fn any_of_get<I, A, E>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
Query(query_params): Query<models::AnyOfGetQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::AnyOfGetQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
) -> Result<Response, StatusCode>
|
||||
where
|
||||
@ -253,7 +253,7 @@ async fn callback_with_header_post<I, A, E>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
Query(query_params): Query<models::CallbackWithHeaderPostQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::CallbackWithHeaderPostQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
) -> Result<Response, StatusCode>
|
||||
where
|
||||
@ -314,7 +314,7 @@ async fn complex_query_param_get<I, A, E>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
Query(query_params): Query<models::ComplexQueryParamGetQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::ComplexQueryParamGetQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
) -> Result<Response, StatusCode>
|
||||
where
|
||||
@ -505,7 +505,7 @@ async fn get_with_boolean_parameter<I, A, E>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
Query(query_params): Query<models::GetWithBooleanParameterQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::GetWithBooleanParameterQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
) -> Result<Response, StatusCode>
|
||||
where
|
||||
@ -566,7 +566,7 @@ async fn json_complex_query_param_get<I, A, E>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
Query(query_params): Query<models::JsonComplexQueryParamGetQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::JsonComplexQueryParamGetQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
) -> Result<Response, StatusCode>
|
||||
where
|
||||
@ -1247,7 +1247,7 @@ async fn paramget_get<I, A, E>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
Query(query_params): Query<models::ParamgetGetQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::ParamgetGetQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
) -> Result<Response, StatusCode>
|
||||
where
|
||||
@ -1381,7 +1381,7 @@ async fn register_callback_post<I, A, E>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
Query(query_params): Query<models::RegisterCallbackPostQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::RegisterCallbackPostQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
) -> Result<Response, StatusCode>
|
||||
where
|
||||
|
@ -1 +1 @@
|
||||
7.12.0-SNAPSHOT
|
||||
7.13.0-SNAPSHOT
|
||||
|
@ -19,7 +19,7 @@ conversion = [
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
axum = { version = "0.8", features = ["multipart"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie", "query"] }
|
||||
base64 = "0.22"
|
||||
bytes = "1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
@ -32,8 +32,8 @@ http = "1"
|
||||
lazy_static = "1"
|
||||
regex = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_html_form = "0.2"
|
||||
serde_json = { version = "1", features = ["raw_value"] }
|
||||
serde_urlencoded = "0.7"
|
||||
tokio = { version = "1", default-features = false, features = [
|
||||
"signal",
|
||||
"rt-multi-thread",
|
||||
|
@ -12,7 +12,7 @@ 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
|
||||
- Generator version: 7.12.0-SNAPSHOT
|
||||
- Generator version: 7.13.0-SNAPSHOT
|
||||
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{body::Body, extract::*, response::Response, routing::*};
|
||||
use axum_extra::extract::{CookieJar, Host};
|
||||
use axum_extra::extract::{CookieJar, Host, Query as QueryExtra};
|
||||
use bytes::Bytes;
|
||||
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
|
||||
use tracing::error;
|
||||
|
@ -1 +1 @@
|
||||
7.12.0-SNAPSHOT
|
||||
7.13.0-SNAPSHOT
|
||||
|
@ -21,7 +21,7 @@ conversion = [
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
axum = { version = "0.8", features = ["multipart"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie", "query"] }
|
||||
base64 = "0.22"
|
||||
bytes = "1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
@ -34,8 +34,8 @@ http = "1"
|
||||
lazy_static = "1"
|
||||
regex = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_html_form = "0.2"
|
||||
serde_json = { version = "1", features = ["raw_value"] }
|
||||
serde_urlencoded = "0.7"
|
||||
tokio = { version = "1", default-features = false, features = [
|
||||
"signal",
|
||||
"rt-multi-thread",
|
||||
|
@ -12,7 +12,7 @@ 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
|
||||
- Generator version: 7.12.0-SNAPSHOT
|
||||
- Generator version: 7.13.0-SNAPSHOT
|
||||
|
||||
|
||||
|
||||
|
@ -34,8 +34,8 @@ pub struct TestEnumParametersQueryParams {
|
||||
/// Query parameter enum test (string array)
|
||||
/// Note: inline enums are not fully supported by openapi-generator
|
||||
#[serde(rename = "enum_query_string_array")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub enum_query_string_array: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub enum_query_string_array: Vec<String>,
|
||||
/// Query parameter enum test (string)
|
||||
/// Note: inline enums are not fully supported by openapi-generator
|
||||
#[serde(rename = "enum_query_string")]
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{body::Body, extract::*, response::Response, routing::*};
|
||||
use axum_extra::extract::{CookieJar, Host};
|
||||
use axum_extra::extract::{CookieJar, Host, Query as QueryExtra};
|
||||
use bytes::Bytes;
|
||||
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
|
||||
use tracing::error;
|
||||
@ -804,7 +804,7 @@ async fn test_body_with_query_params<I, A, E, C>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
Query(query_params): Query<models::TestBodyWithQueryParamsQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::TestBodyWithQueryParamsQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
Json(body): Json<models::User>,
|
||||
) -> Result<Response, StatusCode>
|
||||
@ -1074,7 +1074,7 @@ async fn test_enum_parameters<I, A, E, C>(
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
headers: HeaderMap,
|
||||
Query(query_params): Query<models::TestEnumParametersQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::TestEnumParametersQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
Form(body): Form<Option<models::TestEnumParametersRequest>>,
|
||||
) -> Result<Response, StatusCode>
|
||||
@ -1605,7 +1605,7 @@ async fn find_pets_by_status<I, A, E, C>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
Query(query_params): Query<models::FindPetsByStatusQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::FindPetsByStatusQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
) -> Result<Response, StatusCode>
|
||||
where
|
||||
@ -1686,7 +1686,7 @@ async fn find_pets_by_tags<I, A, E, C>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
Query(query_params): Query<models::FindPetsByTagsQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::FindPetsByTagsQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
) -> Result<Response, StatusCode>
|
||||
where
|
||||
@ -2815,7 +2815,7 @@ async fn login_user<I, A, E>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
Query(query_params): Query<models::LoginUserQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::LoginUserQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
) -> Result<Response, StatusCode>
|
||||
where
|
||||
|
@ -1 +1 @@
|
||||
7.12.0-SNAPSHOT
|
||||
7.13.0-SNAPSHOT
|
||||
|
@ -20,7 +20,7 @@ conversion = [
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
axum = { version = "0.8", features = ["multipart"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie", "query"] }
|
||||
base64 = "0.22"
|
||||
bytes = "1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
@ -33,8 +33,8 @@ http = "1"
|
||||
lazy_static = "1"
|
||||
regex = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_html_form = "0.2"
|
||||
serde_json = { version = "1", features = ["raw_value"] }
|
||||
serde_urlencoded = "0.7"
|
||||
tokio = { version = "1", default-features = false, features = [
|
||||
"signal",
|
||||
"rt-multi-thread",
|
||||
|
@ -12,7 +12,7 @@ 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
|
||||
- Generator version: 7.12.0-SNAPSHOT
|
||||
- Generator version: 7.13.0-SNAPSHOT
|
||||
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{body::Body, extract::*, response::Response, routing::*};
|
||||
use axum_extra::extract::{CookieJar, Host};
|
||||
use axum_extra::extract::{CookieJar, Host, Query as QueryExtra};
|
||||
use bytes::Bytes;
|
||||
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
|
||||
use tracing::error;
|
||||
@ -269,7 +269,7 @@ async fn find_pets_by_status<I, A, E, C>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
Query(query_params): Query<models::FindPetsByStatusQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::FindPetsByStatusQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
) -> Result<Response, StatusCode>
|
||||
where
|
||||
@ -350,7 +350,7 @@ async fn find_pets_by_tags<I, A, E, C>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
Query(query_params): Query<models::FindPetsByTagsQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::FindPetsByTagsQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
) -> Result<Response, StatusCode>
|
||||
where
|
||||
@ -1551,7 +1551,7 @@ async fn login_user<I, A, E, C>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
Query(query_params): Query<models::LoginUserQueryParams>,
|
||||
QueryExtra(query_params): QueryExtra<models::LoginUserQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
) -> Result<Response, StatusCode>
|
||||
where
|
||||
|
@ -1 +1 @@
|
||||
7.12.0-SNAPSHOT
|
||||
7.13.0-SNAPSHOT
|
||||
|
@ -19,7 +19,7 @@ conversion = [
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
axum = { version = "0.8", features = ["multipart"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie", "query"] }
|
||||
base64 = "0.22"
|
||||
bytes = "1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
@ -32,8 +32,8 @@ http = "1"
|
||||
lazy_static = "1"
|
||||
regex = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_html_form = "0.2"
|
||||
serde_json = { version = "1", features = ["raw_value"] }
|
||||
serde_urlencoded = "0.7"
|
||||
tokio = { version = "1", default-features = false, features = [
|
||||
"signal",
|
||||
"rt-multi-thread",
|
||||
|
@ -12,7 +12,7 @@ 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
|
||||
- Generator version: 7.12.0-SNAPSHOT
|
||||
- Generator version: 7.13.0-SNAPSHOT
|
||||
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{body::Body, extract::*, response::Response, routing::*};
|
||||
use axum_extra::extract::{CookieJar, Host};
|
||||
use axum_extra::extract::{CookieJar, Host, Query as QueryExtra};
|
||||
use bytes::Bytes;
|
||||
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
|
||||
use tracing::error;
|
||||
|
2
samples/server/petstore/rust-axum/output/rust-axum-array-params-test/.gitignore
vendored
Normal file
2
samples/server/petstore/rust-axum/output/rust-axum-array-params-test/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
target
|
||||
Cargo.lock
|
@ -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
|
@ -0,0 +1,11 @@
|
||||
.gitignore
|
||||
.openapi-generator-ignore
|
||||
Cargo.toml
|
||||
README.md
|
||||
src/apis/default.rs
|
||||
src/apis/mod.rs
|
||||
src/header.rs
|
||||
src/lib.rs
|
||||
src/models.rs
|
||||
src/server/mod.rs
|
||||
src/types.rs
|
@ -0,0 +1 @@
|
||||
7.13.0-SNAPSHOT
|
@ -0,0 +1,46 @@
|
||||
[package]
|
||||
name = "rust-axum-array-params-test"
|
||||
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.8", features = ["multipart"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie", "query"] }
|
||||
base64 = "0.22"
|
||||
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_html_form = "0.2"
|
||||
serde_json = { version = "1", features = ["raw_value"] }
|
||||
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.20", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tracing-subscriber = "0.3"
|
@ -0,0 +1,93 @@
|
||||
# Rust API for rust-axum-array-params-test
|
||||
|
||||
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
|
||||
- Generator version: 7.13.0-SNAPSHOT
|
||||
|
||||
|
||||
|
||||
This autogenerated project defines an API crate `rust-axum-array-params-test` 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 rust_axum_array_params_test::apis::default::Api for ServerImpl {
|
||||
// API implementation goes here
|
||||
}
|
||||
|
||||
impl rust_axum_array_params_test::apis::ErrorHandler for ServerImpl {}
|
||||
|
||||
pub async fn start_server(addr: &str) {
|
||||
// initialize tracing
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
// Init Axum router
|
||||
let app = rust_axum_array_params_test::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 => {},
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,30 @@
|
||||
use async_trait::async_trait;
|
||||
use axum::extract::*;
|
||||
use axum_extra::extract::{CookieJar, Host};
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{models, types::*};
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[must_use]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum EndpointGetResponse {
|
||||
/// OK.
|
||||
Status200_OK,
|
||||
}
|
||||
|
||||
/// Default
|
||||
#[async_trait]
|
||||
#[allow(clippy::ptr_arg)]
|
||||
pub trait Default<E: std::fmt::Debug + Send + Sync + 'static = ()>: super::ErrorHandler<E> {
|
||||
/// EndpointGet - GET /endpoint
|
||||
async fn endpoint_get(
|
||||
&self,
|
||||
method: &Method,
|
||||
host: &Host,
|
||||
cookies: &CookieJar,
|
||||
query_params: &models::EndpointGetQueryParams,
|
||||
) -> Result<EndpointGetResponse, E>;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
pub mod default;
|
||||
|
||||
// Error handler for unhandled errors.
|
||||
#[async_trait::async_trait]
|
||||
pub trait ErrorHandler<E: std::fmt::Debug + Send + Sync + 'static = ()> {
|
||||
#[allow(unused_variables)]
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn handle_error(
|
||||
&self,
|
||||
method: &::http::Method,
|
||||
host: &axum_extra::extract::Host,
|
||||
cookies: &axum_extra::extract::CookieJar,
|
||||
error: E,
|
||||
) -> Result<axum::response::Response, http::StatusCode> {
|
||||
tracing::error!("Unhandled error: {:?}", error);
|
||||
axum::response::Response::builder()
|
||||
.status(http::StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.body(axum::body::Body::empty())
|
||||
.map_err(|_| http::StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
@ -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
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
#![allow(
|
||||
missing_docs,
|
||||
trivial_casts,
|
||||
unused_variables,
|
||||
unused_mut,
|
||||
unused_extern_crates,
|
||||
non_camel_case_types,
|
||||
unused_imports,
|
||||
unused_attributes
|
||||
)]
|
||||
#![allow(
|
||||
clippy::derive_partial_eq_without_eq,
|
||||
clippy::disallowed_names,
|
||||
clippy::too_many_arguments
|
||||
)]
|
||||
|
||||
pub const BASE_PATH: &str = "";
|
||||
pub const API_VERSION: &str = "0.0.1";
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
pub mod server;
|
||||
|
||||
pub mod apis;
|
||||
pub mod models;
|
||||
pub mod types;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
pub(crate) mod header;
|
@ -0,0 +1,21 @@
|
||||
#![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 EndpointGetQueryParams {
|
||||
/// Some numbers.
|
||||
#[serde(rename = "numbers")]
|
||||
#[serde(default)]
|
||||
pub numbers: Vec<f64>,
|
||||
/// Multipler for sum.
|
||||
#[serde(rename = "multiplier")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub multiplier: Option<f64>,
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{body::Body, extract::*, response::Response, routing::*};
|
||||
use axum_extra::extract::{CookieJar, Host, Query as QueryExtra};
|
||||
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::{apis, models};
|
||||
|
||||
/// Setup API Server.
|
||||
pub fn new<I, A, E>(api_impl: I) -> Router
|
||||
where
|
||||
I: AsRef<A> + Clone + Send + Sync + 'static,
|
||||
A: apis::default::Default<E> + Send + Sync + 'static,
|
||||
E: std::fmt::Debug + Send + Sync + 'static,
|
||||
{
|
||||
// build our application with a route
|
||||
Router::new()
|
||||
.route("/endpoint", get(endpoint_get::<I, A, E>))
|
||||
.with_state(api_impl)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn endpoint_get_validation(
|
||||
query_params: models::EndpointGetQueryParams,
|
||||
) -> std::result::Result<(models::EndpointGetQueryParams,), ValidationErrors> {
|
||||
query_params.validate()?;
|
||||
|
||||
Ok((query_params,))
|
||||
}
|
||||
/// EndpointGet - GET /endpoint
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn endpoint_get<I, A, E>(
|
||||
method: Method,
|
||||
host: Host,
|
||||
cookies: CookieJar,
|
||||
QueryExtra(query_params): QueryExtra<models::EndpointGetQueryParams>,
|
||||
State(api_impl): State<I>,
|
||||
) -> Result<Response, StatusCode>
|
||||
where
|
||||
I: AsRef<A> + Send + Sync,
|
||||
A: apis::default::Default<E> + Send + Sync,
|
||||
E: std::fmt::Debug + Send + Sync + 'static,
|
||||
{
|
||||
#[allow(clippy::redundant_closure)]
|
||||
let validation = tokio::task::spawn_blocking(move || endpoint_get_validation(query_params))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let Ok((query_params,)) = 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()
|
||||
.endpoint_get(&method, &host, &cookies, &query_params)
|
||||
.await;
|
||||
|
||||
let mut response = Response::builder();
|
||||
|
||||
let resp = match result {
|
||||
Ok(rsp) => match rsp {
|
||||
apis::default::EndpointGetResponse::Status200_OK => {
|
||||
let mut response = response.status(200);
|
||||
response.body(Body::empty())
|
||||
}
|
||||
},
|
||||
Err(why) => {
|
||||
// Application code returned an error. This should not happen, as the implementation should
|
||||
// return a valid response.
|
||||
return api_impl
|
||||
.as_ref()
|
||||
.handle_error(&method, &host, &cookies, why)
|
||||
.await;
|
||||
}
|
||||
};
|
||||
|
||||
resp.map_err(|e| {
|
||||
error!(error = ?e);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})
|
||||
}
|
@ -0,0 +1,790 @@
|
||||
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 rust_axum_array_params_test::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 rust_axum_array_params_test::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 rust_axum_array_params_test::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 rust_axum_array_params_test::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 rust_axum_array_params_test::types::Nullable;
|
||||
///
|
||||
/// let x = Nullable::Present("value");
|
||||
/// assert_eq!(x.expect("the world is ending"), "value");
|
||||
/// ```
|
||||
///
|
||||
/// ```should_panic
|
||||
/// # use rust_axum_array_params_test::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 rust_axum_array_params_test::types::Nullable;
|
||||
///
|
||||
/// let x = Nullable::Present("air");
|
||||
/// assert_eq!(x.unwrap(), "air");
|
||||
/// ```
|
||||
///
|
||||
/// ```should_panic
|
||||
/// # use rust_axum_array_params_test::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 rust_axum_array_params_test::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 rust_axum_array_params_test::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 rust_axum_array_params_test::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 rust_axum_array_params_test::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 rust_axum_array_params_test::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 rust_axum_array_params_test::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 rust_axum_array_params_test::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 rust_axum_array_params_test::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 rust_axum_array_params_test::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 rust_axum_array_params_test::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 rust_axum_array_params_test::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 rust_axum_array_params_test::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<T: Clone> Nullable<&T> {
|
||||
/// Maps an `Nullable<&T>` to an `Nullable<T>` by cloning the contents of the
|
||||
/// Nullable.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rust_axum_array_params_test::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 rust_axum_array_params_test::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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> validator::Validate for Nullable<T>
|
||||
where
|
||||
T: validator::Validate,
|
||||
{
|
||||
fn validate(&self) -> Result<(), validator::ValidationErrors> {
|
||||
match self {
|
||||
Self::Present(x) => x.validate(),
|
||||
Self::Null => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> validator::ValidateArgs<'a> for Nullable<T>
|
||||
where
|
||||
T: validator::ValidateArgs<'a>,
|
||||
{
|
||||
type Args = T::Args;
|
||||
fn validate_with_args(&self, args: Self::Args) -> Result<(), validator::ValidationErrors> {
|
||||
match self {
|
||||
Self::Present(x) => x.validate_with_args(args),
|
||||
Self::Null => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> validator::ValidateEmail for Nullable<T>
|
||||
where
|
||||
T: validator::ValidateEmail,
|
||||
{
|
||||
fn as_email_string(&self) -> Option<std::borrow::Cow<str>> {
|
||||
match self {
|
||||
Self::Present(x) => x.as_email_string(),
|
||||
Self::Null => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> validator::ValidateUrl for Nullable<T>
|
||||
where
|
||||
T: validator::ValidateUrl,
|
||||
{
|
||||
fn as_url_string(&self) -> Option<std::borrow::Cow<str>> {
|
||||
match self {
|
||||
Self::Present(x) => x.as_url_string(),
|
||||
Self::Null => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> validator::ValidateContains for Nullable<T>
|
||||
where
|
||||
T: validator::ValidateContains,
|
||||
{
|
||||
fn validate_contains(&self, needle: &str) -> bool {
|
||||
match self {
|
||||
Self::Present(x) => x.validate_contains(needle),
|
||||
Self::Null => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> validator::ValidateRequired for Nullable<T>
|
||||
where
|
||||
T: validator::ValidateRequired,
|
||||
{
|
||||
fn is_some(&self) -> bool {
|
||||
self.is_present()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> validator::ValidateRegex for Nullable<T>
|
||||
where
|
||||
T: validator::ValidateRegex,
|
||||
{
|
||||
fn validate_regex(&self, regex: impl validator::AsRegex) -> bool {
|
||||
match self {
|
||||
Self::Present(x) => x.validate_regex(regex),
|
||||
Self::Null => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I> validator::ValidateRange<I> for Nullable<T>
|
||||
where
|
||||
T: validator::ValidateRange<I>,
|
||||
{
|
||||
fn greater_than(&self, max: I) -> Option<bool> {
|
||||
use validator::ValidateRange;
|
||||
match self {
|
||||
Self::Present(x) => x.greater_than(max),
|
||||
Self::Null => None,
|
||||
}
|
||||
}
|
||||
fn less_than(&self, min: I) -> Option<bool> {
|
||||
use validator::ValidateRange;
|
||||
match self {
|
||||
Self::Present(x) => x.less_than(min),
|
||||
Self::Null => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I> validator::ValidateLength<I> for Nullable<T>
|
||||
where
|
||||
T: validator::ValidateLength<I>,
|
||||
I: PartialEq + PartialOrd,
|
||||
{
|
||||
fn length(&self) -> Option<I> {
|
||||
use validator::ValidateLength;
|
||||
match self {
|
||||
Self::Present(x) => x.length(),
|
||||
Self::Null => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Nullable<T>> for Option<T> {
|
||||
fn from(value: Nullable<T>) -> Option<T> {
|
||||
match value {
|
||||
Nullable::Present(x) => Some(x),
|
||||
Nullable::Null => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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")),
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
7.12.0-SNAPSHOT
|
||||
7.13.0-SNAPSHOT
|
||||
|
@ -19,7 +19,7 @@ conversion = [
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
axum = { version = "0.8", features = ["multipart"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie", "query"] }
|
||||
base64 = "0.22"
|
||||
bytes = "1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
@ -32,8 +32,8 @@ http = "1"
|
||||
lazy_static = "1"
|
||||
regex = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_html_form = "0.2"
|
||||
serde_json = { version = "1", features = ["raw_value"] }
|
||||
serde_urlencoded = "0.7"
|
||||
tokio = { version = "1", default-features = false, features = [
|
||||
"signal",
|
||||
"rt-multi-thread",
|
||||
|
@ -12,7 +12,7 @@ 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.1.9
|
||||
- Generator version: 7.12.0-SNAPSHOT
|
||||
- Generator version: 7.13.0-SNAPSHOT
|
||||
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{body::Body, extract::*, response::Response, routing::*};
|
||||
use axum_extra::extract::{CookieJar, Host};
|
||||
use axum_extra::extract::{CookieJar, Host, Query as QueryExtra};
|
||||
use bytes::Bytes;
|
||||
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
|
||||
use tracing::error;
|
||||
|
@ -1 +1 @@
|
||||
7.12.0-SNAPSHOT
|
||||
7.13.0-SNAPSHOT
|
||||
|
@ -19,7 +19,7 @@ conversion = [
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
axum = { version = "0.8", features = ["multipart"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie", "query"] }
|
||||
base64 = "0.22"
|
||||
bytes = "1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
@ -32,8 +32,8 @@ http = "1"
|
||||
lazy_static = "1"
|
||||
regex = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_html_form = "0.2"
|
||||
serde_json = { version = "1", features = ["raw_value"] }
|
||||
serde_urlencoded = "0.7"
|
||||
tokio = { version = "1", default-features = false, features = [
|
||||
"signal",
|
||||
"rt-multi-thread",
|
||||
|
@ -12,7 +12,7 @@ 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
|
||||
- Generator version: 7.12.0-SNAPSHOT
|
||||
- Generator version: 7.13.0-SNAPSHOT
|
||||
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{body::Body, extract::*, response::Response, routing::*};
|
||||
use axum_extra::extract::{CookieJar, Host};
|
||||
use axum_extra::extract::{CookieJar, Host, Query as QueryExtra};
|
||||
use bytes::Bytes;
|
||||
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
|
||||
use tracing::error;
|
||||
|
@ -1 +1 @@
|
||||
7.12.0-SNAPSHOT
|
||||
7.13.0-SNAPSHOT
|
||||
|
@ -19,7 +19,7 @@ conversion = [
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
axum = { version = "0.8", features = ["multipart"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie", "query"] }
|
||||
base64 = "0.22"
|
||||
bytes = "1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
@ -32,8 +32,8 @@ http = "1"
|
||||
lazy_static = "1"
|
||||
regex = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_html_form = "0.2"
|
||||
serde_json = { version = "1", features = ["raw_value"] }
|
||||
serde_urlencoded = "0.7"
|
||||
tokio = { version = "1", default-features = false, features = [
|
||||
"signal",
|
||||
"rt-multi-thread",
|
||||
|
@ -12,7 +12,7 @@ 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: 2.3.4
|
||||
- Generator version: 7.12.0-SNAPSHOT
|
||||
- Generator version: 7.13.0-SNAPSHOT
|
||||
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{body::Body, extract::*, response::Response, routing::*};
|
||||
use axum_extra::extract::{CookieJar, Host};
|
||||
use axum_extra::extract::{CookieJar, Host, Query as QueryExtra};
|
||||
use bytes::Bytes;
|
||||
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
|
||||
use tracing::error;
|
||||
|
@ -1 +1 @@
|
||||
7.12.0-SNAPSHOT
|
||||
7.13.0-SNAPSHOT
|
||||
|
@ -19,7 +19,7 @@ conversion = [
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
axum = { version = "0.8", features = ["multipart"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie"] }
|
||||
axum-extra = { version = "0.10", features = ["cookie", "query"] }
|
||||
base64 = "0.22"
|
||||
bytes = "1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
@ -32,8 +32,8 @@ http = "1"
|
||||
lazy_static = "1"
|
||||
regex = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_html_form = "0.2"
|
||||
serde_json = { version = "1", features = ["raw_value"] }
|
||||
serde_urlencoded = "0.7"
|
||||
tokio = { version = "1", default-features = false, features = [
|
||||
"signal",
|
||||
"rt-multi-thread",
|
||||
|
@ -12,7 +12,7 @@ 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
|
||||
- Generator version: 7.12.0-SNAPSHOT
|
||||
- Generator version: 7.13.0-SNAPSHOT
|
||||
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{body::Body, extract::*, response::Response, routing::*};
|
||||
use axum_extra::extract::{CookieJar, Host};
|
||||
use axum_extra::extract::{CookieJar, Host, Query as QueryExtra};
|
||||
use bytes::Bytes;
|
||||
use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
|
||||
use tracing::error;
|
||||
|
Loading…
x
Reference in New Issue
Block a user