diff --git a/utoipa-gen/src/component.rs b/utoipa-gen/src/component.rs index 792465e8..7751462b 100644 --- a/utoipa-gen/src/component.rs +++ b/utoipa-gen/src/component.rs @@ -275,6 +275,7 @@ impl<'t> TypeTree<'t> { #[cfg(feature = "indexmap")] "IndexMap" => Some(GenericType::Map), "Vec" => Some(GenericType::Vec), + "BTreeSet" | "HashSet" => Some(GenericType::Set), "LinkedList" => Some(GenericType::LinkedList), #[cfg(feature = "smallvec")] "SmallVec" => Some(GenericType::SmallVec), @@ -393,6 +394,7 @@ pub enum ValueType { pub enum GenericType { Vec, LinkedList, + Set, #[cfg(feature = "smallvec")] SmallVec, Map, @@ -503,6 +505,14 @@ impl<'c> ComponentSchema { description_stream, deprecated_stream, ), + Some(GenericType::Set) => ComponentSchema::vec_to_tokens( + &mut tokens, + features, + type_tree, + object_name, + description_stream, + deprecated_stream, + ), #[cfg(feature = "smallvec")] Some(GenericType::SmallVec) => ComponentSchema::vec_to_tokens( &mut tokens, @@ -668,6 +678,8 @@ impl<'c> ComponentSchema { child }; + let unique = matches!(type_tree.generic_type, Some(GenericType::Set)); + // is octet-stream let schema = if child .path @@ -689,9 +701,17 @@ impl<'c> ComponentSchema { object_name, }); + let unique = match unique { + true => quote! { + .unique_items(true) + }, + false => quote! {}, + }; + quote! { utoipa::openapi::schema::ArrayBuilder::new() .items(#component_schema) + #unique } }; diff --git a/utoipa-gen/src/path.rs b/utoipa-gen/src/path.rs index cdd4a93a..abc9894f 100644 --- a/utoipa-gen/src/path.rs +++ b/utoipa-gen/src/path.rs @@ -590,7 +590,7 @@ impl PathTypeTree for TypeTree<'_> { /// Check whether [`TypeTree`] is a Vec, slice, array or other supported array type fn is_array(&self) -> bool { match self.generic_type { - Some(GenericType::Vec) => true, + Some(GenericType::Vec | GenericType::Set) => true, Some(_) => self .children .as_ref() diff --git a/utoipa-gen/tests/schema_derive_test.rs b/utoipa-gen/tests/schema_derive_test.rs index d1283c79..d1cb518e 100644 --- a/utoipa-gen/tests/schema_derive_test.rs +++ b/utoipa-gen/tests/schema_derive_test.rs @@ -4918,6 +4918,66 @@ fn derive_struct_with_rc() { ) } +#[test] +fn derive_btreeset() { + use std::collections::BTreeSet; + + let greeting = api_doc! { + struct Greeting { + values: BTreeSet, + } + }; + + assert_json_eq!( + greeting, + json!({ + "properties": { + "values": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + } + }, + }, + "required": [ + "values" + ], + "type": "object" + }) + ) +} + +#[test] +fn derive_hashset() { + use std::collections::HashSet; + + let greeting = api_doc! { + struct Greeting { + values: HashSet, + } + }; + + assert_json_eq!( + greeting, + json!({ + "properties": { + "values": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + } + }, + }, + "required": [ + "values" + ], + "type": "object" + }) + ) +} + #[test] fn derive_doc_hidden() { let map = api_doc! {