use proc_macro2::TokenStream;
use quote::quote;
use crate::emit::Targets;
use crate::emit::doc_attr;
use crate::emit::emit_multipart_struct;
use crate::emit::emit_negotiated_body_enum;
use crate::emit::emit_type;
use crate::emit::models::DEBUG_AND_CLONE;
use crate::emit::models::DEBUG_CLONE_AND_EQ;
use crate::emit::models::ModelDerives;
use crate::emit::models::SerdeDerives;
use crate::emit::models::emit_struct;
use crate::emit::models::plain_derive_attr;
use crate::emit::usage::ForeignResolver;
use crate::error::Result;
use crate::ir::Cookies;
use crate::ir::Headers;
use crate::ir::Operation;
use crate::ir::RequestPayload;
use crate::ir::ResponseBody;
use crate::ir::ResponseCase;
use crate::ir::ResponseStatus;
use crate::ir::RustType;
pub fn emit_operation_types(
operation: &Operation,
targets: Targets,
foreign: &ForeignResolver,
) -> Result<Vec<TokenStream>> {
let mut items = Vec::new();
if let Some(query) = &operation.query {
let serde = SerdeDerives {
serialize: false,
deserialize: targets.server,
};
let field_types = query.fields.iter().map(|field| return &field.ty);
let derives = ModelDerives {
serde,
foreign: foreign.of_types(field_types),
};
items.push(emit_struct(query, derives)?);
}
if let Some(headers) = &operation.headers {
items.push(emit_headers_struct(headers, foreign)?);
}
if let Some(cookies) = &operation.cookies {
items.push(emit_cookies_struct(cookies, foreign)?);
}
match &operation.request {
Some(RequestPayload::Multipart(multipart)) => items.push(emit_multipart_struct(multipart, foreign)?),
Some(RequestPayload::Negotiated(request)) => items.push(emit_negotiated_body_enum(request, foreign)?),
Some(RequestPayload::Single(_)) | None => {}
}
for case in &operation.responses {
if let Some(ResponseBody::Negotiated(body)) = &case.body {
items.push(emit_negotiated_body_enum(body, foreign)?);
}
}
items.push(emit_response_enum(operation, foreign)?);
return Ok(items);
}
fn emit_headers_struct(headers: &Headers, foreign: &ForeignResolver) -> Result<TokenStream> {
let name = headers.name.to_token();
let derive_attr = plain_derive_attr(
DEBUG_AND_CLONE,
foreign.of_types(headers.params.iter().map(|param| return ¶m.ty)),
);
let mut fields = Vec::with_capacity(headers.params.len());
for param in &headers.params {
let field = param.name.to_token();
let doc = doc_attr(¶m.doc);
let mut ty = emit_type(¶m.ty)?;
if !param.required {
ty = quote! { Option<#ty> };
}
fields.push(quote! { #doc pub #field: #ty });
}
return Ok(quote! {
#derive_attr
pub struct #name {
#(#fields),*
}
});
}
fn emit_cookies_struct(cookies: &Cookies, foreign: &ForeignResolver) -> Result<TokenStream> {
let name = cookies.name.to_token();
let derive_attr = plain_derive_attr(
DEBUG_AND_CLONE,
foreign.of_types(cookies.params.iter().map(|param| return ¶m.ty)),
);
let mut fields = Vec::with_capacity(cookies.params.len());
for param in &cookies.params {
let field = param.name.to_token();
let doc = doc_attr(¶m.doc);
let mut ty = emit_type(¶m.ty)?;
if !param.required {
ty = quote! { Option<#ty> };
}
fields.push(quote! { #doc pub #field: #ty });
}
return Ok(quote! {
#derive_attr
pub struct #name {
#(#fields),*
}
});
}
fn emit_response_enum(operation: &Operation, foreign: &ForeignResolver) -> Result<TokenStream> {
let name = operation.response_enum.to_token();
let doc = doc_attr(&operation.doc);
let derive_attr = plain_derive_attr(
DEBUG_CLONE_AND_EQ,
foreign.of_types(response_enum_types(operation).iter()),
);
let mut variants = Vec::with_capacity(operation.responses.len());
for case in &operation.responses {
let case_doc = doc_attr(&case.doc);
let variant_def = response_variant_def(case)?;
variants.push(quote! { #case_doc #variant_def });
}
return Ok(quote! {
#doc
#derive_attr
pub enum #name {
#(#variants),*
}
});
}
fn response_enum_types(operation: &Operation) -> Vec<RustType> {
let mut types = Vec::new();
for case in &operation.responses {
match &case.body {
Some(ResponseBody::Single(body)) => types.push(body.ty.clone()),
Some(ResponseBody::Negotiated(negotiated)) => {
for variant in &negotiated.variants {
types.push(variant.body.ty.clone());
}
}
None => {}
}
for header in &case.headers {
types.push(header.ty.clone());
}
}
return types;
}
fn response_variant_def(case: &ResponseCase) -> Result<TokenStream> {
let variant = case.variant.to_token();
let dynamic = !matches!(case.status, ResponseStatus::Fixed(_));
let body_ty = response_body_type(&case.body)?;
if case.headers.is_empty() {
let variant_def = match (&body_ty, dynamic) {
(None, false) => quote! { #variant },
(None, true) => quote! { #variant(http::StatusCode) },
(Some(ty), false) => quote! { #variant(#ty) },
(Some(ty), true) => quote! { #variant(http::StatusCode, #ty) },
};
return Ok(variant_def);
}
let mut field_defs = Vec::new();
if dynamic {
field_defs.push(quote! { status: http::StatusCode });
}
if let Some(ty) = &body_ty {
field_defs.push(quote! { body: #ty });
}
for header in &case.headers {
let field = header.name.to_token();
let ty = emit_type(&header.ty)?;
let doc = doc_attr(&header.doc);
let field_ty = if header.required {
quote! { #ty }
} else {
quote! { Option<#ty> }
};
field_defs.push(quote! { #doc #field: #field_ty });
}
return Ok(quote! { #variant { #(#field_defs),* } });
}
fn response_body_type(body: &Option<ResponseBody>) -> Result<Option<TokenStream>> {
return Ok(match body {
Some(ResponseBody::Single(body)) => Some(emit_type(&body.ty)?),
Some(ResponseBody::Negotiated(negotiated)) => {
let ident = negotiated.name.to_token();
Some(quote! { #ident })
}
None => None,
});
}