utoipa_gen/path/
example.rs

1use proc_macro2::{Ident, TokenStream};
2use quote::{quote, ToTokens};
3use syn::parse::{Parse, ParseStream};
4use syn::token::Comma;
5use syn::{parenthesized, Error, LitStr, Token};
6
7use crate::{parse_utils, AnyValue};
8
9// (name = (summary = "...", description = "...", value = "..", external_value = "..."))
10#[derive(Default)]
11#[cfg_attr(feature = "debug", derive(Debug))]
12pub(super) struct Example {
13    pub(super) name: String,
14    pub(super) summary: Option<String>,
15    pub(super) description: Option<String>,
16    pub(super) value: Option<AnyValue>,
17    pub(super) external_value: Option<String>,
18}
19
20impl Parse for Example {
21    fn parse(input: ParseStream) -> syn::Result<Self> {
22        let example_stream;
23        parenthesized!(example_stream in input);
24        let mut example = Example {
25            name: example_stream.parse::<LitStr>()?.value(),
26            ..Default::default()
27        };
28        example_stream.parse::<Token![=]>()?;
29
30        let content;
31        parenthesized!(content in example_stream);
32
33        while !content.is_empty() {
34            let ident = content.parse::<Ident>()?;
35            let attribute_name = &*ident.to_string();
36            match attribute_name {
37                "summary" => {
38                    example.summary = Some(
39                        parse_utils::parse_next(&content, || content.parse::<LitStr>())?
40                            .value(),
41                    )
42                }
43                "description" => {
44                    example.description = Some(
45                        parse_utils::parse_next(&content, || content.parse::<LitStr>())?
46                            .value(),
47                    )
48                }
49                "value" => {
50                    example.value = Some(parse_utils::parse_next(&content, || {
51                        AnyValue::parse_json(&content)
52                    })?)
53                }
54                "external_value" => {
55                    example.external_value = Some(
56                        parse_utils::parse_next(&content, || content.parse::<LitStr>())?
57                            .value(),
58                    )
59                }
60                _ => {
61                    return Err(
62                        Error::new(
63                            ident.span(),
64                            format!("unexpected attribute: {attribute_name}, expected one of: summary, description, value, external_value")
65                        )
66                    )
67                }
68            }
69
70            if !content.is_empty() {
71                content.parse::<Comma>()?;
72            }
73        }
74
75        Ok(example)
76    }
77}
78
79impl ToTokens for Example {
80    fn to_tokens(&self, tokens: &mut TokenStream) {
81        let summary = self
82            .summary
83            .as_ref()
84            .map(|summary| quote!(.summary(#summary)));
85        let description = self
86            .description
87            .as_ref()
88            .map(|description| quote!(.description(#description)));
89        let value = self
90            .value
91            .as_ref()
92            .map(|value| quote!(.value(Some(#value))));
93        let external_value = self
94            .external_value
95            .as_ref()
96            .map(|external_value| quote!(.external_value(#external_value)));
97
98        tokens.extend(quote! {
99            utoipa::openapi::example::ExampleBuilder::new()
100                #summary
101                #description
102                #value
103                #external_value
104        })
105    }
106}