Skip to content

Commit

Permalink
Add support for Cow generic type
Browse files Browse the repository at this point in the history
  • Loading branch information
juhaku committed Jan 28, 2022
1 parent 7cf1153 commit d5e8cc5
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 17 deletions.
18 changes: 17 additions & 1 deletion tests/component_derive_test.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::HashMap, vec};
use std::{borrow::Cow, collections::HashMap, vec};

use serde_json::Value;
use utoipa::{Component, OpenApi};
Expand Down Expand Up @@ -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"
};
}
3 changes: 3 additions & 0 deletions tests/utoipa_gen_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#![cfg(feature = "actix_extras")]

use std::borrow::Cow;

use serde::{Deserialize, Serialize};
use utoipa::{Component, OpenApi};

Expand All @@ -25,6 +27,7 @@ enum StatusType {
#[derive(Serialize, Deserialize, Component)]
struct Simple {
greeting: &'static str,
cow: Cow<'static, str>,
}

mod pet_api {
Expand Down
41 changes: 25 additions & 16 deletions utoipa-gen/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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"
),
}
}
Expand All @@ -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,
}
}
Expand All @@ -395,6 +403,7 @@ enum GenericType {
Vec,
Map,
Option,
Cow,
}

#[cfg_attr(feature = "debug", derive(Debug))]
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit d5e8cc5

Please sign in to comment.