diff --git a/tests/component_derive_test.rs b/tests/component_derive_test.rs index 8e4a44f9..f8815e43 100644 --- a/tests/component_derive_test.rs +++ b/tests/component_derive_test.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, vec}; +use std::{borrow::Cow, collections::HashMap, vec}; use serde_json::Value; use utoipa::{Component, OpenApi}; @@ -452,3 +452,19 @@ fn derive_struct_with_lifetime_generics() { "properties.greeting.type" = r###""string""###, "Greeting greeting field type" }; } + +#[test] +fn derive_struct_with_cow() { + #[allow(unused)] + let greeting = api_doc! { + struct Greeting<'a> { + greeting: Cow<'a, str> + } + }; + + common::assert_json_array_len(greeting.get("required").unwrap(), 1); + assert_value! {greeting=> + "properties.greeting.type" = r###""string""###, "Greeting greeting field type" + "required.[0]" = r###""greeting""###, "Greeting required" + }; +} diff --git a/tests/utoipa_gen_test.rs b/tests/utoipa_gen_test.rs index b8178aee..83c08a70 100644 --- a/tests/utoipa_gen_test.rs +++ b/tests/utoipa_gen_test.rs @@ -1,5 +1,7 @@ #![cfg(feature = "actix_extras")] +use std::borrow::Cow; + use serde::{Deserialize, Serialize}; use utoipa::{Component, OpenApi}; @@ -25,6 +27,7 @@ enum StatusType { #[derive(Serialize, Deserialize, Component)] struct Simple { greeting: &'static str, + cow: Cow<'static, str>, } mod pet_api { diff --git a/utoipa-gen/src/component.rs b/utoipa-gen/src/component.rs index 537d633c..6e257aa9 100644 --- a/utoipa-gen/src/component.rs +++ b/utoipa-gen/src/component.rs @@ -4,8 +4,9 @@ use proc_macro2::{Ident, TokenStream as TokenStream2}; use proc_macro_error::{abort, abort_call_site, emit_error}; use quote::{quote, ToTokens}; use syn::{ - punctuated::Punctuated, token::Comma, Attribute, Data, Field, Fields, FieldsNamed, - FieldsUnnamed, GenericArgument, Generics, PathArguments, PathSegment, Type, TypePath, Variant, + punctuated::Punctuated, token::Comma, AngleBracketedGenericArguments, Attribute, Data, Field, + Fields, FieldsNamed, FieldsUnnamed, GenericArgument, Generics, PathArguments, PathSegment, + Type, TypePath, Variant, }; use crate::{ @@ -326,33 +327,39 @@ impl<'a> ComponentPart<'a> { fn resolve_component_type(segment: &'a PathSegment) -> ComponentPart<'a> { if segment.arguments.is_empty() { abort!( - segment.ident.span(), - "Expected at least one angle bracket argument but was 0" + segment.ident, + "expected at least one angle bracket argument but was 0" ); }; let mut generic_component_type = ComponentPart::convert(&segment.ident, segment); generic_component_type.child = Some(Rc::new(ComponentPart::from_type( - ComponentPart::get_first_generic_type(segment), + match &segment.arguments { + PathArguments::AngleBracketed(angle_bracketed_args) => { + ComponentPart::get_generic_arg_type(0, angle_bracketed_args) + } + _ => abort!( + segment.ident, + "unexpected path argument, expected angle bracketed path argument" + ), + }, ))); generic_component_type } - fn get_first_generic_type(segment: &PathSegment) -> &Type { - match &segment.arguments { - PathArguments::AngleBracketed(angle_bracketed_args) => { - let first_arg = angle_bracketed_args.args.first().unwrap(); + fn get_generic_arg_type(index: usize, args: &'a AngleBracketedGenericArguments) -> &'a Type { + let generic_arg = args.args.iter().nth(index); - match first_arg { - GenericArgument::Type(generic_type) => generic_type, - _ => abort!(segment.ident, "Expected GenericArgument::Type"), - } + match generic_arg { + Some(GenericArgument::Type(generic_type)) => generic_type, + Some(GenericArgument::Lifetime(_)) => { + ComponentPart::get_generic_arg_type(index + 1, args) } _ => abort!( - segment.ident, - "Unexpected PathArgument, expected PathArgument::AngleBracketed" + generic_arg, + "expected generic argument type or generic argument lifetime" ), } } @@ -377,6 +384,7 @@ impl<'a> ComponentPart<'a> { "HashMap" | "Map" | "BTreeMap" => Some(GenericType::Map), "Vec" => Some(GenericType::Vec), "Option" => Some(GenericType::Option), + "Cow" => Some(GenericType::Cow), _ => None, } } @@ -395,6 +403,7 @@ enum GenericType { Vec, Map, Option, + Cow, } #[cfg_attr(feature = "debug", derive(Debug))] @@ -460,7 +469,7 @@ where #component_property.to_array() }); } - Some(GenericType::Option) => { + Some(GenericType::Option) | Some(GenericType::Cow) => { let component_property = ComponentProperty::new( self.component_part.child.as_ref().unwrap(), self.comments,