Skip to content

Commit f893e07

Browse files
committed
Implement include_str! for tags
1 parent 4b32ba9 commit f893e07

File tree

5 files changed

+65
-40
lines changed

5 files changed

+65
-40
lines changed

utoipa-gen/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ utoipa = { path = "../utoipa", features = ["debug", "uuid"], default-features =
2727
serde_json = "1"
2828
serde = "1"
2929
actix-web = { version = "4", features = ["macros"], default-features = false }
30-
axum = { version = "0.7", default-features = false }
30+
axum = { version = "0.7", default-features = false, features = ["json", "query"] }
3131
paste = "1"
3232
rocket = { version = "0.5", features = ["json"] }
3333
smallvec = { version = "1.10", features = ["serde"] }

utoipa-gen/src/lib.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2776,7 +2776,7 @@ mod parse_utils {
27762776
use std::fmt::Display;
27772777

27782778
use proc_macro2::{Group, Ident, TokenStream};
2779-
use quote::ToTokens;
2779+
use quote::{quote, ToTokens};
27802780
use syn::{
27812781
parenthesized,
27822782
parse::{Parse, ParseStream},
@@ -2844,6 +2844,10 @@ mod parse_utils {
28442844
Ok(parse_next(input, || input.parse::<LitStr>())?.value())
28452845
}
28462846

2847+
pub fn parse_next_literal_str_or_include_str(input: ParseStream) -> syn::Result<Str> {
2848+
Ok(parse_next(input, || input.parse::<Str>())?)
2849+
}
2850+
28472851
pub fn parse_next_literal_str_or_expr(input: ParseStream) -> syn::Result<Value> {
28482852
parse_next(input, || Value::parse(input)).map_err(|error| {
28492853
syn::Error::new(
@@ -2911,4 +2915,40 @@ mod parse_utils {
29112915
))
29122916
}
29132917
}
2918+
2919+
#[derive(Clone)]
2920+
#[cfg_attr(feature = "debug", derive(Debug))]
2921+
pub(super) enum Str {
2922+
String(String),
2923+
IncludeStr(TokenStream),
2924+
}
2925+
2926+
impl Parse for Str {
2927+
fn parse(input: ParseStream) -> syn::Result<Self> {
2928+
if input.peek(LitStr) {
2929+
Ok(Self::String(input.parse::<LitStr>()?.value()))
2930+
} else {
2931+
let include_str = input.parse::<Ident>()?;
2932+
let bang = input.parse::<Option<Token![!]>>()?;
2933+
if include_str != "include_str" || bang.is_none() {
2934+
return Err(Error::new(
2935+
include_str.span(),
2936+
"unexpected token, expected either literal string or include_str!(...)",
2937+
));
2938+
}
2939+
Ok(Self::IncludeStr(input.parse::<Group>()?.stream()))
2940+
}
2941+
}
2942+
}
2943+
2944+
impl ToTokens for Str {
2945+
fn to_tokens(&self, tokens: &mut TokenStream) {
2946+
match self {
2947+
Self::String(str) => str.to_tokens(tokens),
2948+
Self::IncludeStr(include_str) => {
2949+
tokens.extend(quote! { include_str!(#include_str) })
2950+
}
2951+
}
2952+
}
2953+
}
29142954
}

utoipa-gen/src/openapi.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use syn::{
1111
use proc_macro2::TokenStream;
1212
use quote::{format_ident, quote, quote_spanned, ToTokens};
1313

14+
use crate::parse_utils::Str;
1415
use crate::{
1516
parse_utils, path::PATH_STRUCT_PREFIX, security_requirement::SecurityRequirementsAttr, Array,
1617
ExternalDocs, ResultExt,
@@ -178,7 +179,7 @@ impl Parse for Modifier {
178179
#[cfg_attr(feature = "debug", derive(Debug))]
179180
struct Tag {
180181
name: String,
181-
description: Option<String>,
182+
description: Option<Str>,
182183
external_docs: Option<ExternalDocs>,
183184
}
184185

@@ -198,7 +199,8 @@ impl Parse for Tag {
198199
match attribute_name {
199200
"name" => tag.name = parse_utils::parse_next_literal_str(input)?,
200201
"description" => {
201-
tag.description = Some(parse_utils::parse_next_literal_str(input)?)
202+
tag.description =
203+
Some(parse_utils::parse_next_literal_str_or_include_str(input)?)
202204
}
203205
"external_docs" => {
204206
let content;

utoipa-gen/src/openapi/info.rs

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,14 @@
11
use std::borrow::Cow;
22
use std::io;
33

4-
use proc_macro2::{Group, Ident, TokenStream as TokenStream2};
4+
use proc_macro2::{Ident, TokenStream as TokenStream2};
55
use quote::{quote, ToTokens};
66
use syn::parse::Parse;
77
use syn::token::Comma;
8-
use syn::{parenthesized, Error, LitStr, Token};
8+
use syn::{parenthesized, Error, LitStr};
99

1010
use crate::parse_utils;
11-
12-
#[derive(Clone)]
13-
#[cfg_attr(feature = "debug", derive(Debug))]
14-
pub(super) enum Str {
15-
String(String),
16-
IncludeStr(TokenStream2),
17-
}
18-
19-
impl Parse for Str {
20-
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
21-
if input.peek(LitStr) {
22-
Ok(Self::String(input.parse::<LitStr>()?.value()))
23-
} else {
24-
let include_str = input.parse::<Ident>()?;
25-
let bang = input.parse::<Option<Token![!]>>()?;
26-
if include_str != "include_str" || bang.is_none() {
27-
return Err(Error::new(
28-
include_str.span(),
29-
"unexpected token, expected either literal string or include_str!(...)",
30-
));
31-
}
32-
Ok(Self::IncludeStr(input.parse::<Group>()?.stream()))
33-
}
34-
}
35-
}
36-
37-
impl ToTokens for Str {
38-
fn to_tokens(&self, tokens: &mut TokenStream2) {
39-
match self {
40-
Self::String(str) => str.to_tokens(tokens),
41-
Self::IncludeStr(include_str) => tokens.extend(quote! { include_str!(#include_str) }),
42-
}
43-
}
44-
}
11+
use crate::parse_utils::Str;
4512

4613
#[derive(Default, Clone)]
4714
#[cfg_attr(feature = "debug", derive(Debug))]

utoipa-gen/tests/openapi_derive.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,22 @@ fn derive_openapi_tags() {
5858
}
5959
}
6060

61+
#[test]
62+
fn derive_openapi_tags_include_str() {
63+
#[derive(OpenApi)]
64+
#[openapi(tags(
65+
(name = "random::api", description = include_str!("testdata/openapi-derive-info-description")),
66+
))]
67+
struct ApiDoc;
68+
69+
let doc = serde_json::to_value(&ApiDoc::openapi()).unwrap();
70+
71+
assert_value! {doc=>
72+
"tags.[0].name" = r###""random::api""###, "Tags random_api name"
73+
"tags.[0].description" = r###""this is include description\n""###, "Tags random_api description"
74+
}
75+
}
76+
6177
#[test]
6278
fn derive_openapi_with_external_docs() {
6379
#[derive(OpenApi)]

0 commit comments

Comments
 (0)