{%- macro SCHEMA(schema) -%} {%- if schema.type -%} {%- if schema.type == "object" -%} {%- if schema.additionalProperties -%} HashMap {%- else -%} {%- for property_key, property in schema.properties %} pub r#{{property_key}}: {%- set inner=self::SCHEMA(schema=property) -%} {%- if schema.required and property_key in schema.required -%}{{inner}}{%- else -%}Option<{{inner}}>{%- endif -%}, {%- endfor %} {%- endif -%} {%- elif schema.type == "array" -%} Vec<{{self::SCHEMA(schema=schema.items)}}> {%- elif schema.type == "integer" -%} {%- if "u" in schema.format -%}u{%- else -%}i{%- endif -%}{%- if "int128" in schema.format -%}128{%- elif "int64" in schema.format -%}64{%- elif "int32" in schema.format -%}32{%- else -%}8{%- endif -%} {%- elif schema.type == "string" -%} {%- if schema.format and schema.format == "binary" -%}Vec{%- else -%}String{%- endif -%} {%- elif schema.type == "number" -%} f64 {%- elif schema.type == "boolean" -%} bool {%- else -%} {%- endif %} {%- elif schema|ref -%} {{schema|ref}} {%- else -%} u8 {%- endif %} {%- endmacro SCHEMA -%} {%- macro IDENTIFIED_SCHEMA(name, schema) -%} {%- if schema.type and schema.type == "object" -%} #[derive(Default,Clone,Debug,serde::Serialize,serde::Deserialize)] pub struct {{name}}{ {{self::SCHEMA(schema=schema)}} } {%- else -%} pub type {{name}} = {{self::SCHEMA(schema=schema)}}; {%- endif -%} {%- endmacro IDENTIFIED_SCHEMA -%} #![allow(non_camel_case_types)] #![allow(unused_variables)] // DON'T EDIT MANUALLY THIS GENERATED CODE use std::collections::HashMap; use serde; use std::future::Future; pub trait Server{ {%- for operation in paths|paths_into_operations %} /// {{operation.method}} {{operation.path}} fn {{operation.function}}(request: {{operation.request_identifier}}) -> impl Future + Send{async{Default::default()}} {%- endfor %} } /* pub struct TestServer{} impl server::Server for TestServer{ {%- for operation in paths|paths_into_operations %} /// {{operation.method}} {{operation.path}} async fn {{operation.function}}(request: {{operation.request_identifier}}) -> {{operation.response_identifier}}{Default::default()} {%- endfor %} } */ {%- for operation in paths|paths_into_operations %} /// Request of {{operation.function}} {{operation.method}} {{operation.path}} #[derive(serde::Serialize,serde::Deserialize,Debug)] pub struct {{operation.request_identifier}}{ {%- if operation.parameters -%} {%- for parameter in operation.parameters %} pub {{parameter.name}}:{%- set inner=self::SCHEMA(schema=parameter.schema) -%}{%- if parameter.required -%}{{inner}}{%- else -%}Option<{{inner}}>{%- endif -%}, {%- endfor %} {%- endif %} {%- if operation.request_body %} pub body:{{operation.request_body.identifier}} {%- endif %} } {%- if operation.request_body %} /// {{operation.request_body.default_content.media_type}} {{ self::IDENTIFIED_SCHEMA(name=operation.request_body.identifier, schema=operation.request_body.default_content.schema) }} {%- endif %} /// Response of {{operation.function}} {{operation.method}} {{operation.path}} #[derive(serde::Serialize,serde::Deserialize,Debug)] pub enum {{operation.response_identifier}}{ {%- for response_key, response in operation.responses %} Status{{response_key}}( {%- if response.content -%} {%- set media=response.content|content_into_media -%} {{self::SCHEMA(schema=media.schema)}} {%- else -%} String {%- endif -%} ), {%- endfor %} } impl Default for {{operation.response_identifier}}{ fn default() -> Self{ {%- for response_key, response in operation.responses %} Self::Status{{response_key}}(Default::default()) {%- break %} {%- endfor %} } } {%- endfor %} {%- for schema_key, schema in components.schemas %} {{ self::IDENTIFIED_SCHEMA(name=schema_key, schema=schema) }} {%- endfor %} use axum; pub fn axum_router_operations()->axum::Router{ axum::Router::new() {%- for operation in paths|paths_into_operations %} .route("{{operation.path|re_replace(from="\{\s*(\w+)\s*\}", to=":$1")}}", axum::routing::{{operation.method}}(| path: axum::extract::Path>, query: axum::extract::Query>, header: axum::http::HeaderMap, {%- if operation.request_body %} body: axum::body::Bytes, {%- endif%} | async move{ let ret=S::{{operation.function}}({{operation.request_identifier}}{ {%- if operation.parameters -%} {%- for parameter in operation.parameters %} r#{{parameter.name}}:match {{parameter.in}}.get("{{parameter.name}}").and_then(|v| v.parse().ok()) {%- if parameter.required -%} {Some(v)=>v, None=>return (axum::http::StatusCode::from_u16(400).unwrap(),[(axum::http::header::CONTENT_TYPE, "text/plain")], format!("parse error: {{parameter.name}} in {{parameter.in}}={:?}", {{parameter.in}}).as_bytes().to_vec())} {%- else -%} {Some(v)=>Some(v), None=>None} {%- endif -%}, {%- endfor %} {%- endif %} {%- if operation.request_body %} body: {%- if "json" in operation.request_body.default_content.media_type -%} match serde_json::from_slice(body.to_vec().as_slice()){Ok(v)=>v,Err(v) => { return (axum::http::StatusCode::INTERNAL_SERVER_ERROR,[(axum::http::header::CONTENT_TYPE, "text/plain")], format!("{:?}", v).as_bytes().to_vec())}} {%- else -%} body.into() {%- endif -%} , {%- endif %} }).await; match ret{ {%- for response_key, response in operation.responses %} {{operation.response_identifier}}::Status{{response_key}}(v) => (axum::http::StatusCode::from_u16({{response_key}}).unwrap(), {%- if response.content -%} {%- set media=response.content|content_into_media -%} [(axum::http::header::CONTENT_TYPE, "{{media.media_type}}")], {%- if "json" in media.media_type-%}serde_json::to_vec_pretty(&v).expect("error serialize response json") {%- elif media.schema.format and media.schema.format == "binary" -%}v {%- else -%}v.as_bytes().to_vec() {%- endif -%} {%- else -%} [(axum::http::header::CONTENT_TYPE, "text/plain")], format!("{{response|get(key="description",default="")}}\n{:?}", v).as_bytes().to_vec() {%- endif -%} ), {%- endfor %} } })) {%- endfor %} .route("/openapi.json", axum::routing::get(|| async move{ r###"{{self()|json_encode()}}"### })) .route("/ui", axum::routing::get(|| async move{ axum::response::Html(r###" SwaggerUI
"###) })) } pub fn axum_router()->axum::Router{ axum::Router::new(). {%- for server in servers %} nest_service("{{server.url}}", axum_router_operations::()) {%- endfor %} } use std::env; pub struct TestServer{} impl Server for TestServer{} #[allow(dead_code)] #[tokio::main] async fn main() { let app = axum_router::() .layer(axum::extract::DefaultBodyLimit::disable()); let port:u16 = env::var("PORT").unwrap_or("8080".to_string()).parse().expect("PORT should be integer"); println!("http://localhost:{}/api/ui", port); let bind=format!("0.0.0.0:{}", port); let listener = tokio::net::TcpListener::bind(bind).await.unwrap(); axum::serve(listener, app) .with_graceful_shutdown(async { tokio::signal::ctrl_c().await.unwrap() }) .await .unwrap(); }