autapi-macros 0.0.1

Macros for autapi
Documentation
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use syn::{Data, DeriveInput, Fields, Ident, Type, spanned::Spanned};

pub struct ResponsesImpl {
    pub name: Ident,
    pub type_idents: Vec<Ident>,
    pub types: Vec<Type>,
}

pub fn derive_responses(input: DeriveInput) -> TokenStream {
    let Data::Enum(data) = input.data else {
        return syn::Error::new(input.span(), "has to be an enum").to_compile_error();
    };
    let mut type_idents = Vec::new();
    let mut types = Vec::new();
    for variant in data.variants {
        let Fields::Unnamed(fields) = variant.fields else {
            return syn::Error::new(variant.span(), "has to contain exactly one unnamed field")
                .to_compile_error();
        };
        if fields.unnamed.len() != 1 {
            return syn::Error::new(fields.span(), "has to contain exactly one unnamed field")
                .to_compile_error();
        }
        let ty = fields.unnamed.into_iter().next().unwrap().ty;
        type_idents.push(variant.ident);
        types.push(ty);
    }
    ResponsesImpl {
        name: input.ident,
        type_idents,
        types,
    }
    .into_token_stream()
}

impl ToTokens for ResponsesImpl {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let base = quote!(autapi);
        let ResponsesImpl {
            name,
            type_idents,
            types,
        } = self;

        tokens.extend(quote! {
            impl #base::DescribeResponse for #name {
                fn describe(
                    description: &mut #base::ResponsesDescription,
                    args: &#base::DescribeResponseArgs
                ) {
                    #(<#types as #base::DescribeResponse>::describe(description, args);)*
                }
                fn into_response(self) -> #base::axum::response::Response {
                    match self {
                        #(Self::#type_idents(response) =>
                            #base::DescribeResponse::into_response(response)),*
                    }
                }
            }

            #(
            impl From<#types> for #name {
                fn from(value: #types) -> Self {
                    Self::#type_idents(value)
                }
            }
            )*
        });
    }
}