utoipa_gen/path/response/
link.rs

1use proc_macro2::TokenStream;
2use quote::{quote, ToTokens};
3use syn::parse::Parse;
4use syn::punctuated::Punctuated;
5use syn::token::Comma;
6use syn::{Ident, Token};
7
8use crate::openapi::Server;
9use crate::{parse_utils, AnyValue};
10
11/// ("name" = (link))
12#[cfg_attr(feature = "debug", derive(Debug))]
13pub struct LinkTuple(pub parse_utils::LitStrOrExpr, pub Link);
14
15impl Parse for LinkTuple {
16    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
17        let inner;
18        syn::parenthesized!(inner in input);
19
20        let name = inner.parse::<parse_utils::LitStrOrExpr>()?;
21        inner.parse::<Token![=]>()?;
22        let value = inner.parse::<Link>()?;
23
24        Ok(LinkTuple(name, value))
25    }
26}
27
28/// (operation_ref = "", operation_id = "",
29///     parameters(
30///          ("name" = value),
31///          ("name" = value)
32///     ),
33///     request_body = value,
34///     description = "",
35///     server(...)
36/// )
37#[derive(Default)]
38#[cfg_attr(feature = "debug", derive(Debug))]
39pub struct Link {
40    operation_ref: Option<parse_utils::LitStrOrExpr>,
41    operation_id: Option<parse_utils::LitStrOrExpr>,
42    parameters: Punctuated<LinkParameter, Comma>,
43    request_body: Option<AnyValue>,
44    description: Option<parse_utils::LitStrOrExpr>,
45    server: Option<Server>,
46}
47
48impl Parse for Link {
49    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
50        let inner;
51        syn::parenthesized!(inner in input);
52        let mut link = Link::default();
53
54        while !inner.is_empty() {
55            let ident = inner.parse::<Ident>()?;
56            let attribute = &*ident.to_string();
57
58            match attribute {
59                "operation_ref" => link.operation_ref = Some(parse_utils::parse_next_literal_str_or_expr(&inner)?),
60                "operation_id" => link.operation_id = Some(parse_utils::parse_next_literal_str_or_expr(&inner)?),
61                "parameters" => {
62                    link.parameters = parse_utils::parse_comma_separated_within_parenthesis(&inner)?;
63                },
64                "request_body" => link.request_body = Some(parse_utils::parse_next(&inner, || { AnyValue::parse_any(&inner)})?),
65                "description" => link.description = Some(parse_utils::parse_next_literal_str_or_expr(&inner)?),
66                "server" => link.server = Some(inner.call(Server::parse)?),
67                _ => return Err(syn::Error::new(ident.span(), format!("unexpected attribute: {attribute}, expected any of: operation_ref, operation_id, parameters, request_body, description, server")))
68            }
69
70            if !inner.is_empty() {
71                inner.parse::<Token![,]>()?;
72            }
73        }
74
75        Ok(link)
76    }
77}
78
79impl ToTokens for Link {
80    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
81        let operation_ref = self
82            .operation_ref
83            .as_ref()
84            .map(|operation_ref| quote! { .operation_ref(#operation_ref)});
85
86        let operation_id = self
87            .operation_id
88            .as_ref()
89            .map(|operation_id| quote! { .operation_id(#operation_id)});
90
91        let parameters =
92            self.parameters
93                .iter()
94                .fold(TokenStream::new(), |mut params, parameter| {
95                    let name = &parameter.name;
96                    let value = &parameter.value;
97                    params.extend(quote! { .parameter(#name, #value) });
98
99                    params
100                });
101
102        let request_body = self
103            .request_body
104            .as_ref()
105            .map(|request_body| quote! { .request_body(Some(#request_body)) });
106
107        let description = self
108            .description
109            .as_ref()
110            .map(|description| quote! { .description(#description) });
111
112        let server = self
113            .server
114            .as_ref()
115            .map(|server| quote! { .server(Some(#server)) });
116
117        tokens.extend(quote! {
118            utoipa::openapi::link::Link::builder()
119                #operation_ref
120                #operation_id
121                #parameters
122                #request_body
123                #description
124                #server
125                .build()
126        })
127    }
128}
129
130/// ("foobar" = json!(...))
131#[cfg_attr(feature = "debug", derive(Debug))]
132pub struct LinkParameter {
133    name: parse_utils::LitStrOrExpr,
134    value: parse_utils::LitStrOrExpr,
135}
136
137impl Parse for LinkParameter {
138    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
139        let inner;
140        syn::parenthesized!(inner in input);
141        let name = inner.parse::<parse_utils::LitStrOrExpr>()?;
142
143        inner.parse::<Token![=]>()?;
144
145        let value = inner.parse::<parse_utils::LitStrOrExpr>()?;
146
147        Ok(LinkParameter { name, value })
148    }
149}