From 20342987079191167141177fef3b59f9e02bdbc7 Mon Sep 17 00:00:00 2001 From: Denux Date: Wed, 2 Oct 2024 14:55:34 +0200 Subject: [PATCH 01/14] added generics acceptance test preparation for v0.2.0 as we have to rework the whole generic feature --- .editorconfig | 2 +- Cargo.toml | 8 +- acceptance/Cargo.lock | 106 ++---- acceptance/Cargo.toml | 6 +- acceptance/generics/Cargo.toml | 21 ++ acceptance/generics/src/main.rs | 44 +++ .../generics/src/open_api.expected.json | 328 ++++++++++++++++++ acceptance/generics/src/routes.rs | 103 ++++++ acceptance/generics/src/schemas.rs | 41 +++ 9 files changed, 581 insertions(+), 78 deletions(-) create mode 100644 acceptance/generics/Cargo.toml create mode 100644 acceptance/generics/src/main.rs create mode 100644 acceptance/generics/src/open_api.expected.json create mode 100644 acceptance/generics/src/routes.rs create mode 100644 acceptance/generics/src/schemas.rs diff --git a/.editorconfig b/.editorconfig index fb8ad34..6e1a4a7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -19,4 +19,4 @@ max_line_length = 120 trim_trailing_whitespace = false [{*.yml,*.yaml}] -indent_size = 2 \ No newline at end of file +indent_size = 2 diff --git a/Cargo.toml b/Cargo.toml index f5af506..4d702b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ resolver = "2" [workspace.package] authors = ["ProbablyClem", "RxDiscovery", "DenuxPlays"] -version = "0.1.14" +version = "0.2.0" edition = "2021" keywords = ["utoipa", "openapi", "swagger", "path", "auto"] description = "Rust Macros to automate the addition of Paths/Schemas to Utoipa crate, simulating Reflection during the compilation phase" @@ -21,11 +21,11 @@ rust-version = "1.74.0" [workspace.dependencies] # Core dependencies -utoipauto-core = { path = "utoipauto-core", version = "0.1" } -utoipauto-macro = { path = "utoipauto-macro", version = "0.1" } +utoipauto-core = { path = "utoipauto-core", version = "0.2" } +utoipauto-macro = { path = "utoipauto-macro", version = "0.2" } # Utoipa -utoipa = { version = "4.2.3", features = ["preserve_path_order"] } +utoipa = { git = "https://github.com/juhaku/utoipa", rev = "abd3572b3544f870a46e4ee749a6d480509a7c63", features = ["preserve_path_order"] } # Macro dependencies quote = "1.0.36" diff --git a/acceptance/Cargo.lock b/acceptance/Cargo.lock index 89d9f57..63fc50b 100644 --- a/acceptance/Cargo.lock +++ b/acceptance/Cargo.lock @@ -16,17 +16,26 @@ dependencies = [ "utoipauto", ] +[[package]] +name = "generics" +version = "0.1.0" +dependencies = [ + "serde_json", + "utoipa", + "utoipauto", +] + [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" [[package]] name = "indexmap" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown", @@ -45,30 +54,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" version = "1.0.86" @@ -80,9 +65,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -95,29 +80,29 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" -version = "1.0.207" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.207" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn", ] [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -127,19 +112,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.109" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -148,15 +123,14 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "utoipa" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23" +version = "5.0.0-beta.0" +source = "git+https://github.com/juhaku/utoipa?rev=abd3572b3544f870a46e4ee749a6d480509a7c63#abd3572b3544f870a46e4ee749a6d480509a7c63" dependencies = [ "indexmap", "serde", @@ -166,30 +140,28 @@ dependencies = [ [[package]] name = "utoipa-gen" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bf0e16c02bc4bf5322ab65f10ab1149bdbcaa782cba66dc7057370a3f8190be" +version = "5.0.0-beta.0" +source = "git+https://github.com/juhaku/utoipa?rev=abd3572b3544f870a46e4ee749a6d480509a7c63#abd3572b3544f870a46e4ee749a6d480509a7c63" dependencies = [ - "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.74", + "syn", ] [[package]] name = "utoipauto" -version = "0.1.14" +version = "0.2.0" dependencies = [ "utoipauto-macro", ] [[package]] name = "utoipauto-core" -version = "0.1.14" +version = "0.2.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn", ] [[package]] @@ -203,16 +175,10 @@ dependencies = [ [[package]] name = "utoipauto-macro" -version = "0.1.14" +version = "0.2.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn", "utoipauto-core", ] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" diff --git a/acceptance/Cargo.toml b/acceptance/Cargo.toml index 20a69dd..33bc032 100644 --- a/acceptance/Cargo.toml +++ b/acceptance/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["crate_segment_path", "folder_in_src"] +members = ["crate_segment_path", "folder_in_src", "generics"] resolver = "2" [workspace.package] @@ -14,5 +14,5 @@ repository = "https://github.com/ProbablyClem/utoipauto" homepage = "https://github.com/ProbablyClem/utoipauto" [workspace.dependencies] -utoipauto = { path = "../utoipauto", version = "0.1" } -utoipa = "4.2.3" +utoipauto = { path = "../utoipauto", version = "0.2" } +utoipa = { git = "https://github.com/juhaku/utoipa", rev = "abd3572b3544f870a46e4ee749a6d480509a7c63", features = ["preserve_path_order"] } diff --git a/acceptance/generics/Cargo.toml b/acceptance/generics/Cargo.toml new file mode 100644 index 0000000..54aa522 --- /dev/null +++ b/acceptance/generics/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "generics" +authors.workspace = true +version.workspace = true +edition.workspace = true +publish.workspace = true +description.workspace = true +readme.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true + +[lints.rust] +unused = "allow" + +[dependencies] +utoipa.workspace = true +utoipauto.workspace = true + +[dev-dependencies] +serde_json = "1.0.128" diff --git a/acceptance/generics/src/main.rs b/acceptance/generics/src/main.rs new file mode 100644 index 0000000..1d0b939 --- /dev/null +++ b/acceptance/generics/src/main.rs @@ -0,0 +1,44 @@ +pub mod routes; +pub mod schemas; + +use routes::*; +use schemas::*; + +use utoipa::OpenApi; +use utoipauto::utoipauto; + +#[utoipauto(paths = "./generics/src")] +#[derive(Debug, OpenApi)] +#[openapi( + info( + title = "Generic Test Api" + ) +)] +pub(crate) struct ApiDoc; + +fn main() { + println!("Our OpenApi documentation {}", ApiDoc::openapi().to_pretty_json().unwrap()); +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::Value; + + pub(crate) const EXPECTED_OPEN_API: &str = include_str!("open_api.expected.json"); + + fn assert_json_eq(actual: &str, expected: &str) { + let actual_value: Value = serde_json::from_str(actual).expect("Invalid JSON in actual"); + let expected_value: Value = serde_json::from_str(expected).expect("Invalid JSON in expected"); + + assert_eq!(actual_value, expected_value, "JSON objects are not equal"); + } + + #[test] + fn test_openapi() { + let open_api = ApiDoc::openapi().to_json().unwrap(); + let expected_value = EXPECTED_OPEN_API; + + assert_json_eq(&open_api, expected_value); + } +} diff --git a/acceptance/generics/src/open_api.expected.json b/acceptance/generics/src/open_api.expected.json new file mode 100644 index 0000000..f77872f --- /dev/null +++ b/acceptance/generics/src/open_api.expected.json @@ -0,0 +1,328 @@ +{ + "components": { + "schemas": { + "ArrayResponse_Person": { + "properties": { + "data": { + "items": { + "properties": { + "age": { + "format": "int32", + "minimum": 0, + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name", + "age" + ], + "type": "object" + }, + "type": "array" + }, + "status": { + "format": "int32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "status", + "data" + ], + "type": "object" + }, + "ArrayResponse_T_N": { + "properties": { + "data": { + "items": { + "$ref": "#/components/schemas/Person" + }, + "type": "array" + }, + "status": { + "format": "int32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "status", + "data" + ], + "type": "object" + }, + "BorrowedResponse_Person": { + "properties": { + "data": { + "properties": { + "age": { + "format": "int32", + "minimum": 0, + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name", + "age" + ], + "type": "object" + }, + "status": { + "format": "int32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "status", + "data" + ], + "type": "object" + }, + "BorrowedResponse_T": { + "properties": { + "data": { + "$ref": "#/components/schemas/Person" + }, + "status": { + "format": "int32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "status", + "data" + ], + "type": "object" + }, + "CombinedResponse_Person": { + "properties": { + "array_response": { + "$ref": "#/components/schemas/ArrayResponse_T_N" + }, + "borrowed_response": { + "$ref": "#/components/schemas/BorrowedResponse_T" + }, + "nested_response": { + "$ref": "#/components/schemas/NestedResponse_T" + } + }, + "required": [ + "nested_response", + "array_response", + "borrowed_response" + ], + "type": "object" + }, + "NestedResponse_Person": { + "properties": { + "response": { + "$ref": "#/components/schemas/Response_T" + } + }, + "required": [ + "response" + ], + "type": "object" + }, + "NestedResponse_T": { + "properties": { + "response": { + "$ref": "#/components/schemas/Response_T" + } + }, + "required": [ + "response" + ], + "type": "object" + }, + "Person": { + "properties": { + "age": { + "format": "int32", + "minimum": 0, + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name", + "age" + ], + "type": "object" + }, + "Response_Person": { + "properties": { + "data": { + "properties": { + "age": { + "format": "int32", + "minimum": 0, + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name", + "age" + ], + "type": "object" + }, + "status": { + "format": "int32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "status", + "data" + ], + "type": "object" + }, + "Response_T": { + "properties": { + "data": { + "$ref": "#/components/schemas/Person" + }, + "status": { + "format": "int32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "status", + "data" + ], + "type": "object" + } + } + }, + "info": { + "contact": { + "name": "ProbablyClem" + }, + "description": "A collection of crates to test utoipauto.", + "license": { + "name": "MIT OR Apache-2.0" + }, + "title": "Generic Test Api", + "version": "0.1.0" + }, + "openapi": "3.1.0", + "paths": { + "/array_persons": { + "get": { + "operationId": "get_array_persons", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArrayResponse_Person" + } + } + }, + "description": "An ArrayResponse" + } + }, + "tags": [ + "crate::routes" + ] + } + }, + "/borrowed_persons": { + "get": { + "operationId": "get_borrowed_persons", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BorrowedResponse_Person" + } + } + }, + "description": "A BorrowedResponse<'static, Person>" + } + }, + "tags": [ + "crate::routes" + ] + } + }, + "/combined_persons": { + "get": { + "operationId": "get_combined_persons", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CombinedResponse_Person" + } + } + }, + "description": "A CombinedResponse<'static, Person, 3>" + } + }, + "tags": [ + "crate::routes" + ] + } + }, + "/nested_persons": { + "get": { + "operationId": "get_nested_persons", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NestedResponse_Person" + } + } + }, + "description": "A NestedResponse" + } + }, + "tags": [ + "crate::routes" + ] + } + }, + "/persons": { + "get": { + "operationId": "get_persons", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Response_Person" + } + } + }, + "description": "A Response" + } + }, + "tags": [ + "crate::routes" + ] + } + } + } +} diff --git a/acceptance/generics/src/routes.rs b/acceptance/generics/src/routes.rs new file mode 100644 index 0000000..4526c75 --- /dev/null +++ b/acceptance/generics/src/routes.rs @@ -0,0 +1,103 @@ +use crate::schemas::{ArrayResponse, BorrowedResponse, CombinedResponse, NestedResponse, Person, Response}; + +#[utoipa::path(get, + path = "/persons", + responses( +(status = 200, description = "A Response", content_type = "application/json", body = Response), + ) +)] +pub fn get_persons() -> Response { + Response { + status: 200, + data: Person { + name: "John Doe".to_string(), + age: 30, + }, + } +} + +#[utoipa::path(get, + path = "/nested_persons", + responses( +(status = 200, description = "A NestedResponse", content_type = "application/json", body = NestedResponse), + ) +)] +pub fn get_nested_persons() -> NestedResponse { + NestedResponse { + response: Response { + status: 200, + data: Person { + name: "John Doe".to_string(), + age: 30, + }, + }, + } +} + +#[utoipa::path(get, + path = "/array_persons", + responses( +(status = 200, description = "An ArrayResponse", content_type = "application/json", body = ArrayResponse), + ) +)] +pub fn get_array_persons() -> ArrayResponse { + ArrayResponse { + status: 200, + data: [ + Person { name: "John Doe".to_string(), age: 30 }, + Person { name: "Jane Doe".to_string(), age: 25 }, + Person { name: "Jim Doe".to_string(), age: 20 }, + ], + } +} + +#[utoipa::path(get, + path = "/borrowed_persons", + responses( +(status = 200, description = "A BorrowedResponse<'static, Person>", content_type = "application/json", body = BorrowedResponse<'static, Person>), + ) +)] +pub fn get_borrowed_persons() -> BorrowedResponse<'static, Person> { + let person = Box::new(Person { + name: "John Doe".to_string(), + age: 30, + }); + BorrowedResponse { + status: 200, + data: Box::leak(person), + } +} + +#[utoipa::path(get, + path = "/combined_persons", + responses( +(status = 200, description = "A CombinedResponse<'static, Person, 3>", content_type = "application/json", body = CombinedResponse<'static, Person, 3>), + ) +)] +pub fn get_combined_persons() -> CombinedResponse<'static, Person, 3> { + let person = Box::new(Person { + name: "John Doe".to_string(), + age: 30, + }); + let person_ref = Box::leak(person); + CombinedResponse { + nested_response: NestedResponse { + response: Response { + status: 200, + data: person_ref.clone(), + }, + }, + array_response: ArrayResponse { + status: 200, + data: [ + person_ref.clone(), + Person { name: "Jane Doe".to_string(), age: 25 }, + Person { name: "Jim Doe".to_string(), age: 20 }, + ], + }, + borrowed_response: BorrowedResponse { + status: 200, + data: person_ref, + }, + } +} diff --git a/acceptance/generics/src/schemas.rs b/acceptance/generics/src/schemas.rs new file mode 100644 index 0000000..5c1a61f --- /dev/null +++ b/acceptance/generics/src/schemas.rs @@ -0,0 +1,41 @@ +use utoipa::ToSchema; + +#[derive(Debug, ToSchema)] +pub struct Response { + pub status: u16, + pub data: T, +} + +#[derive(Debug, Clone, ToSchema)] +pub struct Person { + pub name: String, + pub age: u8, +} + +// Nested Generics +#[derive(Debug, ToSchema)] +pub struct NestedResponse { + pub response: Response, +} + +// Const Generics +#[derive(Debug, ToSchema)] +pub struct ArrayResponse { + pub status: u16, + pub data: [T; N], +} + +// Lifetime Generics +#[derive(Debug, ToSchema)] +pub struct BorrowedResponse<'a, T: ToSchema> { + pub status: u16, + pub data: &'a T, +} + +// Combined Generics +#[derive(Debug, ToSchema)] +pub struct CombinedResponse<'a, T: ToSchema, const N: usize> { + pub nested_response: NestedResponse, + pub array_response: ArrayResponse, + pub borrowed_response: BorrowedResponse<'a, T>, +} From ab58653fdde38853329c6c933e7fada98e836bd6 Mon Sep 17 00:00:00 2001 From: Denux Date: Sat, 5 Oct 2024 15:30:22 +0200 Subject: [PATCH 02/14] added more acceptance tests --- .github/workflows/ci.yml | 2 +- Cargo.toml | 4 +- acceptance/Cargo.lock | 17 +- acceptance/Cargo.toml | 4 +- acceptance/responses/Cargo.toml | 19 +++ acceptance/responses/src/main.rs | 44 +++++ .../responses/src/open_api.expected.json | 159 ++++++++++++++++++ acceptance/responses/src/response.rs | 40 +++++ acceptance/responses/src/routes.rs | 25 +++ 9 files changed, 305 insertions(+), 9 deletions(-) create mode 100644 acceptance/responses/Cargo.toml create mode 100644 acceptance/responses/src/main.rs create mode 100644 acceptance/responses/src/open_api.expected.json create mode 100644 acceptance/responses/src/response.rs create mode 100644 acceptance/responses/src/routes.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a092311..9a897da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rust: [ "1.74.0", "stable", "nightly" ] + rust: [ "1.75.0", "stable", "nightly" ] steps: - uses: actions/checkout@v4 - name: install toolchain diff --git a/Cargo.toml b/Cargo.toml index 4d702b8..fe11d64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/ProbablyClem/utoipauto" homepage = "https://github.com/ProbablyClem/utoipauto" -rust-version = "1.74.0" +rust-version = "1.75.0" [workspace.dependencies] # Core dependencies @@ -25,7 +25,7 @@ utoipauto-core = { path = "utoipauto-core", version = "0.2" } utoipauto-macro = { path = "utoipauto-macro", version = "0.2" } # Utoipa -utoipa = { git = "https://github.com/juhaku/utoipa", rev = "abd3572b3544f870a46e4ee749a6d480509a7c63", features = ["preserve_path_order"] } +utoipa = { git = "https://github.com/juhaku/utoipa", rev = "ceb543e4d8ceeea47416989647b4c8dfc46cac6d", features = ["preserve_path_order"] } # Macro dependencies quote = "1.0.36" diff --git a/acceptance/Cargo.lock b/acceptance/Cargo.lock index 63fc50b..aa360d7 100644 --- a/acceptance/Cargo.lock +++ b/acceptance/Cargo.lock @@ -72,6 +72,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "responses" +version = "0.1.0" +dependencies = [ + "serde_json", + "utoipa", + "utoipauto", +] + [[package]] name = "ryu" version = "1.0.18" @@ -129,8 +138,8 @@ checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "utoipa" -version = "5.0.0-beta.0" -source = "git+https://github.com/juhaku/utoipa?rev=abd3572b3544f870a46e4ee749a6d480509a7c63#abd3572b3544f870a46e4ee749a6d480509a7c63" +version = "5.0.0-rc.0" +source = "git+https://github.com/juhaku/utoipa?rev=ceb543e4d8ceeea47416989647b4c8dfc46cac6d#ceb543e4d8ceeea47416989647b4c8dfc46cac6d" dependencies = [ "indexmap", "serde", @@ -140,8 +149,8 @@ dependencies = [ [[package]] name = "utoipa-gen" -version = "5.0.0-beta.0" -source = "git+https://github.com/juhaku/utoipa?rev=abd3572b3544f870a46e4ee749a6d480509a7c63#abd3572b3544f870a46e4ee749a6d480509a7c63" +version = "5.0.0-rc.0" +source = "git+https://github.com/juhaku/utoipa?rev=ceb543e4d8ceeea47416989647b4c8dfc46cac6d#ceb543e4d8ceeea47416989647b4c8dfc46cac6d" dependencies = [ "proc-macro2", "quote", diff --git a/acceptance/Cargo.toml b/acceptance/Cargo.toml index 33bc032..5acca7f 100644 --- a/acceptance/Cargo.toml +++ b/acceptance/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["crate_segment_path", "folder_in_src", "generics"] +members = ["crate_segment_path", "folder_in_src", "generics", "responses"] resolver = "2" [workspace.package] @@ -15,4 +15,4 @@ homepage = "https://github.com/ProbablyClem/utoipauto" [workspace.dependencies] utoipauto = { path = "../utoipauto", version = "0.2" } -utoipa = { git = "https://github.com/juhaku/utoipa", rev = "abd3572b3544f870a46e4ee749a6d480509a7c63", features = ["preserve_path_order"] } +utoipa = { git = "https://github.com/juhaku/utoipa", rev = "ceb543e4d8ceeea47416989647b4c8dfc46cac6d", features = ["preserve_path_order"] } diff --git a/acceptance/responses/Cargo.toml b/acceptance/responses/Cargo.toml new file mode 100644 index 0000000..4bc2368 --- /dev/null +++ b/acceptance/responses/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "responses" +authors.workspace = true +version.workspace = true +edition.workspace = true +publish.workspace = true +description.workspace = true +readme.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true + +[lints.rust] +unused = "allow" + +[dependencies] +utoipa.workspace = true +utoipauto.workspace = true +serde_json = "1.0.128" diff --git a/acceptance/responses/src/main.rs b/acceptance/responses/src/main.rs new file mode 100644 index 0000000..3b46312 --- /dev/null +++ b/acceptance/responses/src/main.rs @@ -0,0 +1,44 @@ +pub mod response; +mod routes; + +use utoipa::OpenApi; +use utoipauto::utoipauto; + +#[utoipauto(paths = "./responses/src")] +#[derive(Debug, OpenApi)] +#[openapi( + info( + title = "Responses Test Api" + ) +)] +pub(crate) struct ApiDoc; + +fn main() { + println!("Our OpenApi documentation {}", ApiDoc::openapi().to_pretty_json().unwrap()); +} + +#[cfg(test)] +mod tests { + use crate::ApiDoc; + use serde_json::Value; + use utoipa::OpenApi; + + pub(crate) const EXPECTED_OPEN_API: &str = include_str!("open_api.expected.json"); + + fn assert_json_eq(actual: &str, expected: &str) { + let actual_value: Value = serde_json::from_str(actual).expect("Invalid JSON in actual"); + let expected_value: Value = serde_json::from_str(expected).expect("Invalid JSON in expected"); + + println!("Actual: {}", actual); + + assert_eq!(actual_value, expected_value, "JSON objects are not equal"); + } + + #[test] + fn test_open_api() { + let open_api = ApiDoc::openapi().to_json().unwrap(); + let expected_value = EXPECTED_OPEN_API; + + assert_json_eq(&open_api, expected_value); + } +} diff --git a/acceptance/responses/src/open_api.expected.json b/acceptance/responses/src/open_api.expected.json new file mode 100644 index 0000000..6e3d15b --- /dev/null +++ b/acceptance/responses/src/open_api.expected.json @@ -0,0 +1,159 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Responses Test Api", + "description": "A collection of crates to test utoipauto.", + "contact": { + "name": "ProbablyClem" + }, + "license": { + "name": "MIT OR Apache-2.0" + }, + "version": "0.1.0" + }, + "paths": { + "/api/user": { + "get": { + "tags": [ + "crate::routes" + ], + "operationId": "get_user", + "responses": { + "200": { + "description": "Success response", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Success response", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "string" + } + } + } + } + } + }, + "400": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequest" + } + } + } + }, + "404": { + "description": "" + } + } + } + }, + "/api/person": { + "get": { + "tags": [ + "crate::routes" + ], + "operationId": "get_person", + "responses": { + "200": { + "$ref": "#/components/responses/Person" + } + } + } + } + }, + "components": { + "schemas": { + "Admin": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + } + } + }, + "Admin2": { + "type": "object", + "required": [ + "name", + "id" + ], + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "name": { + "type": "string" + } + } + }, + "BadRequest": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string" + } + } + } + }, + "responses": { + "Person": { + "description": "", + "content": { + "application/vnd-custom-v1+json": { + "schema": { + "$ref": "#/components/schemas/Admin" + }, + "examples": { + "Person1": { + "value": { + "name": "name1" + } + }, + "Person2": { + "value": { + "name": "name2" + } + } + } + }, + "application/vnd-custom-v2+json": { + "schema": { + "type": "object", + "required": [ + "name", + "id" + ], + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "name": { + "type": "string" + } + } + }, + "example": { + "id": 1, + "name": "name3" + } + } + } + } + } + } +} diff --git a/acceptance/responses/src/response.rs b/acceptance/responses/src/response.rs new file mode 100644 index 0000000..d610bce --- /dev/null +++ b/acceptance/responses/src/response.rs @@ -0,0 +1,40 @@ +#[derive(utoipa::ToSchema)] +pub struct Admin { + pub name: String, +} +#[derive(utoipa::ToSchema)] +pub struct Admin2 { + pub name: String, + pub id: i32, +} + +#[derive(utoipa::ToResponse)] +pub enum Person { + #[response(examples( + ("Person1" = (value = json!({"name": "name1"}))), + ("Person2" = (value = json!({"name": "name2"}))) + ))] + Admin(#[content("application/vnd-custom-v1+json")] Admin), + + #[response(example = json!({"name": "name3", "id": 1}))] + Admin2(#[content("application/vnd-custom-v2+json")] + #[to_schema] Admin2), +} + +#[derive(utoipa::ToSchema)] +pub struct BadRequest { + message: String, +} + +#[derive(utoipa::IntoResponses)] +pub enum UserResponses { + /// Success response + #[response(status = 200)] + Success { value: String }, + + #[response(status = 404)] + NotFound, + + #[response(status = 400)] + BadRequest(BadRequest), +} diff --git a/acceptance/responses/src/routes.rs b/acceptance/responses/src/routes.rs new file mode 100644 index 0000000..521ac26 --- /dev/null +++ b/acceptance/responses/src/routes.rs @@ -0,0 +1,25 @@ +use crate::response::{Admin, Person, UserResponses}; + +#[utoipa::path( + get, + path = "/api/user", + responses( + UserResponses + ) +)] +fn get_user() -> UserResponses { + UserResponses::NotFound +} + +#[utoipa::path( + get, + path = "/api/person", + responses( + (status = 200, response = Person) + ) +)] +fn get_person() -> Person { + Person::Admin(Admin { + name: "name1".to_string(), + }) +} From 9751f05bdec070005eb606e6fdaf3de4b25f0d2e Mon Sep 17 00:00:00 2001 From: Denux Date: Sat, 5 Oct 2024 15:31:37 +0200 Subject: [PATCH 03/14] updated utoipa --- Cargo.toml | 2 +- acceptance/Cargo.lock | 4 ++-- acceptance/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fe11d64..f38e708 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ utoipauto-core = { path = "utoipauto-core", version = "0.2" } utoipauto-macro = { path = "utoipauto-macro", version = "0.2" } # Utoipa -utoipa = { git = "https://github.com/juhaku/utoipa", rev = "ceb543e4d8ceeea47416989647b4c8dfc46cac6d", features = ["preserve_path_order"] } +utoipa = { git = "https://github.com/juhaku/utoipa", rev = "22ea68da5134f478283feb212cbb584ed8bac612", features = ["preserve_path_order"] } # Macro dependencies quote = "1.0.36" diff --git a/acceptance/Cargo.lock b/acceptance/Cargo.lock index aa360d7..900c65b 100644 --- a/acceptance/Cargo.lock +++ b/acceptance/Cargo.lock @@ -139,7 +139,7 @@ checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "utoipa" version = "5.0.0-rc.0" -source = "git+https://github.com/juhaku/utoipa?rev=ceb543e4d8ceeea47416989647b4c8dfc46cac6d#ceb543e4d8ceeea47416989647b4c8dfc46cac6d" +source = "git+https://github.com/juhaku/utoipa?rev=22ea68da5134f478283feb212cbb584ed8bac612#22ea68da5134f478283feb212cbb584ed8bac612" dependencies = [ "indexmap", "serde", @@ -150,7 +150,7 @@ dependencies = [ [[package]] name = "utoipa-gen" version = "5.0.0-rc.0" -source = "git+https://github.com/juhaku/utoipa?rev=ceb543e4d8ceeea47416989647b4c8dfc46cac6d#ceb543e4d8ceeea47416989647b4c8dfc46cac6d" +source = "git+https://github.com/juhaku/utoipa?rev=22ea68da5134f478283feb212cbb584ed8bac612#22ea68da5134f478283feb212cbb584ed8bac612" dependencies = [ "proc-macro2", "quote", diff --git a/acceptance/Cargo.toml b/acceptance/Cargo.toml index 5acca7f..8b0f57e 100644 --- a/acceptance/Cargo.toml +++ b/acceptance/Cargo.toml @@ -15,4 +15,4 @@ homepage = "https://github.com/ProbablyClem/utoipauto" [workspace.dependencies] utoipauto = { path = "../utoipauto", version = "0.2" } -utoipa = { git = "https://github.com/juhaku/utoipa", rev = "ceb543e4d8ceeea47416989647b4c8dfc46cac6d", features = ["preserve_path_order"] } +utoipa = { git = "https://github.com/juhaku/utoipa", rev = "22ea68da5134f478283feb212cbb584ed8bac612", features = ["preserve_path_order"] } From 96cb39543e6534daa91b84d8e6ee4ee48fb85e84 Mon Sep 17 00:00:00 2001 From: Denux Date: Sat, 5 Oct 2024 17:35:18 +0200 Subject: [PATCH 04/14] removed tests Note: for now as we will create new ones when the core is restructured --- .../tests/default_features/const_generics.rs | 45 ------ .../controllers/controller1.rs | 9 -- .../controllers/controller2.rs | 4 - .../controllers/controller3.rs | 5 - .../tests/default_features/controllers/mod.rs | 3 - utoipauto/tests/default_features/generics.rs | 100 ------------- .../default_features/lifetime_generics.rs | 41 ------ utoipauto/tests/default_features/mod.rs | 7 - utoipauto/tests/default_features/models.rs | 63 --------- utoipauto/tests/default_features/test.rs | 133 ------------------ .../tests/default_features/type_generics.rs | 35 ----- .../tests/generic_full_path/const_generics.rs | 13 -- .../generic_full_path/even_more_schemas.rs | 3 - utoipauto/tests/generic_full_path/mod.rs | 6 - .../tests/generic_full_path/more_schemas.rs | 10 -- .../generic_full_path/partial_imports.rs | 22 --- utoipauto/tests/generic_full_path/schemas.rs | 31 ---- utoipauto/tests/generic_full_path/test.rs | 26 ---- utoipauto/tests/test.rs | 4 - 19 files changed, 560 deletions(-) delete mode 100644 utoipauto/tests/default_features/const_generics.rs delete mode 100644 utoipauto/tests/default_features/controllers/controller1.rs delete mode 100644 utoipauto/tests/default_features/controllers/controller2.rs delete mode 100644 utoipauto/tests/default_features/controllers/controller3.rs delete mode 100644 utoipauto/tests/default_features/controllers/mod.rs delete mode 100644 utoipauto/tests/default_features/generics.rs delete mode 100644 utoipauto/tests/default_features/lifetime_generics.rs delete mode 100644 utoipauto/tests/default_features/mod.rs delete mode 100644 utoipauto/tests/default_features/models.rs delete mode 100644 utoipauto/tests/default_features/test.rs delete mode 100644 utoipauto/tests/default_features/type_generics.rs delete mode 100644 utoipauto/tests/generic_full_path/const_generics.rs delete mode 100644 utoipauto/tests/generic_full_path/even_more_schemas.rs delete mode 100644 utoipauto/tests/generic_full_path/mod.rs delete mode 100644 utoipauto/tests/generic_full_path/more_schemas.rs delete mode 100644 utoipauto/tests/generic_full_path/partial_imports.rs delete mode 100644 utoipauto/tests/generic_full_path/schemas.rs delete mode 100644 utoipauto/tests/generic_full_path/test.rs delete mode 100644 utoipauto/tests/test.rs diff --git a/utoipauto/tests/default_features/const_generics.rs b/utoipauto/tests/default_features/const_generics.rs deleted file mode 100644 index bfdcdd0..0000000 --- a/utoipauto/tests/default_features/const_generics.rs +++ /dev/null @@ -1,45 +0,0 @@ -use utoipa::{OpenApi, ToSchema}; -use utoipauto_macro::utoipauto; - -#[derive(ToSchema)] -#[aliases(ConstGenericStructSchema0 = ConstGenericStructSchema<0>)] -pub struct ConstGenericStructSchema { - foo: [u16; N], -} - -#[derive(ToSchema)] -#[aliases(DoubleConstGenericStructSchema0 = DoubleConstGenericStructSchema<0, 0>)] -pub struct DoubleConstGenericStructSchema { - foo: [u16; N], - bar: [u16; N2], -} - -#[derive(ToSchema)] -#[aliases(ConstGenericEnumSchema0 = ConstGenericEnumSchema<0>)] -pub enum ConstGenericEnumSchema { - Foo([u16; N]), -} - -#[derive(ToSchema)] -#[aliases(DoubleConstGenericEnumSchema0 = DoubleConstGenericEnumSchema<0, 0>)] -pub enum DoubleConstGenericEnumSchema { - Foo([u16; N], [u16; N2]), -} - -/// Discover schema with const generics -#[utoipauto(paths = "./utoipauto/tests/default_features/const_generics.rs")] -#[derive(OpenApi)] -#[openapi(info(title = "Const Generic API", version = "1.0.0"))] -pub struct ConstGenericApiDocs {} - -#[test] -fn test_const_generics() { - assert_eq!( - ConstGenericApiDocs::openapi() - .components - .expect("no components") - .schemas - .len(), - 4 - ) -} diff --git a/utoipauto/tests/default_features/controllers/controller1.rs b/utoipauto/tests/default_features/controllers/controller1.rs deleted file mode 100644 index ca16e3c..0000000 --- a/utoipauto/tests/default_features/controllers/controller1.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![allow(dead_code)] // This code is used in the tests -use utoipauto_macro::utoipa_ignore; - -#[utoipa::path(post, path = "/route1")] -pub fn route1() {} - -#[utoipa_ignore] -#[utoipa::path(post, path = "/route-ignored")] -pub fn route_ignored() {} diff --git a/utoipauto/tests/default_features/controllers/controller2.rs b/utoipauto/tests/default_features/controllers/controller2.rs deleted file mode 100644 index 43480f7..0000000 --- a/utoipauto/tests/default_features/controllers/controller2.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![allow(dead_code)] // This code is used in the tests - -#[utoipa::path(post, path = "/route3")] -pub fn route3() {} diff --git a/utoipauto/tests/default_features/controllers/controller3.rs b/utoipauto/tests/default_features/controllers/controller3.rs deleted file mode 100644 index 11888aa..0000000 --- a/utoipauto/tests/default_features/controllers/controller3.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![allow(dead_code)] -use utoipauto_macro::test_handler; - -#[test_handler] -pub fn route_custom() {} diff --git a/utoipauto/tests/default_features/controllers/mod.rs b/utoipauto/tests/default_features/controllers/mod.rs deleted file mode 100644 index 9b43b4c..0000000 --- a/utoipauto/tests/default_features/controllers/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod controller1; -pub mod controller2; -pub mod controller3; diff --git a/utoipauto/tests/default_features/generics.rs b/utoipauto/tests/default_features/generics.rs deleted file mode 100644 index c866e4f..0000000 --- a/utoipauto/tests/default_features/generics.rs +++ /dev/null @@ -1,100 +0,0 @@ -use utoipa::OpenApi; -use utoipauto_macro::utoipauto; - -/// Combined Struct - type & const -#[derive(utoipa::ToSchema)] -#[aliases( - TypeGenericAndConstGenericStructSchemaU32 = TypeGenericAndConstGenericStructSchema, - TypeGenericAndConstGenericStructSchemaU64 = TypeGenericAndConstGenericStructSchema, -)] -pub struct TypeGenericAndConstGenericStructSchema { - foo: T, - bar: [u16; N], -} - -/// Combined Struct - lifetime & type -#[derive(utoipa::ToSchema)] -#[aliases( - LifetimeAndTypeGenericGenericStructSchemaU32 = LifetimeAndTypeGenericGenericStructSchema<'a, core::primitive::u32>, - LifetimeAndTypeGenericGenericStructSchemaU64 = LifetimeAndTypeGenericGenericStructSchema<'a, core::primitive::u64>, -)] -pub struct LifetimeAndTypeGenericGenericStructSchema<'a, T> { - foo: &'a str, - bar: T, -} - -/// Combined Struct - lifetime & const -#[derive(utoipa::ToSchema)] -#[aliases(LifetimeAndConstGenericGenericStructSchema2 = LifetimeAndConstGenericGenericStructSchema<'a, 2>)] -pub struct LifetimeAndConstGenericGenericStructSchema<'a, const N: usize> { - foo: &'a str, - bar: [u16; N], -} - -/// Combined Struct - lifetime & const & type -#[derive(utoipa::ToSchema)] -#[aliases( - LifetimeAndConstAndTypeGenericGenericStructSchema1 = LifetimeAndConstAndTypeGenericGenericStructSchema<'a, 5, 1, core::primitive::u32>, - LifetimeAndConstAndTypeGenericGenericStructSchema2 = LifetimeAndConstAndTypeGenericGenericStructSchema<'a, 6, 2, core::primitive::u64>, -)] -pub struct LifetimeAndConstAndTypeGenericGenericStructSchema<'a, const N: usize, const N2: usize, T> { - a: &'a str, - foo: [u16; N], - bar: [u16; N2], - t: T, -} - -/// Combined Enum - type & const -#[derive(utoipa::ToSchema)] -#[aliases( - TypeGenericAndConstGenericEnumSchemaU32 = TypeGenericAndConstGenericEnumSchema, - TypeGenericAndConstGenericEnumSchemaU64 = TypeGenericAndConstGenericEnumSchema, -)] -pub enum TypeGenericAndConstGenericEnumSchema { - Foo(T, [u16; N]), -} - -/// Combined Enum - lifetime & type -#[derive(utoipa::ToSchema)] -#[aliases( - LifetimeAndTypeGenericGenericEnumSchemaU32 = LifetimeAndTypeGenericGenericEnumSchema<'a, core::primitive::u32>, - LifetimeAndTypeGenericGenericEnumSchemaU64 = LifetimeAndTypeGenericGenericEnumSchema<'a, core::primitive::u64>, -)] -pub enum LifetimeAndTypeGenericGenericEnumSchema<'a, T> { - Foo(&'a str, T), -} - -/// Combined Enum - lifetime & const -#[derive(utoipa::ToSchema)] -#[aliases(LifetimeAndConstGenericGenericEnumSchema2 = LifetimeAndConstGenericGenericEnumSchema<'a, 2>)] -pub enum LifetimeAndConstGenericGenericEnumSchema<'a, const N: usize> { - Foo(&'a str, [u16; N]), -} - -/// Combined Enum - lifetime & const & type -#[derive(utoipa::ToSchema)] -#[aliases( - LifetimeAndConstAndTypeGenericGenericEnumSchema1 = LifetimeAndConstAndTypeGenericGenericEnumSchema<'a, 5, 1, core::primitive::u32>, - LifetimeAndConstAndTypeGenericGenericEnumSchema2 = LifetimeAndConstAndTypeGenericGenericEnumSchema<'a, 6, 2, core::primitive::u64>, -)] -pub enum LifetimeAndConstAndTypeGenericGenericEnumSchema<'a, const N: usize, const N2: usize, T> { - Foo(&'a str, [u16; N], [u16; N2], T), -} - -/// Discover schema with generics -#[utoipauto(paths = "./utoipauto/tests/default_features/generics.rs")] -#[derive(OpenApi)] -#[openapi(info(title = "Const Generic API", version = "1.0.0"))] -pub struct GenericsApiDocs {} - -#[test] -fn test_generics() { - assert_eq!( - GenericsApiDocs::openapi() - .components - .expect("no components") - .schemas - .len(), - 14 - ) -} diff --git a/utoipauto/tests/default_features/lifetime_generics.rs b/utoipauto/tests/default_features/lifetime_generics.rs deleted file mode 100644 index 8e4c6fc..0000000 --- a/utoipauto/tests/default_features/lifetime_generics.rs +++ /dev/null @@ -1,41 +0,0 @@ -use utoipa::{OpenApi, ToSchema}; -use utoipauto_macro::utoipauto; - -#[derive(ToSchema)] -pub struct LifetimeStructSchema<'a> { - foo: &'a str, -} - -#[derive(ToSchema)] -pub struct DoubleLifetimeStructSchema<'a, 'b> { - foo: &'a str, - bar: &'b str, -} - -#[derive(ToSchema)] -pub enum LifetimeEnumSchema<'a> { - Foo(&'a str), -} - -#[derive(ToSchema)] -pub enum DoubleLifetimeEnumSchema<'a, 'b> { - Foo(&'a str, &'b str), -} - -/// Discover schema with lifetime generics -#[utoipauto(paths = "./utoipauto/tests/default_features/lifetime_generics.rs")] -#[derive(OpenApi)] -#[openapi(info(title = "Lifetimes API", version = "1.0.0"))] -pub struct LifetimesApiDocs {} - -#[test] -fn test_lifetimes() { - assert_eq!( - LifetimesApiDocs::openapi() - .components - .expect("no components") - .schemas - .len(), - 4 - ) -} diff --git a/utoipauto/tests/default_features/mod.rs b/utoipauto/tests/default_features/mod.rs deleted file mode 100644 index 3502641..0000000 --- a/utoipauto/tests/default_features/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod const_generics; -pub mod controllers; -pub mod generics; -pub mod lifetime_generics; -pub mod models; -pub mod test; -pub mod type_generics; diff --git a/utoipauto/tests/default_features/models.rs b/utoipauto/tests/default_features/models.rs deleted file mode 100644 index 45a599b..0000000 --- a/utoipauto/tests/default_features/models.rs +++ /dev/null @@ -1,63 +0,0 @@ -#![allow(dead_code)] // This code is used in the tests -use utoipa::{ - openapi::{ObjectBuilder, RefOr, Response, ResponseBuilder, Schema, SchemaType}, - ToResponse, ToSchema, -}; -use utoipauto_macro::utoipa_ignore; - -#[derive(ToSchema)] -pub struct ModelSchema; -#[derive(ToResponse)] -pub struct ModelResponse; - -#[utoipa_ignore] -#[derive(ToSchema)] -pub struct IgnoredModelSchema; - -// Manual implementation of ToSchema -pub struct ModelSchemaImpl; - -impl<'s> ToSchema<'s> for ModelSchemaImpl { - fn schema() -> (&'s str, RefOr) { - ( - "ModelSchemaImpl", - ObjectBuilder::new().schema_type(SchemaType::String).into(), - ) - } -} - -// Manual implementation of utoipa::ToSchema -pub struct ModelSchemaImplFullName; - -impl<'s> utoipa::ToSchema<'s> for ModelSchemaImplFullName { - fn schema() -> (&'s str, RefOr) { - ( - "ModelSchemaImplFullName", - ObjectBuilder::new().schema_type(SchemaType::String).into(), - ) - } -} - -// Manual implementation of ToSchema -pub struct ModelResponseImpl; - -impl<'s> ToResponse<'s> for ModelResponseImpl { - fn response() -> (&'s str, RefOr) { - ( - "ModelResponseImpl", - ResponseBuilder::new().description("A manual response").into(), - ) - } -} - -// Manual implementation of utoipa::ToResponse -pub struct ModelResponseImplFullName; - -impl<'s> utoipa::ToResponse<'s> for ModelResponseImplFullName { - fn response() -> (&'s str, RefOr) { - ( - "ModelResponseImplFullName", - ResponseBuilder::new().description("A manual response").into(), - ) - } -} diff --git a/utoipauto/tests/default_features/test.rs b/utoipauto/tests/default_features/test.rs deleted file mode 100644 index 9ba0427..0000000 --- a/utoipauto/tests/default_features/test.rs +++ /dev/null @@ -1,133 +0,0 @@ -use utoipa::OpenApi; - -use utoipauto::utoipauto; - -use crate::default_features::controllers; -use crate::default_features::type_generics::GenericSchema; -use crate::default_features::type_generics::NonGenericSchema; -use crate::default_features::type_generics::NonGenericSchema2; - -// Discover from multiple controllers -#[utoipauto( - paths = "( crate::controllers::controller1 => ./utoipauto/tests/default_features/controllers/controller1.rs) ; ( crate::controllers::controller2 => ./utoipauto/tests/default_features/controllers/controller2.rs )" -)] -#[derive(OpenApi)] -#[openapi(info(title = "Percentage API", version = "1.0.0"))] -pub struct MultiControllerApiDocs {} - -#[test] -fn test_path_import() { - assert_eq!(MultiControllerApiDocs::openapi().paths.paths.len(), 2) -} - -/// Discover from a single controller -#[utoipauto( - paths = "( crate::controllers::controller1 => ./utoipauto/tests/default_features/controllers/controller1.rs)" -)] -#[derive(OpenApi)] -#[openapi(info(title = "Percentage API", version = "1.0.0"))] -pub struct SingleControllerApiDocs {} - -#[test] -fn test_ignored_path() { - assert_eq!(SingleControllerApiDocs::openapi().paths.paths.len(), 1) -} - -/// Discover with manual path -#[utoipauto(paths = "./utoipauto/tests/default_features/controllers/controller1.rs")] -#[derive(OpenApi)] -#[openapi( - info(title = "Percentage API", version = "1.0.0"), - paths(controllers::controller2::route3) -)] -pub struct SingleControllerManualPathApiDocs {} - -#[test] -fn test_manual_path() { - assert_eq!(SingleControllerManualPathApiDocs::openapi().paths.paths.len(), 2) -} - -/// Discover from a module root -#[utoipauto(paths = "( crate::controllers => ./utoipauto/tests/default_features/controllers)")] -#[derive(OpenApi)] -#[openapi(info(title = "Percentage API", version = "1.0.0"))] -pub struct ModuleApiDocs {} - -#[test] -fn test_module_import_path() { - assert_eq!(ModuleApiDocs::openapi().paths.paths.len(), 2) -} - -/// Discover from the crate root -#[utoipauto(paths = "./utoipauto/tests/default_features")] -#[derive(OpenApi)] -#[openapi(info(title = "Percentage API", version = "1.0.0"))] -pub struct CrateApiDocs {} - -#[test] -fn test_crate_import_path() { - assert_eq!(CrateApiDocs::openapi().paths.paths.len(), 2) -} - -// Discover from multiple controllers new syntax -#[utoipauto( - paths = "./utoipauto/tests/default_features/controllers/controller1.rs, ./utoipauto/tests/default_features/controllers/controller2.rs" -)] -#[derive(OpenApi)] -#[openapi(info(title = "Percentage API", version = "1.0.0"))] -pub struct MultiControllerNoModuleApiDocs {} - -#[test] -fn test_path_import_no_module() { - assert_eq!(MultiControllerNoModuleApiDocs::openapi().paths.paths.len(), 2) -} - -// Discover from multiple controllers new syntax -#[utoipauto(paths = "./utoipauto/tests/default_features/models.rs")] -#[derive(OpenApi)] -#[openapi(info(title = "Percentage API", version = "1.0.0"))] -pub struct ModelsImportApiDocs {} - -#[test] -fn test_path_import_schema() { - assert_eq!( - ModelsImportApiDocs::openapi() - .components - .expect("no components") - .schemas - .len(), - 3, // 1 derive, 1 manual, 1 manual with utoipa::ToSchema - ) -} - -// Discover from multiple controllers new syntax -#[utoipauto(paths = "./utoipauto/tests/default_features/models.rs")] -#[derive(OpenApi)] -#[openapi(info(title = "Percentage API", version = "1.0.0"))] -pub struct ResponsesImportApiDocs {} - -#[test] -fn test_path_import_responses() { - assert_eq!( - ResponsesImportApiDocs::openapi() - .components - .expect("no components") - .responses - .len(), - 3, // 1 derive, 1 manual, 1 manual with utoipa::ToResponse - ) -} - -/// Discover custom handler -#[utoipauto( - paths = "./utoipauto/tests/default_features/controllers/controller3.rs", - function_attribute_name = "test_handler" -)] -#[derive(OpenApi)] -#[openapi(info(title = "Percentage API", version = "1.0.0"))] -pub struct CustomHandlerApiDocs {} - -#[test] -fn test_custom_handler() { - assert_eq!(CustomHandlerApiDocs::openapi().paths.paths.len(), 1) -} diff --git a/utoipauto/tests/default_features/type_generics.rs b/utoipauto/tests/default_features/type_generics.rs deleted file mode 100644 index 73fcacb..0000000 --- a/utoipauto/tests/default_features/type_generics.rs +++ /dev/null @@ -1,35 +0,0 @@ -use utoipa::ToSchema; - -#[derive(ToSchema)] -pub struct NonGenericSchema; - -#[derive(ToSchema)] -pub struct NonGenericSchema2; - -#[derive(ToSchema)] -#[aliases(GenricModelSchema = GenericSchema < NonGenericSchema >)] -pub struct GenericSchema { - _data: T, -} - -#[derive(ToSchema)] -#[aliases(MultipleGenericModelSchema = MultipleGenericSchema < NonGenericSchema, NonGenericSchema2 >)] -pub struct MultipleGenericSchema { - _data: T, - _data2: U, -} - -#[derive(ToSchema)] -#[aliases( -MultipleAlaises1 = MultipleAliasesSchema < NonGenericSchema >, -MultipleAlaises2 = MultipleAliasesSchema < NonGenericSchema2 > -)] -pub struct MultipleAliasesSchema { - _data: T, -} - -#[derive(ToSchema)] -#[aliases(NestedGenericsSchema = NestedGenerics < GenericSchema < NonGenericSchema > >)] -pub struct NestedGenerics { - _data: T, -} diff --git a/utoipauto/tests/generic_full_path/const_generics.rs b/utoipauto/tests/generic_full_path/const_generics.rs deleted file mode 100644 index 513768c..0000000 --- a/utoipauto/tests/generic_full_path/const_generics.rs +++ /dev/null @@ -1,13 +0,0 @@ -use utoipa::ToSchema; - -#[derive(ToSchema)] -#[aliases(ConstGenericStructSchema0 = ConstGenericStructSchema<0>)] -pub struct ConstGenericStructSchema { - foo: [u16; N], -} - -#[derive(ToSchema)] -#[aliases(ConstGenericEnumSchema0 = ConstGenericEnumSchema<0>)] -pub enum ConstGenericEnumSchema { - Foo([u16; N]), -} diff --git a/utoipauto/tests/generic_full_path/even_more_schemas.rs b/utoipauto/tests/generic_full_path/even_more_schemas.rs deleted file mode 100644 index f75f6ba..0000000 --- a/utoipauto/tests/generic_full_path/even_more_schemas.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub struct EvenMoreSchema; - -pub struct EvenMoreSchema2; diff --git a/utoipauto/tests/generic_full_path/mod.rs b/utoipauto/tests/generic_full_path/mod.rs deleted file mode 100644 index 7082e2e..0000000 --- a/utoipauto/tests/generic_full_path/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod const_generics; -mod even_more_schemas; -mod more_schemas; -mod partial_imports; -mod schemas; -mod test; diff --git a/utoipauto/tests/generic_full_path/more_schemas.rs b/utoipauto/tests/generic_full_path/more_schemas.rs deleted file mode 100644 index 6ba6f4b..0000000 --- a/utoipauto/tests/generic_full_path/more_schemas.rs +++ /dev/null @@ -1,10 +0,0 @@ -use utoipa::ToSchema; - -#[derive(ToSchema)] -pub struct MoreSchema; - -#[derive(ToSchema)] -pub struct MoreSchema2; - -#[derive(ToSchema)] -pub struct AsSchema; diff --git a/utoipauto/tests/generic_full_path/partial_imports.rs b/utoipauto/tests/generic_full_path/partial_imports.rs deleted file mode 100644 index 305a2e5..0000000 --- a/utoipauto/tests/generic_full_path/partial_imports.rs +++ /dev/null @@ -1,22 +0,0 @@ -use utoipa::ToSchema; - -use crate::generic_full_path::even_more_schemas as schemas; -use crate::generic_full_path::more_schemas; - -#[derive(ToSchema)] -#[aliases( - PartialImportGenericSchemaMoreSchema = PartialImportGenericSchema < more_schemas::MoreSchema >, - PartialImportGenericSchemaMoreSchema2 = PartialImportGenericSchema < more_schemas::MoreSchema2 > -)] -pub struct PartialImportGenericSchema { - _data: T, -} - -#[derive(ToSchema)] -#[aliases( - PartialImportEvenMoreGenericSchemaAsImport = PartialImportGenericSchema < schemas::EvenMoreSchema >, - PartialImportEvenMoreGenericSchema2AsImport = PartialImportGenericSchema < schemas::EvenMoreSchema2 > -)] -pub struct PartialImportGenericSchemaAsImport { - _data: T, -} diff --git a/utoipauto/tests/generic_full_path/schemas.rs b/utoipauto/tests/generic_full_path/schemas.rs deleted file mode 100644 index c290a68..0000000 --- a/utoipauto/tests/generic_full_path/schemas.rs +++ /dev/null @@ -1,31 +0,0 @@ -use utoipa::ToSchema; - -use crate::generic_full_path::more_schemas::AsSchema as AsSchemaImport; -use crate::generic_full_path::more_schemas::MoreSchema; -use crate::generic_full_path::more_schemas::MoreSchema2; - -#[derive(ToSchema)] -#[aliases(GenericMoreSchema = GenericSchema < MoreSchema >, -GenericMoreSchema2 = GenericSchema < crate::generic_full_path::more_schemas::MoreSchema2 >)] -pub struct GenericSchema { - _data: T, -} - -#[derive(ToSchema)] -#[aliases(MoreGenericSchema2 = MoreGenericSchema < AsSchemaImport >)] -pub struct MoreGenericSchema { - _data: T, -} - -#[derive(ToSchema)] -#[aliases(MultipleGenericsSchema = MultipleGenerics < MoreSchema, MoreSchema2 >)] -pub struct MultipleGenerics { - _data: T, - _data2: U, -} - -#[derive(ToSchema)] -#[aliases(NestedGenericsSchema = NestedGenerics < MoreGenericSchema < MoreSchema > >)] -pub struct NestedGenerics { - _data: T, -} diff --git a/utoipauto/tests/generic_full_path/test.rs b/utoipauto/tests/generic_full_path/test.rs deleted file mode 100644 index 6272852..0000000 --- a/utoipauto/tests/generic_full_path/test.rs +++ /dev/null @@ -1,26 +0,0 @@ -use utoipa::OpenApi; - -use utoipauto_macro::utoipauto; - -#[utoipauto(paths = "./utoipauto/tests/generic_full_path")] -#[derive(OpenApi)] -#[openapi(info(title = "Percentage API", version = "1.0.0"))] -pub struct CrateApiDocs; - -/// Discover schema with const generics -#[utoipauto(paths = "./utoipauto/tests/generic_full_path/const_generics.rs")] -#[derive(OpenApi)] -#[openapi(info(title = "Const Generic API", version = "1.0.0"))] -pub struct ConstGenericApiDocs {} - -#[test] -fn test_const_generics() { - assert_eq!( - ConstGenericApiDocs::openapi() - .components - .expect("no components") - .schemas - .len(), - 2 - ) -} diff --git a/utoipauto/tests/test.rs b/utoipauto/tests/test.rs deleted file mode 100644 index f811459..0000000 --- a/utoipauto/tests/test.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[cfg(not(feature = "generic_full_path"))] -mod default_features; -#[cfg(feature = "generic_full_path")] -mod generic_full_path; From 45ccc393dae097b6696c7587162045b5950fce25 Mon Sep 17 00:00:00 2001 From: Denux Date: Sat, 5 Oct 2024 17:36:42 +0200 Subject: [PATCH 05/14] formatted code --- .../crate_folder/sub_folder/test.rs | 4 +-- acceptance/generics/src/main.rs | 11 ++++---- acceptance/generics/src/routes.rs | 25 +++++++++++++++---- acceptance/responses/src/main.rs | 11 ++++---- acceptance/responses/src/response.rs | 7 ++++-- acceptance/responses/src/routes.rs | 8 +----- 6 files changed, 37 insertions(+), 29 deletions(-) diff --git a/acceptance/crate_segment_path/crate_folder/sub_folder/test.rs b/acceptance/crate_segment_path/crate_folder/sub_folder/test.rs index 6ddd6db..05508a5 100644 --- a/acceptance/crate_segment_path/crate_folder/sub_folder/test.rs +++ b/acceptance/crate_segment_path/crate_folder/sub_folder/test.rs @@ -7,9 +7,7 @@ use utoipauto::utoipauto; #[allow(dead_code)] pub struct CrateInAnotherPath {} -#[utoipauto( - paths = "( ./folder_in_src/crate_folder/new_sub_folder/paths.rs from folder-in-src::new_sub_folder )" -)] +#[utoipauto(paths = "( ./folder_in_src/crate_folder/new_sub_folder/paths.rs from folder-in-src::new_sub_folder )")] #[derive(OpenApi)] #[openapi(info(title = "Percentage API", version = "1.0.0"))] #[allow(dead_code)] diff --git a/acceptance/generics/src/main.rs b/acceptance/generics/src/main.rs index 1d0b939..4444385 100644 --- a/acceptance/generics/src/main.rs +++ b/acceptance/generics/src/main.rs @@ -9,15 +9,14 @@ use utoipauto::utoipauto; #[utoipauto(paths = "./generics/src")] #[derive(Debug, OpenApi)] -#[openapi( - info( - title = "Generic Test Api" - ) -)] +#[openapi(info(title = "Generic Test Api"))] pub(crate) struct ApiDoc; fn main() { - println!("Our OpenApi documentation {}", ApiDoc::openapi().to_pretty_json().unwrap()); + println!( + "Our OpenApi documentation {}", + ApiDoc::openapi().to_pretty_json().unwrap() + ); } #[cfg(test)] diff --git a/acceptance/generics/src/routes.rs b/acceptance/generics/src/routes.rs index 4526c75..a614788 100644 --- a/acceptance/generics/src/routes.rs +++ b/acceptance/generics/src/routes.rs @@ -44,9 +44,18 @@ pub fn get_array_persons() -> ArrayResponse { ArrayResponse { status: 200, data: [ - Person { name: "John Doe".to_string(), age: 30 }, - Person { name: "Jane Doe".to_string(), age: 25 }, - Person { name: "Jim Doe".to_string(), age: 20 }, + Person { + name: "John Doe".to_string(), + age: 30, + }, + Person { + name: "Jane Doe".to_string(), + age: 25, + }, + Person { + name: "Jim Doe".to_string(), + age: 20, + }, ], } } @@ -91,8 +100,14 @@ pub fn get_combined_persons() -> CombinedResponse<'static, Person, 3> { status: 200, data: [ person_ref.clone(), - Person { name: "Jane Doe".to_string(), age: 25 }, - Person { name: "Jim Doe".to_string(), age: 20 }, + Person { + name: "Jane Doe".to_string(), + age: 25, + }, + Person { + name: "Jim Doe".to_string(), + age: 20, + }, ], }, borrowed_response: BorrowedResponse { diff --git a/acceptance/responses/src/main.rs b/acceptance/responses/src/main.rs index 3b46312..9512cc1 100644 --- a/acceptance/responses/src/main.rs +++ b/acceptance/responses/src/main.rs @@ -6,15 +6,14 @@ use utoipauto::utoipauto; #[utoipauto(paths = "./responses/src")] #[derive(Debug, OpenApi)] -#[openapi( - info( - title = "Responses Test Api" - ) -)] +#[openapi(info(title = "Responses Test Api"))] pub(crate) struct ApiDoc; fn main() { - println!("Our OpenApi documentation {}", ApiDoc::openapi().to_pretty_json().unwrap()); + println!( + "Our OpenApi documentation {}", + ApiDoc::openapi().to_pretty_json().unwrap() + ); } #[cfg(test)] diff --git a/acceptance/responses/src/response.rs b/acceptance/responses/src/response.rs index d610bce..52adbff 100644 --- a/acceptance/responses/src/response.rs +++ b/acceptance/responses/src/response.rs @@ -17,8 +17,11 @@ pub enum Person { Admin(#[content("application/vnd-custom-v1+json")] Admin), #[response(example = json!({"name": "name3", "id": 1}))] - Admin2(#[content("application/vnd-custom-v2+json")] - #[to_schema] Admin2), + Admin2( + #[content("application/vnd-custom-v2+json")] + #[to_schema] + Admin2, + ), } #[derive(utoipa::ToSchema)] diff --git a/acceptance/responses/src/routes.rs b/acceptance/responses/src/routes.rs index 521ac26..865f511 100644 --- a/acceptance/responses/src/routes.rs +++ b/acceptance/responses/src/routes.rs @@ -1,12 +1,6 @@ use crate::response::{Admin, Person, UserResponses}; -#[utoipa::path( - get, - path = "/api/user", - responses( - UserResponses - ) -)] +#[utoipa::path(get, path = "/api/user", responses(UserResponses))] fn get_user() -> UserResponses { UserResponses::NotFound } From d9ee5a86f955563ea67fafd85b956fda5944c791 Mon Sep 17 00:00:00 2001 From: Denux Date: Sat, 12 Oct 2024 13:05:10 +0200 Subject: [PATCH 06/14] moved schema discovery behind a feature flag Updated utoipa --- Cargo.toml | 2 +- acceptance/Cargo.lock | 8 +- acceptance/Cargo.toml | 2 +- utoipauto-core/Cargo.toml | 2 +- utoipauto-core/src/discover.rs | 426 ++------------------------------- utoipauto-macro/Cargo.toml | 2 +- utoipauto/Cargo.toml | 2 +- 7 files changed, 35 insertions(+), 409 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f38e708..d1091da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ utoipauto-core = { path = "utoipauto-core", version = "0.2" } utoipauto-macro = { path = "utoipauto-macro", version = "0.2" } # Utoipa -utoipa = { git = "https://github.com/juhaku/utoipa", rev = "22ea68da5134f478283feb212cbb584ed8bac612", features = ["preserve_path_order"] } +utoipa = { git = "https://github.com/juhaku/utoipa", rev = "d2f076aafbe2569101fc279e9caa359a7ad72fdb", features = ["preserve_path_order"] } # Macro dependencies quote = "1.0.36" diff --git a/acceptance/Cargo.lock b/acceptance/Cargo.lock index 900c65b..2c83631 100644 --- a/acceptance/Cargo.lock +++ b/acceptance/Cargo.lock @@ -56,9 +56,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] @@ -139,7 +139,7 @@ checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "utoipa" version = "5.0.0-rc.0" -source = "git+https://github.com/juhaku/utoipa?rev=22ea68da5134f478283feb212cbb584ed8bac612#22ea68da5134f478283feb212cbb584ed8bac612" +source = "git+https://github.com/juhaku/utoipa?rev=d2f076aafbe2569101fc279e9caa359a7ad72fdb#d2f076aafbe2569101fc279e9caa359a7ad72fdb" dependencies = [ "indexmap", "serde", @@ -150,7 +150,7 @@ dependencies = [ [[package]] name = "utoipa-gen" version = "5.0.0-rc.0" -source = "git+https://github.com/juhaku/utoipa?rev=22ea68da5134f478283feb212cbb584ed8bac612#22ea68da5134f478283feb212cbb584ed8bac612" +source = "git+https://github.com/juhaku/utoipa?rev=d2f076aafbe2569101fc279e9caa359a7ad72fdb#d2f076aafbe2569101fc279e9caa359a7ad72fdb" dependencies = [ "proc-macro2", "quote", diff --git a/acceptance/Cargo.toml b/acceptance/Cargo.toml index 8b0f57e..5d42559 100644 --- a/acceptance/Cargo.toml +++ b/acceptance/Cargo.toml @@ -15,4 +15,4 @@ homepage = "https://github.com/ProbablyClem/utoipauto" [workspace.dependencies] utoipauto = { path = "../utoipauto", version = "0.2" } -utoipa = { git = "https://github.com/juhaku/utoipa", rev = "22ea68da5134f478283feb212cbb584ed8bac612", features = ["preserve_path_order"] } +utoipa = { git = "https://github.com/juhaku/utoipa", rev = "d2f076aafbe2569101fc279e9caa359a7ad72fdb", features = ["preserve_path_order"] } diff --git a/utoipauto-core/Cargo.toml b/utoipauto-core/Cargo.toml index 4309111..9cf338e 100644 --- a/utoipauto-core/Cargo.toml +++ b/utoipauto-core/Cargo.toml @@ -13,7 +13,7 @@ homepage.workspace = true rust-version.workspace = true [features] -generic_full_path = [] +schema_discovery = [] [dependencies] quote.workspace = true diff --git a/utoipauto-core/src/discover.rs b/utoipauto-core/src/discover.rs index d22fe6d..9b57bac 100644 --- a/utoipauto-core/src/discover.rs +++ b/utoipauto-core/src/discover.rs @@ -3,9 +3,13 @@ use std::vec; use crate::file_utils::{extract_module_name_from_path, parse_files}; use crate::token_utils::Parameters; use quote::ToTokens; -use syn::meta::ParseNestedMeta; use syn::token::Comma; -use syn::{punctuated::Punctuated, Attribute, GenericParam, Item, ItemFn, Meta, Token, Type}; +use syn::{punctuated::Punctuated, Attribute, GenericParam, Item, ItemFn, ItemImpl, Meta, Token}; + +#[cfg(feature = "schema_discovery")] +pub(crate) const SHOULD_PARSE_SCHEMA: bool = true; +#[cfg(not(feature = "schema_discovery"))] +pub(crate) const SHOULD_PARSE_SCHEMA: bool = false; /// Discover everything from a file, will explore folder recursively pub fn discover_from_file( @@ -17,18 +21,7 @@ pub fn discover_from_file( files .into_iter() - .map(|e| { - #[cfg(feature = "generic_full_path")] - let imports = extract_use_statements(&e.0, &crate_name); - #[cfg(not(feature = "generic_full_path"))] - let imports = vec![]; - parse_module_items( - &extract_module_name_from_path(&e.0, &crate_name), - e.1.items, - imports, - params, - ) - }) + .map(|e| parse_module_items(&extract_module_name_from_path(&e.0, &crate_name), e.1.items, params)) .fold(Vec::::new(), |mut acc, mut v| { acc.append(&mut v); acc @@ -58,48 +51,36 @@ enum DiscoverType { CustomResponseImpl(String), } -fn parse_module_items( - module_path: &str, - items: Vec, - imports: Vec, - params: &Parameters, -) -> Vec { +fn parse_module_items(module_path: &str, items: Vec, params: &Parameters) -> Vec { items .into_iter() .filter(|e| { matches!( e, - syn::Item::Mod(_) | syn::Item::Fn(_) | syn::Item::Struct(_) | syn::Item::Enum(_) | syn::Item::Impl(_) + Item::Mod(_) | Item::Fn(_) | Item::Struct(_) | Item::Enum(_) | Item::Impl(_) ) }) .map(|v| match v { - syn::Item::Mod(m) => m.content.map_or(Vec::::new(), |cs| { - parse_module_items( - &build_path(module_path, &m.ident.to_string()), - cs.1, - imports.clone(), - params, - ) + Item::Mod(m) => m.content.map_or(Vec::::new(), |cs| { + parse_module_items(&build_path(module_path, &m.ident.to_string()), cs.1, params) }), - syn::Item::Fn(f) => parse_function(&f, ¶ms.fn_attribute_name) + Item::Fn(f) => parse_function(&f, ¶ms.fn_attribute_name) .into_iter() .map(|item| DiscoverType::Fn(build_path(module_path, &item))) .collect(), - syn::Item::Struct(s) => parse_from_attr( + Item::Struct(s) => parse_from_attr( &s.attrs, &build_path(module_path, &s.ident.to_string()), s.generics.params, - imports.clone(), params, ), - syn::Item::Enum(e) => parse_from_attr( + Item::Enum(e) => parse_from_attr( &e.attrs, &build_path(module_path, &e.ident.to_string()), e.generics.params, - imports.clone(), params, ), - syn::Item::Impl(im) => parse_from_impl(&im, module_path, params), + Item::Impl(im) => parse_from_impl(&im, module_path, params), _ => vec![], }) .fold(Vec::::new(), |mut acc, mut v| { @@ -113,11 +94,12 @@ fn parse_from_attr( a: &Vec, name: &str, generic_params: Punctuated, - imports: Vec, params: &Parameters, ) -> Vec { let mut out: Vec = vec![]; - let is_non_lifetime_generic = !generic_params.iter().all(|p| matches!(p, GenericParam::Lifetime(_))); + if !generic_params.is_empty() { + return out; + } for attr in a { let meta = &attr.meta; @@ -131,13 +113,13 @@ fn parse_from_attr( for nested_meta in nested { if nested_meta.path().segments.len() == 2 { if nested_meta.path().segments[0].ident == "utoipa" { - if nested_meta.path().segments[1].ident == "ToSchema" && !is_non_lifetime_generic { + if nested_meta.path().segments[1].ident == "ToSchema" && SHOULD_PARSE_SCHEMA { out.push(DiscoverType::Model(name.to_string())); - } else if nested_meta.path().segments[1].ident == "ToResponse" && !is_non_lifetime_generic { + } else if nested_meta.path().segments[1].ident == "ToResponse" { out.push(DiscoverType::Response(name.to_string())); } } - } else if nested_meta.path().is_ident(¶ms.schema_attribute_name) && !is_non_lifetime_generic { + } else if nested_meta.path().is_ident(¶ms.schema_attribute_name) && SHOULD_PARSE_SCHEMA { out.push(DiscoverType::Model(name.to_string())); } if nested_meta.path().is_ident(¶ms.response_attribute_name) { @@ -145,158 +127,17 @@ fn parse_from_attr( } } } - if is_non_lifetime_generic && attr.path().is_ident("aliases") { - let _ = attr.parse_nested_meta(|meta| { - out.push(DiscoverType::Model(parse_generic_schema( - meta, - name, - imports.clone(), - &generic_params, - ))); - - Ok(()) - }); - } } out } -#[cfg(not(feature = "generic_full_path"))] -fn parse_generic_schema( - meta: ParseNestedMeta, - name: &str, - _imports: Vec, - non_lifetime_generic_params: &Punctuated, -) -> String { - let splited_types = split_type(meta); - let mut nested_generics = Vec::new(); - - for spl_type in splited_types { - let parts: Vec<&str> = spl_type.split(',').collect(); - let mut generics = Vec::new(); - - for (index, part) in parts.iter().enumerate() { - match non_lifetime_generic_params - .get(index) - .expect("Too few parameters provided to generic") - { - GenericParam::Lifetime(_) => (), - _ => generics.push(part.to_string()), - }; - } - - nested_generics.push(generics.join(", ")); - } - - let generics = merge_nested_generics(nested_generics); - - name.to_string() + &generics -} - -#[cfg(feature = "generic_full_path")] -fn parse_generic_schema( - meta: ParseNestedMeta, - name: &str, - imports: Vec, - non_lifetime_generic_params: &Punctuated, -) -> String { - let splitted_types = split_type(meta); - let mut nested_generics = Vec::new(); - - for spl_type in splitted_types { - let parts: Vec<&str> = spl_type.split(",").collect(); - let mut generics = Vec::new(); - - for (index, part) in parts.iter().enumerate() { - match non_lifetime_generic_params - .get(index) - .expect("Too few parameters provided to generic") - { - GenericParam::Lifetime(_) => (), - GenericParam::Type(_) => generics.push(process_one_generic(part, name, imports.clone())), - GenericParam::Const(_) => generics.push(part.to_string()), - }; - } - - nested_generics.push(generics.join(", ")); - } - - let generics = merge_nested_generics(nested_generics); - let generic_type_with_module_path = name.to_string() + &generics; - - generic_type_with_module_path -} - -#[cfg(feature = "generic_full_path")] -fn process_one_generic(part: &str, name: &str, imports: Vec) -> String { - let mut processed_parts = Vec::new(); - - if part.contains("<") { - // Handle nested generics - let nested_parts: Vec<&str> = part.splitn(2, "<").collect(); - let nested_generic = find_import( - imports.clone(), - get_current_module_from_name(name).as_str(), - nested_parts[0].trim(), - ) + "<" - + &process_one_generic(nested_parts[1].trim(), name, imports.clone()); - processed_parts.push(nested_generic); - } else { - // Normal type, find the full path - let full_path = find_import( - imports.clone(), - get_current_module_from_name(name).as_str(), - part.trim(), - ); - processed_parts.push(full_path); - } - - processed_parts.join("::") -} - -pub fn split_type(meta: ParseNestedMeta) -> Vec { - let value = meta.value().unwrap(); // this parses the `=` - let generic_type: Type = value.parse().unwrap(); - let type_as_string = generic_type.into_token_stream().to_string(); - // get generic type - let start = type_as_string.find('<').unwrap_or(0) + 1; - let end = type_as_string.rfind('>').unwrap_or(type_as_string.len()); - let splited_type = type_as_string[start..end].to_string(); - - let types: Vec = splited_type - .split('<') - .map(|s| s.split('>').next().unwrap_or("").to_string()) - .collect(); - - types -} - -fn merge_nested_generics(nested_generics: Vec) -> String { - let mut generics = String::from("<"); - if nested_generics.len() == 1 { - generics = generics + nested_generics.first().unwrap() + ">"; - } else { - for (i, gen) in nested_generics.iter().enumerate() { - generics.push_str(gen.trim()); - if i != nested_generics.len() - 1 { - generics.push('<'); - } - } - for _ in 0..nested_generics.len() { - generics.push('>'); - } - } - - generics -} - -fn parse_from_impl(im: &syn::ItemImpl, module_base_path: &str, params: &Parameters) -> Vec { +fn parse_from_impl(im: &ItemImpl, module_base_path: &str, params: &Parameters) -> Vec { im.trait_ .as_ref() .and_then(|trt| trt.1.segments.last().map(|p| p.ident.to_string())) .and_then(|impl_name| { - if impl_name.eq(params.schema_attribute_name.as_str()) { + if impl_name.eq(params.schema_attribute_name.as_str()) && SHOULD_PARSE_SCHEMA { Some(vec![DiscoverType::CustomModelImpl(build_path( module_base_path, &im.self_ty.to_token_stream().to_string(), @@ -349,118 +190,10 @@ fn build_path(file_name: &str, fn_name: &str) -> String { format!("{}::{}", file_name, fn_name) } -#[cfg(feature = "generic_full_path")] -fn extract_use_statements(file_path: &str, crate_name: &str) -> Vec { - let file = std::fs::read_to_string(file_path).unwrap(); - let mut out: Vec = vec![]; - let mut multiline_import = String::new(); - let mut is_multiline = false; - - for line in file.lines() { - let mut line = line.trim().to_string(); - - if is_multiline { - multiline_import.push_str(&line); - if line.ends_with("}") { - is_multiline = false; - line = multiline_import.clone(); - multiline_import.clear(); - } else { - continue; - } - } - - if line.starts_with("use") { - line = line.replace("use ", "").replace(";", "").replace(crate_name, ""); - - if line.ends_with("{") { - is_multiline = true; - multiline_import = line; - continue; - } - - let parts: Vec<&str> = line.split('{').collect(); - if parts.len() > 1 { - let module_path = parts[0]; - let imports: Vec<&str> = parts[1].trim_end_matches('}').split(',').collect(); - for import in imports { - let import = import.trim(); - if import.starts_with("::") { - out.push(format!("{}{}", crate_name, import)); - } else { - out.push(format!("{}{}", module_path, import)); - } - } - } else { - if line.starts_with("::") { - line = format!("{}{}", crate_name, line); - } - out.push(line); - } - } - } - out -} - -#[cfg(feature = "generic_full_path")] -fn find_import(imports: Vec, current_module: &str, name: &str) -> String { - let name = name.trim(); - let current_module = current_module.trim(); - for import in imports.iter() { - if import.contains(name) { - let full_path = import_to_full_path(import); - return full_path; - } - } - - // If the name contains `::` it means that it's a partial import or a full path - if name.contains("::") { - return handle_partial_import(imports, name).unwrap_or_else(|| name.to_string()); - } - - // Only append the module path if the name does not already contain it - if !name.starts_with(current_module) { - return current_module.to_string() + "::" + name; - } - - name.to_string() -} - -#[cfg(feature = "generic_full_path")] -fn handle_partial_import(imports: Vec, name: &str) -> Option { - name.split("::").next().and_then(|first| { - let first = first.trim(); - - for import in imports { - let import = import.trim(); - - if import.ends_with(first) { - let full_path = import_to_full_path(&import); - - let usable_import = format!("{}{}", full_path.trim(), name[first.len()..].trim()); - return Some(usable_import); - } - } - None - }) -} - -#[cfg(feature = "generic_full_path")] -fn import_to_full_path(import: &str) -> String { - import.split(" as ").next().unwrap_or(&import).trim().to_string() -} - -#[cfg(feature = "generic_full_path")] -fn get_current_module_from_name(name: &str) -> String { - let parts: Vec<&str> = name.split("::").collect(); - parts[..parts.len() - 1].join("::") -} - #[cfg(test)] mod test { - #[cfg(feature = "generic_full_path")] - use crate::discover::{find_import, get_current_module_from_name, process_one_generic}; use quote::quote; + use syn::ItemFn; #[test] fn test_parse_function() { @@ -469,7 +202,7 @@ mod test { pub fn route_custom() {} }; - let item_fn: syn::ItemFn = syn::parse2(quoted).unwrap(); + let item_fn: ItemFn = syn::parse2(quoted).unwrap(); let fn_name = super::parse_function(&item_fn, "utoipa"); assert_eq!(fn_name, vec!["route_custom"]); @@ -478,115 +211,8 @@ mod test { pub fn route_custom() {} }; - let item_fn: syn::ItemFn = syn::parse2(quoted).unwrap(); + let item_fn: ItemFn = syn::parse2(quoted).unwrap(); let fn_name = super::parse_function(&item_fn, "handler"); assert_eq!(fn_name, vec!["route_custom"]); } - - #[test] - #[cfg(feature = "generic_full_path")] - fn test_process_one_generic_nested_generics() { - let part = "Generic"; - let name = "module::name"; - let imports = vec!["module::Generic".to_string(), "module::Inner".to_string()]; - let expected = "module::Generic"; - let result = process_one_generic(part, name, imports); - assert_eq!(result, expected); - } - - #[test] - #[cfg(feature = "generic_full_path")] - fn test_process_one_generic_partial_import() { - let part = "PartialImportGenericSchema"; - let name = "crate"; - let imports = vec!["crate::generic_full_path::more_schemas".to_string()]; - let expected = "PartialImportGenericSchema"; - let result = process_one_generic(part, name, imports); - assert_eq!(result, expected); - } - - #[test] - #[cfg(feature = "generic_full_path")] - fn test_process_one_generic_no_nested_generics() { - let part = "Generic"; - let name = "module::name"; - let imports = vec!["module::Generic".to_string()]; - let expected = "module::Generic"; - let result = process_one_generic(part, name, imports); - assert_eq!(result, expected); - } - - #[test] - #[cfg(feature = "generic_full_path")] - fn test_process_one_generic_no_generics() { - let part = "NonGeneric"; - let name = "module::name"; - let imports = vec!["module::NonGeneric".to_string()]; - let expected = "module::NonGeneric"; - let result = process_one_generic(part, name, imports); - assert_eq!(result, expected); - } - - #[test] - #[cfg(feature = "generic_full_path")] - fn test_find_import_multiple_modules() { - let imports = vec![ - "module1::module2::name".to_string(), - "module1::module2::other_name".to_string(), - ]; - let current_module = "module1::module2"; - let name = "name"; - let expected = "module1::module2::name"; - let result = find_import(imports, current_module, name); - assert_eq!(result, expected); - } - - #[test] - #[cfg(feature = "generic_full_path")] - fn test_find_import_single_module() { - let imports = vec!["module1::name".to_string(), "module1::other_name".to_string()]; - let current_module = "module1"; - let name = "name"; - let expected = "module1::name"; - let result = find_import(imports, current_module, name); - assert_eq!(result, expected); - } - - #[test] - #[cfg(feature = "generic_full_path")] - fn test_find_import_no_module() { - let imports = vec!["name".to_string(), "other_name".to_string()]; - let current_module = ""; - let name = "name"; - let expected = "name"; - let result = find_import(imports, current_module, name); - assert_eq!(result, expected); - } - - #[test] - #[cfg(feature = "generic_full_path")] - fn test_get_current_module_from_name_multiple_modules() { - let name = "module1::module2::name"; - let expected = "module1::module2"; - let result = get_current_module_from_name(name); - assert_eq!(result, expected); - } - - #[test] - #[cfg(feature = "generic_full_path")] - fn test_get_current_module_from_name_single_module() { - let name = "module1::name"; - let expected = "module1"; - let result = get_current_module_from_name(name); - assert_eq!(result, expected); - } - - #[test] - #[cfg(feature = "generic_full_path")] - fn test_get_current_module_from_name_no_module() { - let name = "name"; - let expected = ""; - let result = get_current_module_from_name(name); - assert_eq!(result, expected); - } } diff --git a/utoipauto-macro/Cargo.toml b/utoipauto-macro/Cargo.toml index db794cd..a3b450e 100644 --- a/utoipauto-macro/Cargo.toml +++ b/utoipauto-macro/Cargo.toml @@ -16,7 +16,7 @@ rust-version.workspace = true proc-macro = true [features] -generic_full_path = ["utoipauto-core/generic_full_path"] +schema_discovery = ["utoipauto-core/schema_discovery"] [dependencies] utoipauto-core.workspace = true diff --git a/utoipauto/Cargo.toml b/utoipauto/Cargo.toml index 6057caa..0758a92 100644 --- a/utoipauto/Cargo.toml +++ b/utoipauto/Cargo.toml @@ -13,7 +13,7 @@ homepage.workspace = true rust-version.workspace = true [features] -generic_full_path = ["utoipauto-macro/generic_full_path"] +schema_discovery = ["utoipauto-macro/schema_discovery"] [dependencies] utoipauto-macro.workspace = true From e05a69365075441005ed316b908c562b2c5f1997 Mon Sep 17 00:00:00 2001 From: Timon Klinkert Date: Sun, 13 Oct 2024 16:40:55 +0200 Subject: [PATCH 07/14] updated utoipa --- Cargo.toml | 2 +- acceptance/Cargo.lock | 4 ++-- acceptance/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d1091da..1eb2527 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ utoipauto-core = { path = "utoipauto-core", version = "0.2" } utoipauto-macro = { path = "utoipauto-macro", version = "0.2" } # Utoipa -utoipa = { git = "https://github.com/juhaku/utoipa", rev = "d2f076aafbe2569101fc279e9caa359a7ad72fdb", features = ["preserve_path_order"] } +utoipa = { git = "https://github.com/juhaku/utoipa", rev = "7d105161d3f25d3634f1ba0445a98a016a782cc7", features = ["preserve_path_order"] } # Macro dependencies quote = "1.0.36" diff --git a/acceptance/Cargo.lock b/acceptance/Cargo.lock index 2c83631..ddf9099 100644 --- a/acceptance/Cargo.lock +++ b/acceptance/Cargo.lock @@ -139,7 +139,7 @@ checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "utoipa" version = "5.0.0-rc.0" -source = "git+https://github.com/juhaku/utoipa?rev=d2f076aafbe2569101fc279e9caa359a7ad72fdb#d2f076aafbe2569101fc279e9caa359a7ad72fdb" +source = "git+https://github.com/juhaku/utoipa?rev=7d105161d3f25d3634f1ba0445a98a016a782cc7#7d105161d3f25d3634f1ba0445a98a016a782cc7" dependencies = [ "indexmap", "serde", @@ -150,7 +150,7 @@ dependencies = [ [[package]] name = "utoipa-gen" version = "5.0.0-rc.0" -source = "git+https://github.com/juhaku/utoipa?rev=d2f076aafbe2569101fc279e9caa359a7ad72fdb#d2f076aafbe2569101fc279e9caa359a7ad72fdb" +source = "git+https://github.com/juhaku/utoipa?rev=7d105161d3f25d3634f1ba0445a98a016a782cc7#7d105161d3f25d3634f1ba0445a98a016a782cc7" dependencies = [ "proc-macro2", "quote", diff --git a/acceptance/Cargo.toml b/acceptance/Cargo.toml index 5d42559..ddc8cb8 100644 --- a/acceptance/Cargo.toml +++ b/acceptance/Cargo.toml @@ -15,4 +15,4 @@ homepage = "https://github.com/ProbablyClem/utoipauto" [workspace.dependencies] utoipauto = { path = "../utoipauto", version = "0.2" } -utoipa = { git = "https://github.com/juhaku/utoipa", rev = "d2f076aafbe2569101fc279e9caa359a7ad72fdb", features = ["preserve_path_order"] } +utoipa = { git = "https://github.com/juhaku/utoipa", rev = "7d105161d3f25d3634f1ba0445a98a016a782cc7", features = ["preserve_path_order"] } From 17942c7f262c71be02ea4d69f7cf232194b66ab1 Mon Sep 17 00:00:00 2001 From: Timon Klinkert Date: Sun, 13 Oct 2024 16:46:15 +0200 Subject: [PATCH 08/14] removed const generics as they are not officially supported by utoipa --- acceptance/generics/src/routes.rs | 46 ++---------------------------- acceptance/generics/src/schemas.rs | 10 +------ 2 files changed, 4 insertions(+), 52 deletions(-) diff --git a/acceptance/generics/src/routes.rs b/acceptance/generics/src/routes.rs index a614788..84a58f7 100644 --- a/acceptance/generics/src/routes.rs +++ b/acceptance/generics/src/routes.rs @@ -1,4 +1,4 @@ -use crate::schemas::{ArrayResponse, BorrowedResponse, CombinedResponse, NestedResponse, Person, Response}; +use crate::schemas::{BorrowedResponse, CombinedResponse, NestedResponse, Person, Response}; #[utoipa::path(get, path = "/persons", @@ -34,32 +34,6 @@ pub fn get_nested_persons() -> NestedResponse { } } -#[utoipa::path(get, - path = "/array_persons", - responses( -(status = 200, description = "An ArrayResponse", content_type = "application/json", body = ArrayResponse), - ) -)] -pub fn get_array_persons() -> ArrayResponse { - ArrayResponse { - status: 200, - data: [ - Person { - name: "John Doe".to_string(), - age: 30, - }, - Person { - name: "Jane Doe".to_string(), - age: 25, - }, - Person { - name: "Jim Doe".to_string(), - age: 20, - }, - ], - } -} - #[utoipa::path(get, path = "/borrowed_persons", responses( @@ -80,10 +54,10 @@ pub fn get_borrowed_persons() -> BorrowedResponse<'static, Person> { #[utoipa::path(get, path = "/combined_persons", responses( -(status = 200, description = "A CombinedResponse<'static, Person, 3>", content_type = "application/json", body = CombinedResponse<'static, Person, 3>), +(status = 200, description = "A CombinedResponse<'static, Person>", content_type = "application/json", body = CombinedResponse<'static, Person>), ) )] -pub fn get_combined_persons() -> CombinedResponse<'static, Person, 3> { +pub fn get_combined_persons() -> CombinedResponse<'static, Person> { let person = Box::new(Person { name: "John Doe".to_string(), age: 30, @@ -96,20 +70,6 @@ pub fn get_combined_persons() -> CombinedResponse<'static, Person, 3> { data: person_ref.clone(), }, }, - array_response: ArrayResponse { - status: 200, - data: [ - person_ref.clone(), - Person { - name: "Jane Doe".to_string(), - age: 25, - }, - Person { - name: "Jim Doe".to_string(), - age: 20, - }, - ], - }, borrowed_response: BorrowedResponse { status: 200, data: person_ref, diff --git a/acceptance/generics/src/schemas.rs b/acceptance/generics/src/schemas.rs index 5c1a61f..22b87fe 100644 --- a/acceptance/generics/src/schemas.rs +++ b/acceptance/generics/src/schemas.rs @@ -18,13 +18,6 @@ pub struct NestedResponse { pub response: Response, } -// Const Generics -#[derive(Debug, ToSchema)] -pub struct ArrayResponse { - pub status: u16, - pub data: [T; N], -} - // Lifetime Generics #[derive(Debug, ToSchema)] pub struct BorrowedResponse<'a, T: ToSchema> { @@ -34,8 +27,7 @@ pub struct BorrowedResponse<'a, T: ToSchema> { // Combined Generics #[derive(Debug, ToSchema)] -pub struct CombinedResponse<'a, T: ToSchema, const N: usize> { +pub struct CombinedResponse<'a, T: ToSchema> { pub nested_response: NestedResponse, - pub array_response: ArrayResponse, pub borrowed_response: BorrowedResponse<'a, T>, } From e5adafd6ad48934114d6b9ad37c9371193991178 Mon Sep 17 00:00:00 2001 From: Timon Klinkert Date: Sun, 13 Oct 2024 16:58:12 +0200 Subject: [PATCH 09/14] fix tests --- acceptance/generics/Cargo.toml | 2 +- .../generics/src/open_api.expected.json | 513 +++++++----------- 2 files changed, 196 insertions(+), 319 deletions(-) diff --git a/acceptance/generics/Cargo.toml b/acceptance/generics/Cargo.toml index 54aa522..4bcc7ac 100644 --- a/acceptance/generics/Cargo.toml +++ b/acceptance/generics/Cargo.toml @@ -15,7 +15,7 @@ unused = "allow" [dependencies] utoipa.workspace = true -utoipauto.workspace = true +utoipauto = { workspace = true, features = ["schema_discovery"]} [dev-dependencies] serde_json = "1.0.128" diff --git a/acceptance/generics/src/open_api.expected.json b/acceptance/generics/src/open_api.expected.json index f77872f..0ab4dcc 100644 --- a/acceptance/generics/src/open_api.expected.json +++ b/acceptance/generics/src/open_api.expected.json @@ -1,328 +1,205 @@ { - "components": { - "schemas": { - "ArrayResponse_Person": { - "properties": { - "data": { - "items": { - "properties": { - "age": { - "format": "int32", - "minimum": 0, - "type": "integer" - }, - "name": { - "type": "string" - } - }, - "required": [ - "name", - "age" - ], - "type": "object" - }, - "type": "array" - }, - "status": { - "format": "int32", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "status", - "data" - ], - "type": "object" + "components": { + "schemas": { + "BorrowedResponse_Person": { + "properties": { + "data": { + "properties": { + "age": { + "format": "int32", + "minimum": 0, + "type": "integer" + }, + "name": { + "type": "string" + } }, - "ArrayResponse_T_N": { - "properties": { - "data": { - "items": { - "$ref": "#/components/schemas/Person" - }, - "type": "array" - }, - "status": { - "format": "int32", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "status", - "data" - ], - "type": "object" - }, - "BorrowedResponse_Person": { - "properties": { - "data": { - "properties": { - "age": { - "format": "int32", - "minimum": 0, - "type": "integer" - }, - "name": { - "type": "string" - } - }, - "required": [ - "name", - "age" - ], - "type": "object" - }, - "status": { - "format": "int32", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "status", - "data" - ], - "type": "object" - }, - "BorrowedResponse_T": { - "properties": { - "data": { - "$ref": "#/components/schemas/Person" - }, - "status": { - "format": "int32", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "status", - "data" - ], - "type": "object" - }, - "CombinedResponse_Person": { - "properties": { - "array_response": { - "$ref": "#/components/schemas/ArrayResponse_T_N" - }, - "borrowed_response": { - "$ref": "#/components/schemas/BorrowedResponse_T" - }, - "nested_response": { - "$ref": "#/components/schemas/NestedResponse_T" - } - }, - "required": [ - "nested_response", - "array_response", - "borrowed_response" - ], - "type": "object" - }, - "NestedResponse_Person": { - "properties": { - "response": { - "$ref": "#/components/schemas/Response_T" - } - }, - "required": [ - "response" - ], - "type": "object" - }, - "NestedResponse_T": { - "properties": { - "response": { - "$ref": "#/components/schemas/Response_T" - } - }, - "required": [ - "response" - ], - "type": "object" - }, - "Person": { - "properties": { - "age": { - "format": "int32", - "minimum": 0, - "type": "integer" - }, - "name": { - "type": "string" - } - }, - "required": [ - "name", - "age" - ], - "type": "object" - }, - "Response_Person": { - "properties": { - "data": { - "properties": { - "age": { - "format": "int32", - "minimum": 0, - "type": "integer" - }, - "name": { - "type": "string" - } - }, - "required": [ - "name", - "age" - ], - "type": "object" - }, - "status": { - "format": "int32", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "status", - "data" - ], - "type": "object" - }, - "Response_T": { - "properties": { - "data": { - "$ref": "#/components/schemas/Person" - }, - "status": { - "format": "int32", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "status", - "data" - ], - "type": "object" - } - } - }, - "info": { - "contact": { - "name": "ProbablyClem" + "required": [ + "name", + "age" + ], + "type": "object" + }, + "status": { + "format": "int32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "status", + "data" + ], + "type": "object" + }, + "CombinedResponse_Person": { + "properties": { + "borrowed_response": { + "$ref": "#/components/schemas/BorrowedResponse_Person" + }, + "nested_response": { + "$ref": "#/components/schemas/NestedResponse_Person" + } + }, + "required": [ + "nested_response", + "borrowed_response" + ], + "type": "object" + }, + "NestedResponse_Person": { + "properties": { + "response": { + "$ref": "#/components/schemas/Response_Person" + } }, - "description": "A collection of crates to test utoipauto.", - "license": { - "name": "MIT OR Apache-2.0" + "required": [ + "response" + ], + "type": "object" + }, + "Person": { + "properties": { + "age": { + "format": "int32", + "minimum": 0, + "type": "integer" + }, + "name": { + "type": "string" + } }, - "title": "Generic Test Api", - "version": "0.1.0" + "required": [ + "name", + "age" + ], + "type": "object" + }, + "Response_Person": { + "properties": { + "data": { + "properties": { + "age": { + "format": "int32", + "minimum": 0, + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name", + "age" + ], + "type": "object" + }, + "status": { + "format": "int32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "status", + "data" + ], + "type": "object" + } + } + }, + "info": { + "contact": { + "name": "ProbablyClem" }, - "openapi": "3.1.0", - "paths": { - "/array_persons": { - "get": { - "operationId": "get_array_persons", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArrayResponse_Person" - } - } - }, - "description": "An ArrayResponse" - } - }, - "tags": [ - "crate::routes" - ] - } + "description": "A collection of crates to test utoipauto.", + "license": { + "name": "MIT OR Apache-2.0" + }, + "title": "Generic Test Api", + "version": "0.1.0" + }, + "openapi": "3.1.0", + "paths": { + "/borrowed_persons": { + "get": { + "operationId": "get_borrowed_persons", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BorrowedResponse_Person" + } + } + }, + "description": "A BorrowedResponse<'static, Person>" + } }, - "/borrowed_persons": { - "get": { - "operationId": "get_borrowed_persons", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BorrowedResponse_Person" - } - } - }, - "description": "A BorrowedResponse<'static, Person>" - } - }, - "tags": [ - "crate::routes" - ] - } + "tags": [ + "crate::routes" + ] + } + }, + "/combined_persons": { + "get": { + "operationId": "get_combined_persons", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CombinedResponse_Person" + } + } + }, + "description": "A CombinedResponse<'static, Person>" + } }, - "/combined_persons": { - "get": { - "operationId": "get_combined_persons", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CombinedResponse_Person" - } - } - }, - "description": "A CombinedResponse<'static, Person, 3>" - } - }, - "tags": [ - "crate::routes" - ] - } + "tags": [ + "crate::routes" + ] + } + }, + "/nested_persons": { + "get": { + "operationId": "get_nested_persons", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NestedResponse_Person" + } + } + }, + "description": "A NestedResponse" + } }, - "/nested_persons": { - "get": { - "operationId": "get_nested_persons", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NestedResponse_Person" - } - } - }, - "description": "A NestedResponse" - } - }, - "tags": [ - "crate::routes" - ] - } + "tags": [ + "crate::routes" + ] + } + }, + "/persons": { + "get": { + "operationId": "get_persons", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Response_Person" + } + } + }, + "description": "A Response" + } }, - "/persons": { - "get": { - "operationId": "get_persons", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Response_Person" - } - } - }, - "description": "A Response" - } - }, - "tags": [ - "crate::routes" - ] - } - } + "tags": [ + "crate::routes" + ] + } } -} + } +} \ No newline at end of file From 59e2d67bf5a73e050459bd872a6f973358316608 Mon Sep 17 00:00:00 2001 From: Timon Klinkert Date: Sun, 13 Oct 2024 20:13:43 +0200 Subject: [PATCH 10/14] enhanced testing --- acceptance/generics/src/main.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/acceptance/generics/src/main.rs b/acceptance/generics/src/main.rs index 4444385..f4cc5e3 100644 --- a/acceptance/generics/src/main.rs +++ b/acceptance/generics/src/main.rs @@ -2,7 +2,6 @@ pub mod routes; pub mod schemas; use routes::*; -use schemas::*; use utoipa::OpenApi; use utoipauto::utoipauto; @@ -36,8 +35,7 @@ mod tests { #[test] fn test_openapi() { let open_api = ApiDoc::openapi().to_json().unwrap(); - let expected_value = EXPECTED_OPEN_API; - assert_json_eq(&open_api, expected_value); + assert_json_eq(&open_api, EXPECTED_OPEN_API); } } From 2fd708c3c80857d7d707d111952204b40b2e96f5 Mon Sep 17 00:00:00 2001 From: Timon Klinkert Date: Mon, 14 Oct 2024 21:39:05 +0200 Subject: [PATCH 11/14] fixed all tests --- Cargo.toml | 2 +- acceptance/Cargo.lock | 20 ++++++++++++++----- acceptance/Cargo.toml | 11 +++++++++-- acceptance/generics/Cargo.toml | 4 ++-- acceptance/generics/src/main.rs | 9 +-------- acceptance/responses/Cargo.toml | 8 +++++++- acceptance/responses/src/main.rs | 12 +---------- acceptance/utility/Cargo.toml | 14 +++++++++++++ acceptance/utility/src/lib.rs | 8 ++++++++ utoipauto-core/Cargo.toml | 3 --- utoipauto-core/src/discover.rs | 34 ++++++++++++++------------------ utoipauto-macro/Cargo.toml | 3 --- utoipauto/Cargo.toml | 3 --- 13 files changed, 73 insertions(+), 58 deletions(-) create mode 100644 acceptance/utility/Cargo.toml create mode 100644 acceptance/utility/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 1eb2527..39f8c31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ utoipauto-core = { path = "utoipauto-core", version = "0.2" } utoipauto-macro = { path = "utoipauto-macro", version = "0.2" } # Utoipa -utoipa = { git = "https://github.com/juhaku/utoipa", rev = "7d105161d3f25d3634f1ba0445a98a016a782cc7", features = ["preserve_path_order"] } +utoipa = { version = "5.0.0", features = ["preserve_path_order"] } # Macro dependencies quote = "1.0.36" diff --git a/acceptance/Cargo.lock b/acceptance/Cargo.lock index ddf9099..5a29dc2 100644 --- a/acceptance/Cargo.lock +++ b/acceptance/Cargo.lock @@ -20,7 +20,7 @@ dependencies = [ name = "generics" version = "0.1.0" dependencies = [ - "serde_json", + "utility", "utoipa", "utoipauto", ] @@ -77,6 +77,7 @@ name = "responses" version = "0.1.0" dependencies = [ "serde_json", + "utility", "utoipa", "utoipauto", ] @@ -136,10 +137,18 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "utility" +version = "0.1.0" +dependencies = [ + "serde_json", +] + [[package]] name = "utoipa" -version = "5.0.0-rc.0" -source = "git+https://github.com/juhaku/utoipa?rev=7d105161d3f25d3634f1ba0445a98a016a782cc7#7d105161d3f25d3634f1ba0445a98a016a782cc7" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2b34fc58a72021914a5745832024b2baa638fe771df5a35f3d1b69266bd92c" dependencies = [ "indexmap", "serde", @@ -149,8 +158,9 @@ dependencies = [ [[package]] name = "utoipa-gen" -version = "5.0.0-rc.0" -source = "git+https://github.com/juhaku/utoipa?rev=7d105161d3f25d3634f1ba0445a98a016a782cc7#7d105161d3f25d3634f1ba0445a98a016a782cc7" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "866f11b33be38a747542f435578a164674b8922d958cc065d7f19319c19d4784" dependencies = [ "proc-macro2", "quote", diff --git a/acceptance/Cargo.toml b/acceptance/Cargo.toml index ddc8cb8..57e7bf6 100644 --- a/acceptance/Cargo.toml +++ b/acceptance/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["crate_segment_path", "folder_in_src", "generics", "responses"] +members = ["crate_segment_path", "folder_in_src", "generics", "responses", "utility"] resolver = "2" [workspace.package] @@ -14,5 +14,12 @@ repository = "https://github.com/ProbablyClem/utoipauto" homepage = "https://github.com/ProbablyClem/utoipauto" [workspace.dependencies] +# Utoipa utoipauto = { path = "../utoipauto", version = "0.2" } -utoipa = { git = "https://github.com/juhaku/utoipa", rev = "7d105161d3f25d3634f1ba0445a98a016a782cc7", features = ["preserve_path_order"] } +utoipa = { version = "5.0.0", features = ["preserve_path_order"] } + +# Serde +serde_json = "1.0.128" + +# Utilities +utility = { path = "utility" } diff --git a/acceptance/generics/Cargo.toml b/acceptance/generics/Cargo.toml index 4bcc7ac..d7c02a1 100644 --- a/acceptance/generics/Cargo.toml +++ b/acceptance/generics/Cargo.toml @@ -15,7 +15,7 @@ unused = "allow" [dependencies] utoipa.workspace = true -utoipauto = { workspace = true, features = ["schema_discovery"]} +utoipauto = { workspace = true } [dev-dependencies] -serde_json = "1.0.128" +utility.workspace = true diff --git a/acceptance/generics/src/main.rs b/acceptance/generics/src/main.rs index f4cc5e3..59d1fc9 100644 --- a/acceptance/generics/src/main.rs +++ b/acceptance/generics/src/main.rs @@ -21,17 +21,10 @@ fn main() { #[cfg(test)] mod tests { use super::*; - use serde_json::Value; + use utility::assert_json_eq; pub(crate) const EXPECTED_OPEN_API: &str = include_str!("open_api.expected.json"); - fn assert_json_eq(actual: &str, expected: &str) { - let actual_value: Value = serde_json::from_str(actual).expect("Invalid JSON in actual"); - let expected_value: Value = serde_json::from_str(expected).expect("Invalid JSON in expected"); - - assert_eq!(actual_value, expected_value, "JSON objects are not equal"); - } - #[test] fn test_openapi() { let open_api = ApiDoc::openapi().to_json().unwrap(); diff --git a/acceptance/responses/Cargo.toml b/acceptance/responses/Cargo.toml index 4bc2368..ef4970d 100644 --- a/acceptance/responses/Cargo.toml +++ b/acceptance/responses/Cargo.toml @@ -14,6 +14,12 @@ homepage.workspace = true unused = "allow" [dependencies] +# Utoipa utoipa.workspace = true utoipauto.workspace = true -serde_json = "1.0.128" + +# Serde +serde_json.workspace = true + +# Utility +utility.workspace = true diff --git a/acceptance/responses/src/main.rs b/acceptance/responses/src/main.rs index 9512cc1..aeaf940 100644 --- a/acceptance/responses/src/main.rs +++ b/acceptance/responses/src/main.rs @@ -19,20 +19,10 @@ fn main() { #[cfg(test)] mod tests { use crate::ApiDoc; - use serde_json::Value; use utoipa::OpenApi; + use utility::assert_json_eq; pub(crate) const EXPECTED_OPEN_API: &str = include_str!("open_api.expected.json"); - - fn assert_json_eq(actual: &str, expected: &str) { - let actual_value: Value = serde_json::from_str(actual).expect("Invalid JSON in actual"); - let expected_value: Value = serde_json::from_str(expected).expect("Invalid JSON in expected"); - - println!("Actual: {}", actual); - - assert_eq!(actual_value, expected_value, "JSON objects are not equal"); - } - #[test] fn test_open_api() { let open_api = ApiDoc::openapi().to_json().unwrap(); diff --git a/acceptance/utility/Cargo.toml b/acceptance/utility/Cargo.toml new file mode 100644 index 0000000..9529cb8 --- /dev/null +++ b/acceptance/utility/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "utility" +description = "Utility functions for the acceptance tests" +authors.workspace = true +version.workspace = true +edition.workspace = true +publish.workspace = true +readme.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true + +[dependencies] +serde_json.workspace = true diff --git a/acceptance/utility/src/lib.rs b/acceptance/utility/src/lib.rs new file mode 100644 index 0000000..3ee21df --- /dev/null +++ b/acceptance/utility/src/lib.rs @@ -0,0 +1,8 @@ +use serde_json::Value; + +pub fn assert_json_eq(actual: &str, expected: &str) { + let actual_value: Value = serde_json::from_str(actual).expect("Invalid JSON in actual"); + let expected_value: Value = serde_json::from_str(expected).expect("Invalid JSON in expected"); + + assert_eq!(actual_value, expected_value, "JSON objects are not equal"); +} diff --git a/utoipauto-core/Cargo.toml b/utoipauto-core/Cargo.toml index 9cf338e..7ad9d69 100644 --- a/utoipauto-core/Cargo.toml +++ b/utoipauto-core/Cargo.toml @@ -12,9 +12,6 @@ repository.workspace = true homepage.workspace = true rust-version.workspace = true -[features] -schema_discovery = [] - [dependencies] quote.workspace = true syn.workspace = true diff --git a/utoipauto-core/src/discover.rs b/utoipauto-core/src/discover.rs index 9b57bac..37ea068 100644 --- a/utoipauto-core/src/discover.rs +++ b/utoipauto-core/src/discover.rs @@ -6,11 +6,6 @@ use quote::ToTokens; use syn::token::Comma; use syn::{punctuated::Punctuated, Attribute, GenericParam, Item, ItemFn, ItemImpl, Meta, Token}; -#[cfg(feature = "schema_discovery")] -pub(crate) const SHOULD_PARSE_SCHEMA: bool = true; -#[cfg(not(feature = "schema_discovery"))] -pub(crate) const SHOULD_PARSE_SCHEMA: bool = false; - /// Discover everything from a file, will explore folder recursively pub fn discover_from_file( src_path: String, @@ -43,6 +38,7 @@ pub fn discover_from_file( ) } +#[allow(unused)] enum DiscoverType { Fn(String), Model(String), @@ -111,19 +107,19 @@ fn parse_from_attr( .parse_args_with(Punctuated::::parse_terminated) .expect("Failed to parse derive attribute"); for nested_meta in nested { - if nested_meta.path().segments.len() == 2 { - if nested_meta.path().segments[0].ident == "utoipa" { - if nested_meta.path().segments[1].ident == "ToSchema" && SHOULD_PARSE_SCHEMA { - out.push(DiscoverType::Model(name.to_string())); - } else if nested_meta.path().segments[1].ident == "ToResponse" { - out.push(DiscoverType::Response(name.to_string())); - } + if nested_meta.path().segments.len() == 2 && nested_meta.path().segments[0].ident == "utoipa" { + match nested_meta.path().segments[1].ident.to_string().as_str() { + "ToSchema" => out.push(DiscoverType::Model(name.to_string())), + "ToResponse" => out.push(DiscoverType::Response(name.to_string())), + _ => {} + } + } else { + if nested_meta.path().is_ident(¶ms.schema_attribute_name) { + out.push(DiscoverType::Model(name.to_string())); + } + if nested_meta.path().is_ident(¶ms.response_attribute_name) { + out.push(DiscoverType::Response(name.to_string())); } - } else if nested_meta.path().is_ident(¶ms.schema_attribute_name) && SHOULD_PARSE_SCHEMA { - out.push(DiscoverType::Model(name.to_string())); - } - if nested_meta.path().is_ident(¶ms.response_attribute_name) { - out.push(DiscoverType::Response(name.to_string())); } } } @@ -137,7 +133,7 @@ fn parse_from_impl(im: &ItemImpl, module_base_path: &str, params: &Parameters) - .as_ref() .and_then(|trt| trt.1.segments.last().map(|p| p.ident.to_string())) .and_then(|impl_name| { - if impl_name.eq(params.schema_attribute_name.as_str()) && SHOULD_PARSE_SCHEMA { + return if impl_name.eq(params.schema_attribute_name.as_str()) { Some(vec![DiscoverType::CustomModelImpl(build_path( module_base_path, &im.self_ty.to_token_stream().to_string(), @@ -149,7 +145,7 @@ fn parse_from_impl(im: &ItemImpl, module_base_path: &str, params: &Parameters) - ))]) } else { None - } + }; }) .unwrap_or_default() } diff --git a/utoipauto-macro/Cargo.toml b/utoipauto-macro/Cargo.toml index a3b450e..1b602b0 100644 --- a/utoipauto-macro/Cargo.toml +++ b/utoipauto-macro/Cargo.toml @@ -15,9 +15,6 @@ rust-version.workspace = true [lib] proc-macro = true -[features] -schema_discovery = ["utoipauto-core/schema_discovery"] - [dependencies] utoipauto-core.workspace = true diff --git a/utoipauto/Cargo.toml b/utoipauto/Cargo.toml index 0758a92..3a7a9e4 100644 --- a/utoipauto/Cargo.toml +++ b/utoipauto/Cargo.toml @@ -12,9 +12,6 @@ repository.workspace = true homepage.workspace = true rust-version.workspace = true -[features] -schema_discovery = ["utoipauto-macro/schema_discovery"] - [dependencies] utoipauto-macro.workspace = true From 6f2fe4edaed24ab96b9b770d7913ccb1060dea0b Mon Sep 17 00:00:00 2001 From: Timon Klinkert Date: Mon, 14 Oct 2024 21:43:18 +0200 Subject: [PATCH 12/14] formatted and lint fixes --- utoipauto-core/src/discover.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utoipauto-core/src/discover.rs b/utoipauto-core/src/discover.rs index 37ea068..3fcbdf3 100644 --- a/utoipauto-core/src/discover.rs +++ b/utoipauto-core/src/discover.rs @@ -133,7 +133,7 @@ fn parse_from_impl(im: &ItemImpl, module_base_path: &str, params: &Parameters) - .as_ref() .and_then(|trt| trt.1.segments.last().map(|p| p.ident.to_string())) .and_then(|impl_name| { - return if impl_name.eq(params.schema_attribute_name.as_str()) { + if impl_name.eq(params.schema_attribute_name.as_str()) { Some(vec![DiscoverType::CustomModelImpl(build_path( module_base_path, &im.self_ty.to_token_stream().to_string(), @@ -145,7 +145,7 @@ fn parse_from_impl(im: &ItemImpl, module_base_path: &str, params: &Parameters) - ))]) } else { None - }; + } }) .unwrap_or_default() } From 7f56923ccddcb9868532b4e9a338d8da2a00e4a3 Mon Sep 17 00:00:00 2001 From: Timon Klinkert Date: Mon, 14 Oct 2024 21:55:13 +0200 Subject: [PATCH 13/14] Reintroduced tests and updated them to utoipa v5 --- utoipauto/Cargo.toml | 1 + .../controllers/controller1.rs | 9 ++ .../controllers/controller2.rs | 4 + .../controllers/controller3.rs | 5 + .../tests/default_features/controllers/mod.rs | 3 + utoipauto/tests/default_features/mod.rs | 3 + utoipauto/tests/default_features/models.rs | 74 ++++++++++ utoipauto/tests/default_features/test.rs | 130 ++++++++++++++++++ utoipauto/tests/test.rs | 1 + 9 files changed, 230 insertions(+) create mode 100644 utoipauto/tests/default_features/controllers/controller1.rs create mode 100644 utoipauto/tests/default_features/controllers/controller2.rs create mode 100644 utoipauto/tests/default_features/controllers/controller3.rs create mode 100644 utoipauto/tests/default_features/controllers/mod.rs create mode 100644 utoipauto/tests/default_features/mod.rs create mode 100644 utoipauto/tests/default_features/models.rs create mode 100644 utoipauto/tests/default_features/test.rs create mode 100644 utoipauto/tests/test.rs diff --git a/utoipauto/Cargo.toml b/utoipauto/Cargo.toml index 3a7a9e4..9694d3b 100644 --- a/utoipauto/Cargo.toml +++ b/utoipauto/Cargo.toml @@ -17,4 +17,5 @@ utoipauto-macro.workspace = true [dev-dependencies] utoipa.workspace = true +serde_json = "1.0.128" syn = { version = "2.0.74", features = ["extra-traits", "full"] } diff --git a/utoipauto/tests/default_features/controllers/controller1.rs b/utoipauto/tests/default_features/controllers/controller1.rs new file mode 100644 index 0000000..ca16e3c --- /dev/null +++ b/utoipauto/tests/default_features/controllers/controller1.rs @@ -0,0 +1,9 @@ +#![allow(dead_code)] // This code is used in the tests +use utoipauto_macro::utoipa_ignore; + +#[utoipa::path(post, path = "/route1")] +pub fn route1() {} + +#[utoipa_ignore] +#[utoipa::path(post, path = "/route-ignored")] +pub fn route_ignored() {} diff --git a/utoipauto/tests/default_features/controllers/controller2.rs b/utoipauto/tests/default_features/controllers/controller2.rs new file mode 100644 index 0000000..43480f7 --- /dev/null +++ b/utoipauto/tests/default_features/controllers/controller2.rs @@ -0,0 +1,4 @@ +#![allow(dead_code)] // This code is used in the tests + +#[utoipa::path(post, path = "/route3")] +pub fn route3() {} diff --git a/utoipauto/tests/default_features/controllers/controller3.rs b/utoipauto/tests/default_features/controllers/controller3.rs new file mode 100644 index 0000000..11888aa --- /dev/null +++ b/utoipauto/tests/default_features/controllers/controller3.rs @@ -0,0 +1,5 @@ +#![allow(dead_code)] +use utoipauto_macro::test_handler; + +#[test_handler] +pub fn route_custom() {} diff --git a/utoipauto/tests/default_features/controllers/mod.rs b/utoipauto/tests/default_features/controllers/mod.rs new file mode 100644 index 0000000..9b43b4c --- /dev/null +++ b/utoipauto/tests/default_features/controllers/mod.rs @@ -0,0 +1,3 @@ +pub mod controller1; +pub mod controller2; +pub mod controller3; diff --git a/utoipauto/tests/default_features/mod.rs b/utoipauto/tests/default_features/mod.rs new file mode 100644 index 0000000..e082fa6 --- /dev/null +++ b/utoipauto/tests/default_features/mod.rs @@ -0,0 +1,3 @@ +pub mod controllers; +pub mod models; +pub mod test; diff --git a/utoipauto/tests/default_features/models.rs b/utoipauto/tests/default_features/models.rs new file mode 100644 index 0000000..21ea9ec --- /dev/null +++ b/utoipauto/tests/default_features/models.rs @@ -0,0 +1,74 @@ +#![allow(dead_code)] + +use std::borrow::Cow; +use utoipa::openapi::schema::SchemaType; +use utoipa::openapi::{Response, ResponseBuilder, Type}; +// This code is used in the tests +use utoipa::{ + openapi::{ObjectBuilder, RefOr, Schema}, + PartialSchema, ToResponse, ToSchema, +}; +use utoipauto_macro::utoipa_ignore; + +#[derive(ToSchema)] +pub struct ModelSchema; +#[derive(ToResponse)] +pub struct ModelResponse; + +#[utoipa_ignore] +#[derive(ToSchema)] +pub struct IgnoredModelSchema; + +// Manual implementation of ToSchema +pub struct ModelSchemaImpl; + +impl PartialSchema for ModelSchemaImpl { + fn schema() -> RefOr { + ObjectBuilder::new().schema_type(SchemaType::Type(Type::String)).into() + } +} + +impl ToSchema for ModelSchemaImpl { + fn name() -> Cow<'static, str> { + Cow::Borrowed("ModelSchemaImpl") + } +} + +// Manual implementation of utoipa::ToSchema +pub struct ModelSchemaImplFullName; + +impl PartialSchema for ModelSchemaImplFullName { + fn schema() -> RefOr { + ObjectBuilder::new().schema_type(SchemaType::Type(Type::String)).into() + } +} + +impl utoipa::ToSchema for ModelSchemaImplFullName { + fn name() -> Cow<'static, str> { + Cow::Borrowed("ModelSchemaImplFullName") + } +} + +// Manual implementation of ToSchema +pub struct ModelResponseImpl; + +impl<'s> ToResponse<'s> for ModelResponseImpl { + fn response() -> (&'s str, RefOr) { + ( + "ModelResponseImpl", + ResponseBuilder::new().description("A manual response").into(), + ) + } +} + +// Manual implementation of utoipa::ToResponse +pub struct ModelResponseImplFullName; + +impl<'s> utoipa::ToResponse<'s> for ModelResponseImplFullName { + fn response() -> (&'s str, RefOr) { + ( + "ModelResponseImplFullName", + ResponseBuilder::new().description("A manual response").into(), + ) + } +} diff --git a/utoipauto/tests/default_features/test.rs b/utoipauto/tests/default_features/test.rs new file mode 100644 index 0000000..4ef1ed8 --- /dev/null +++ b/utoipauto/tests/default_features/test.rs @@ -0,0 +1,130 @@ +use utoipa::OpenApi; + +use utoipauto::utoipauto; + +use crate::default_features::controllers; + +// Discover from multiple controllers +#[utoipauto( + paths = "( crate::controllers::controller1 => ./utoipauto/tests/default_features/controllers/controller1.rs) ; ( crate::controllers::controller2 => ./utoipauto/tests/default_features/controllers/controller2.rs )" +)] +#[derive(OpenApi)] +#[openapi(info(title = "Percentage API", version = "1.0.0"))] +pub struct MultiControllerApiDocs {} + +#[test] +fn test_path_import() { + assert_eq!(MultiControllerApiDocs::openapi().paths.paths.len(), 2) +} + +/// Discover from a single controller +#[utoipauto( + paths = "( crate::controllers::controller1 => ./utoipauto/tests/default_features/controllers/controller1.rs)" +)] +#[derive(OpenApi)] +#[openapi(info(title = "Percentage API", version = "1.0.0"))] +pub struct SingleControllerApiDocs {} + +#[test] +fn test_ignored_path() { + assert_eq!(SingleControllerApiDocs::openapi().paths.paths.len(), 1) +} + +/// Discover with manual path +#[utoipauto(paths = "./utoipauto/tests/default_features/controllers/controller1.rs")] +#[derive(OpenApi)] +#[openapi( + info(title = "Percentage API", version = "1.0.0"), + paths(controllers::controller2::route3) +)] +pub struct SingleControllerManualPathApiDocs {} + +#[test] +fn test_manual_path() { + assert_eq!(SingleControllerManualPathApiDocs::openapi().paths.paths.len(), 2) +} + +/// Discover from a module root +#[utoipauto(paths = "( crate::controllers => ./utoipauto/tests/default_features/controllers)")] +#[derive(OpenApi)] +#[openapi(info(title = "Percentage API", version = "1.0.0"))] +pub struct ModuleApiDocs {} + +#[test] +fn test_module_import_path() { + assert_eq!(ModuleApiDocs::openapi().paths.paths.len(), 2) +} + +/// Discover from the crate root +#[utoipauto(paths = "./utoipauto/tests/default_features")] +#[derive(OpenApi)] +#[openapi(info(title = "Percentage API", version = "1.0.0"))] +pub struct CrateApiDocs {} + +#[test] +fn test_crate_import_path() { + assert_eq!(CrateApiDocs::openapi().paths.paths.len(), 2) +} + +// Discover from multiple controllers new syntax +#[utoipauto( + paths = "./utoipauto/tests/default_features/controllers/controller1.rs, ./utoipauto/tests/default_features/controllers/controller2.rs" +)] +#[derive(OpenApi)] +#[openapi(info(title = "Percentage API", version = "1.0.0"))] +pub struct MultiControllerNoModuleApiDocs {} + +#[test] +fn test_path_import_no_module() { + assert_eq!(MultiControllerNoModuleApiDocs::openapi().paths.paths.len(), 2) +} + +// Discover from multiple controllers new syntax +#[utoipauto(paths = "./utoipauto/tests/default_features/models.rs")] +#[derive(OpenApi)] +#[openapi(info(title = "Percentage API", version = "1.0.0"))] +pub struct ModelsImportApiDocs {} + +#[test] +fn test_path_import_schema() { + assert_eq!( + ModelsImportApiDocs::openapi() + .components + .expect("no components") + .schemas + .len(), + 3, // 1 derive, 1 manual, 1 manual with utoipa::ToSchema + ) +} + +// Discover from multiple controllers new syntax +#[utoipauto(paths = "./utoipauto/tests/default_features/models.rs")] +#[derive(OpenApi)] +#[openapi(info(title = "Percentage API", version = "1.0.0"))] +pub struct ResponsesImportApiDocs {} + +#[test] +fn test_path_import_responses() { + assert_eq!( + ResponsesImportApiDocs::openapi() + .components + .expect("no components") + .responses + .len(), + 3, // 1 derive, 1 manual, 1 manual with utoipa::ToResponse + ) +} + +/// Discover custom handler +#[utoipauto( + paths = "./utoipauto/tests/default_features/controllers/controller3.rs", + function_attribute_name = "test_handler" +)] +#[derive(OpenApi)] +#[openapi(info(title = "Percentage API", version = "1.0.0"))] +pub struct CustomHandlerApiDocs {} + +#[test] +fn test_custom_handler() { + assert_eq!(CustomHandlerApiDocs::openapi().paths.paths.len(), 1) +} diff --git a/utoipauto/tests/test.rs b/utoipauto/tests/test.rs new file mode 100644 index 0000000..3a97363 --- /dev/null +++ b/utoipauto/tests/test.rs @@ -0,0 +1 @@ +mod default_features; From b7e3d4e670f4620d191549a99116ace7bb7fdac1 Mon Sep 17 00:00:00 2001 From: Timon Klinkert Date: Mon, 14 Oct 2024 21:58:49 +0200 Subject: [PATCH 14/14] Removed documentation about generic support --- README.md | 75 +++++++++++++++---------------------------------------- 1 file changed, 20 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index a4ebd3c..8a8502b 100644 --- a/README.md +++ b/README.md @@ -95,33 +95,6 @@ The paths receives a String which must respect this structure : You can add several paths by separating them with a coma `","`. -## Support for generic schemas - -We support generic schemas, but with a few drawbacks. -
-If you want to use generics, you have three ways to do it. - -1. use the full path - -```rust -#[aliases(GenericSchema = path::to::Generic)] -``` - -2. Import where utoipauto lives - -```rust -use path::to::schema; -``` - -3. use `generic_full_path` feature - -Please keep in mind that this feature causes more build-time overhead. -Higher RAM usage, longer compile times and excessive disk usage (especially on larger projects) are the consequences. - -```toml -utoipauto = { version = "*", feature = ["generic_full_path"] } -``` - ## Usage with workspaces If you are using a workspace, you must specify the name of the crate in the path. @@ -174,8 +147,8 @@ Here's an example of how to add all the methods and structs contained in the res use utoipauto::utoipauto; #[utoipauto( - paths = "./src/rest" - )] + paths = "./src/rest" +)] #[derive(OpenApi)] #[openapi( tags( @@ -195,8 +168,8 @@ Here's an example of how to add all methods and structs contained in the rest mo use utoipauto::utoipauto; #[utoipauto( - paths = "(./src/lib/rest from crate::rest)" - )] + paths = "(./src/lib/rest from crate::rest)" +)] #[derive(OpenApi)] #[openapi( tags( @@ -220,8 +193,8 @@ other_controller::get_users", and a schema "TestDTO". use utoipauto::utoipauto; #[utoipauto( - paths = "./src/rest/test_controller.rs,./src/rest/test2_controller.rs " - )] + paths = "./src/rest/test_controller.rs,./src/rest/test2_controller.rs " +)] #[derive(OpenApi)] #[openapi( paths( @@ -251,17 +224,17 @@ ex: ```rust /// Get all pets from database - /// - #[utoipa_ignore] //<============== this Macro - #[utoipa::path( - responses( +/// +#[utoipa_ignore] //<============== this Macro +#[utoipa::path( + responses( (status = 200, description = "List all Pets", body = [ListPetsDTO]) - ) - )] - #[get("/pets")] - async fn get_all_pets(req: HttpRequest, store: web::Data) -> impl Responder { - // your CODE - } + ) +)] +#[get("/pets")] +async fn get_all_pets(req: HttpRequest, store: web::Data) -> impl Responder { + // your CODE +} ``` @@ -273,10 +246,10 @@ ex: ```rust #[utoipa_ignore] //<============== this Macro - #[derive(ToSchema)] - struct ModelToIgnore { - // your CODE - } +#[derive(ToSchema)] +struct ModelToIgnore { + // your CODE +} ``` @@ -324,11 +297,3 @@ Contributions are welcomed, feel free to submit a PR or an issue. ## Inspiration Inspired by [utoipa_auto_discovery](https://github.com/rxdiscovery/utoipa_auto_discovery) - -``` - -``` - -``` - -```