Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allowing utoipa/utoipa-swagger-ui successfully build on Windows and made path proc macro attribute more permissive #830

Merged
merged 6 commits into from
Jan 2, 2024
31 changes: 23 additions & 8 deletions utoipa-gen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2793,6 +2793,28 @@ mod parse_utils {
Expr(Expr),
}

impl Value {
pub(crate) fn is_empty(&self) -> bool {
matches!(self, Self::LitStr(s) if s.value().is_empty())
}
}

impl Default for Value {
fn default() -> Self {
Self::LitStr(LitStr::new("", proc_macro2::Span::call_site()))
}
}

impl Parse for Value {
fn parse(input: ParseStream) -> syn::Result<Self> {
if input.peek(LitStr) {
Ok::<Value, Error>(Value::LitStr(input.parse::<LitStr>()?))
} else {
Ok(Value::Expr(input.parse::<Expr>()?))
}
}
}

impl ToTokens for Value {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Expand Down Expand Up @@ -2823,14 +2845,7 @@ mod parse_utils {
}

pub fn parse_next_literal_str_or_expr(input: ParseStream) -> syn::Result<Value> {
parse_next(input, || {
if input.peek(LitStr) {
Ok::<Value, Error>(Value::LitStr(input.parse::<LitStr>()?))
} else {
Ok(Value::Expr(input.parse::<Expr>()?))
}
})
.map_err(|error| {
parse_next(input, || Value::parse(input)).map_err(|error| {
syn::Error::new(
error.span(),
format!("expected literal string or expression argument: {error}"),
Expand Down
21 changes: 12 additions & 9 deletions utoipa-gen/src/path/request_body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ impl ToTokens for RequestBody<'_> {
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct RequestBodyAttr<'r> {
content: Option<PathType<'r>>,
content_type: Option<String>,
description: Option<String>,
content_type: Option<parse_utils::Value>,
description: Option<parse_utils::Value>,
example: Option<AnyValue>,
examples: Option<Punctuated<Example, Comma>>,
}
Expand Down Expand Up @@ -115,15 +115,15 @@ impl Parse for RequestBodyAttr<'_> {
}
"content_type" => {
request_body_attr.content_type =
Some(parse_utils::parse_next_literal_str(&group)?)
Some(parse_utils::parse_next_literal_str_or_expr(&group)?)
}
"description" => {
request_body_attr.description =
Some(parse_utils::parse_next_literal_str(&group)?)
Some(parse_utils::parse_next_literal_str_or_expr(&group)?)
}
"example" => {
request_body_attr.example = Some(parse_utils::parse_next(&group, || {
AnyValue::parse_json(&group)
AnyValue::parse_any(&group)
})?)
}
"examples" => {
Expand Down Expand Up @@ -210,10 +210,13 @@ impl ToTokens for RequestBodyAttr<'_> {
PathType::MediaType(body_type) => {
let type_tree = body_type.as_type_tree();
let required: Required = (!type_tree.is_option()).into();
let content_type = self
.content_type
.as_deref()
.unwrap_or_else(|| type_tree.get_default_content_type());
let content_type = match &self.content_type {
Some(content_type) => content_type.to_token_stream(),
None => {
let content_type = type_tree.get_default_content_type();
quote!(#content_type)
}
};
tokens.extend(quote! {
utoipa::openapi::request_body::RequestBodyBuilder::new()
.content(#content_type, #content.build())
Expand Down
35 changes: 16 additions & 19 deletions utoipa-gen/src/path/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ impl<'r> From<(ResponseStatus, ResponseValue<'r>)> for ResponseTuple<'r> {

pub struct DeriveResponsesAttributes<T> {
derive_value: T,
description: String,
description: parse_utils::Value,
}

impl<'r> From<DeriveResponsesAttributes<DeriveIntoResponsesValue>> for ResponseValue<'r> {
Expand Down Expand Up @@ -198,9 +198,9 @@ impl<'r> From<DeriveResponsesAttributes<Option<DeriveToResponseValue>>> for Resp
#[derive(Default)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct ResponseValue<'r> {
description: String,
description: parse_utils::Value,
response_type: Option<PathType<'r>>,
content_type: Option<Vec<String>>,
content_type: Option<Vec<parse_utils::Value>>,
headers: Vec<Header>,
example: Option<AnyValue>,
examples: Option<Punctuated<Example, Comma>>,
Expand All @@ -210,7 +210,7 @@ pub struct ResponseValue<'r> {
impl<'r> ResponseValue<'r> {
fn from_derive_to_response_value(
derive_value: DeriveToResponseValue,
description: String,
description: parse_utils::Value,
) -> Self {
Self {
description: if derive_value.description.is_empty() && !description.is_empty() {
Expand All @@ -228,7 +228,7 @@ impl<'r> ResponseValue<'r> {

fn from_derive_into_responses_value(
response_value: DeriveIntoResponsesValue,
description: String,
description: parse_utils::Value,
) -> Self {
ResponseValue {
description: if response_value.description.is_empty() && !description.is_empty() {
Expand Down Expand Up @@ -394,9 +394,9 @@ trait DeriveResponseValue: Parse {
#[derive(Default)]
#[cfg_attr(feature = "debug", derive(Debug))]
struct DeriveToResponseValue {
content_type: Option<Vec<String>>,
content_type: Option<Vec<parse_utils::Value>>,
headers: Vec<Header>,
description: String,
description: parse_utils::Value,
example: Option<(AnyValue, Ident)>,
examples: Option<(Punctuated<Example, Comma>, Ident)>,
}
Expand Down Expand Up @@ -467,9 +467,9 @@ impl Parse for DeriveToResponseValue {
#[derive(Default)]
struct DeriveIntoResponsesValue {
status: ResponseStatus,
content_type: Option<Vec<String>>,
content_type: Option<Vec<parse_utils::Value>>,
headers: Vec<Header>,
description: String,
description: parse_utils::Value,
example: Option<(AnyValue, Ident)>,
examples: Option<(Punctuated<Example, Comma>, Ident)>,
}
Expand Down Expand Up @@ -870,35 +870,32 @@ mod parse {
use syn::parse::ParseStream;
use syn::punctuated::Punctuated;
use syn::token::{Bracket, Comma};
use syn::{bracketed, parenthesized, LitStr, Result};
use syn::{bracketed, parenthesized, Result};

use crate::path::example::Example;
use crate::{parse_utils, AnyValue};

use super::Header;

#[inline]
pub(super) fn description(input: ParseStream) -> Result<String> {
parse_utils::parse_next_literal_str(input)
pub(super) fn description(input: ParseStream) -> Result<parse_utils::Value> {
parse_utils::parse_next_literal_str_or_expr(input)
}

#[inline]
pub(super) fn content_type(input: ParseStream) -> Result<Vec<String>> {
pub(super) fn content_type(input: ParseStream) -> Result<Vec<parse_utils::Value>> {
parse_utils::parse_next(input, || {
let look_content_type = input.lookahead1();
if look_content_type.peek(LitStr) {
Ok(vec![input.parse::<LitStr>()?.value()])
} else if look_content_type.peek(Bracket) {
if look_content_type.peek(Bracket) {
let content_types;
bracketed!(content_types in input);
Ok(
Punctuated::<LitStr, Comma>::parse_terminated(&content_types)?
Punctuated::<parse_utils::Value, Comma>::parse_terminated(&content_types)?
.into_iter()
.map(|lit| lit.value())
.collect(),
)
} else {
Err(look_content_type.error())
Ok(vec![input.parse::<parse_utils::Value>()?])
}
})
}
Expand Down
37 changes: 29 additions & 8 deletions utoipa-gen/src/path/response/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use syn::{
use crate::component::schema::{EnumSchema, NamedStructSchema};
use crate::doc_comment::CommentAttributes;
use crate::path::{InlineType, PathType};
use crate::{Array, ResultExt};
use crate::{parse_utils, Array, ResultExt};

use super::{
Content, DeriveIntoResponsesValue, DeriveResponseValue, DeriveResponsesAttributes,
Expand Down Expand Up @@ -241,7 +241,10 @@ impl<'u> UnnamedStructResponse<'u> {
}
let mut derive_value = DeriveIntoResponsesValue::from_attributes(attributes)
.expect("`IntoResponses` must have `#[response(...)]` attribute");
let description = CommentAttributes::from_attributes(attributes).as_formatted_string();
let description = {
let s = CommentAttributes::from_attributes(attributes).as_formatted_string();
parse_utils::Value::LitStr(LitStr::new(&s, Span::call_site()))
};
let status_code = mem::take(&mut derive_value.status);

match (ref_response, to_response) {
Expand Down Expand Up @@ -294,7 +297,10 @@ impl NamedStructResponse<'_> {

let mut derive_value = DeriveIntoResponsesValue::from_attributes(attributes)
.expect("`IntoResponses` must have `#[response(...)]` attribute");
let description = CommentAttributes::from_attributes(attributes).as_formatted_string();
let description = {
let s = CommentAttributes::from_attributes(attributes).as_formatted_string();
parse_utils::Value::LitStr(LitStr::new(&s, Span::call_site()))
};
let status_code = mem::take(&mut derive_value.status);

let inline_schema = NamedStructSchema {
Expand Down Expand Up @@ -335,7 +341,10 @@ impl UnitStructResponse<'_> {
let mut derive_value = DeriveIntoResponsesValue::from_attributes(attributes)
.expect("`IntoResponses` must have `#[response(...)]` attribute");
let status_code = mem::take(&mut derive_value.status);
let description = CommentAttributes::from_attributes(attributes).as_formatted_string();
let description = {
let s = CommentAttributes::from_attributes(attributes).as_formatted_string();
parse_utils::Value::LitStr(LitStr::new(&s, Span::call_site()))
};

Self(
(
Expand All @@ -360,7 +369,10 @@ impl<'p> ToResponseNamedStructResponse<'p> {
);

let derive_value = DeriveToResponseValue::from_attributes(attributes);
let description = CommentAttributes::from_attributes(attributes).as_formatted_string();
let description = {
let s = CommentAttributes::from_attributes(attributes).as_formatted_string();
parse_utils::Value::LitStr(LitStr::new(&s, Span::call_site()))
};
let ty = Self::to_type(ident);

let inline_schema = NamedStructSchema {
Expand Down Expand Up @@ -402,7 +414,10 @@ impl<'u> ToResponseUnnamedStructResponse<'u> {
}
});
let derive_value = DeriveToResponseValue::from_attributes(attributes);
let description = CommentAttributes::from_attributes(attributes).as_formatted_string();
let description = {
let s = CommentAttributes::from_attributes(attributes).as_formatted_string();
parse_utils::Value::LitStr(LitStr::new(&s, Span::call_site()))
};

let is_inline = inner_attributes
.iter()
Expand Down Expand Up @@ -444,7 +459,10 @@ impl<'r> EnumResponse<'r> {
);

let ty = Self::to_type(ident);
let description = CommentAttributes::from_attributes(attributes).as_formatted_string();
let description = {
let s = CommentAttributes::from_attributes(attributes).as_formatted_string();
parse_utils::Value::LitStr(LitStr::new(&s, Span::call_site()))
};

let variants_content = variants
.into_iter()
Expand Down Expand Up @@ -572,7 +590,10 @@ impl ToResponseUnitStructResponse<'_> {
Self::validate_attributes(attributes, Self::has_no_field_attributes);

let derive_value = DeriveToResponseValue::from_attributes(attributes);
let description = CommentAttributes::from_attributes(attributes).as_formatted_string();
let description = {
let s = CommentAttributes::from_attributes(attributes).as_formatted_string();
parse_utils::Value::LitStr(LitStr::new(&s, Span::call_site()))
};
let response_value: ResponseValue = ResponseValue::from(DeriveResponsesAttributes {
derive_value,
description,
Expand Down
2 changes: 1 addition & 1 deletion utoipa-swagger-ui/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ fn write_embed_code(target_dir: &str, swagger_version: &str) {
r#"
// This file is auto-generated during compilation, do not modify
#[derive(RustEmbed)]
#[folder = "{}/{}/dist/"]
#[folder = r"{}/{}/dist/"]
struct SwaggerUiDist;
"#,
target_dir, swagger_version
Expand Down