Skip to content

Commit

Permalink
Add descriptions to 2 variants of complex enums (#714)
Browse files Browse the repository at this point in the history
Add descriptions to 2 variants of complex enums - unit variants and tuple variants with Option as first element.
  • Loading branch information
lithiumFlower authored Aug 1, 2023
1 parent b1a5d4f commit cea4c50
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 2 deletions.
6 changes: 6 additions & 0 deletions utoipa-gen/src/component/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1345,6 +1345,12 @@ impl ToTokens for Description {
}
}

impl From<String> for Description {
fn from(value: String) -> Self {
Self(value)
}
}

impl From<Description> for Feature {
fn from(value: Description) -> Self {
Self::Description(value)
Expand Down
10 changes: 8 additions & 2 deletions utoipa-gen/src/component/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use syn::{
};

use crate::{
component::features::{Example, Rename},
component::features::{Description, Example, Rename},
doc_comment::CommentAttributes,
Array, Deprecated, ResultExt,
};
Expand Down Expand Up @@ -1007,7 +1007,12 @@ impl ComplexEnum<'_> {
rename_all,
);

let example = pop_feature!(unit_features => Feature::Example(_));
let example: Option<Feature> = pop_feature!(unit_features => Feature::Example(_));

let description =
CommentAttributes::from_attributes(&variant.attrs).as_formatted_string();
let description =
(!description.is_empty()).then(|| Feature::Description(description.into()));

// Unit variant is just simple enum with single variant.
Enum::new([SimpleEnumVariant {
Expand All @@ -1017,6 +1022,7 @@ impl ComplexEnum<'_> {
}])
.with_title(title.as_ref().map(ToTokens::to_token_stream))
.with_example(example.as_ref().map(ToTokens::to_token_stream))
.with_description(description.as_ref().map(ToTokens::to_token_stream))
.to_token_stream()
}
}
Expand Down
10 changes: 10 additions & 0 deletions utoipa-gen/src/component/schema/enum_variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ pub struct Enum<'e, V: Variant> {
items: Array<'e, TokenStream>,
schema_type: TokenStream,
enum_type: TokenStream,
description: Option<TokenStream>,
_p: PhantomData<V>,
}

Expand All @@ -109,6 +110,12 @@ impl<V: Variant> Enum<'_, V> {

self
}

pub fn with_description<I: Into<TokenStream>>(mut self, description: Option<I>) -> Self {
self.description = description.map(|description| description.into());

self
}
}

impl<T> ToTokens for Enum<'_, T>
Expand All @@ -122,10 +129,12 @@ where
let items = &self.items;
let schema_type = &self.schema_type;
let enum_type = &self.enum_type;
let description = &self.description;

tokens.extend(quote! {
utoipa::openapi::ObjectBuilder::new()
#title
#description
#example
.schema_type(#schema_type)
.enum_values::<[#enum_type; #len], #enum_type>(Some(#items))
Expand Down Expand Up @@ -154,6 +163,7 @@ impl<V: Variant> FromIterator<V> for Enum<'_, V> {
Self {
title: None,
example: None,
description: None,
len,
items,
schema_type,
Expand Down
146 changes: 146 additions & 0 deletions utoipa-gen/tests/schema_derive_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4827,3 +4827,149 @@ fn derive_doc_hidden() {
"properties.map.additionalProperties.type" = r#""string""#, "Additional Property Type"
};
}

#[test]
fn derive_schema_with_docstring_on_unit_varian_of_enum() {
let value: Value = api_doc! {
/// top level doc for My enum
#[derive(Serialize)]
enum MyEnum {
/// unit variant doc
UnitVariant,
/// non-unit doc
NonUnitVariant(String),
}
};

assert_json_eq!(
value,
json!({
"description": "top level doc for My enum",
"oneOf": [
{
"description": "unit variant doc",
"enum": [
"UnitVariant"
],
"type": "string"
},
{
"properties": {
"NonUnitVariant": {
"description": "non-unit doc",
"type": "string"
}
},
"required": [
"NonUnitVariant"
],
"type": "object"
}
]
})
);
}

#[test]
fn derive_schema_with_docstring_on_tuple_variant_first_element_option() {
let value: Value = api_doc! {
/// top level doc for My enum
enum MyEnum {
/// doc for tuple variant with Option as first element - I now produce a description
TupleVariantWithOptionFirst(Option<String>),

/// doc for tuple variant without Option as first element - I produce a description
TupleVariantWithNoOption(String),
}
};

assert_json_eq!(
value,
json!(
{
"oneOf": [
{
"type": "object",
"required": [
"TupleVariantWithOptionFirst"
],
"properties": {
"TupleVariantWithOptionFirst": {
"type": "string",
"nullable": true,
"description": "doc for tuple variant with Option as first element - I now produce a description"
}
}
},
{
"type": "object",
"required": [
"TupleVariantWithNoOption"
],
"properties": {
"TupleVariantWithNoOption": {
"type": "string",
"description": "doc for tuple variant without Option as first element - I produce a description"
}
}
}
],
"description": "top level doc for My enum"
}
)
);

let value: Value = api_doc! {
/// top level doc for My enum
enum MyEnum {
/// doc for tuple variant with Option as first element - I now produce a description
TupleVariantWithOptionFirst(Option<String>, String),

/// doc for tuple variant without Option as first element - I produce a description
TupleVariantWithOptionSecond(String, Option<String>),
}
};

assert_json_eq!(
value,
json!({
"description": "top level doc for My enum",
"oneOf": [
{
"properties": {
"TupleVariantWithOptionFirst": {
"description": "doc for tuple variant with Option as first element - I now produce a description",
"items": {
"type": "object"
},
"maxItems": 2,
"minItems": 2,
"type": "array"
}
},
"required": [
"TupleVariantWithOptionFirst"
],
"type": "object"
},
{
"properties": {
"TupleVariantWithOptionSecond": {
"description": "doc for tuple variant without Option as first element - I produce a description",
"items": {
"type": "object"
},
"maxItems": 2,
"minItems": 2,
"type": "array"
}
},
"required": [
"TupleVariantWithOptionSecond"
],
"type": "object"
}
]
})
);
}

0 comments on commit cea4c50

Please sign in to comment.