auraxis_macros 0.1.0

Macros for working with the auraxis crate
Documentation
#![feature(proc_macro_diagnostic)]
#![allow(unused_variables)]
#![allow(dead_code)]

use darling::{ast::Data, util, FromDeriveInput, FromField, FromMeta};
use proc_macro::{Diagnostic, Level, TokenStream};
use quote::quote;
use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Ident, Type};

#[derive(Debug, FromField)]
#[darling(attributes(census))]
struct CensusQueryField {
    ident: Option<Ident>,
    ty: Type,
    #[darling(default)]
    main: bool,
}

#[derive(Debug, FromMeta)]
#[darling(default)]
struct CensusQueryUrl(String);

impl Default for CensusQueryUrl {
    fn default() -> Self {
        Self("https://census.daybreakgames.com".to_string())
    }
}

#[derive(Debug, FromDeriveInput)]
#[darling(attributes(census), forward_attrs(allow, doc, cfg))]
struct CensusQueryArgs {
    ident: syn::Ident,
    attrs: Vec<syn::Attribute>,
    data: Data<util::Ignored, CensusQueryField>,
    #[darling(default)]
    api_url: CensusQueryUrl,
    main: Option<String>,
}

#[proc_macro_derive(Query, attributes(census))]
pub fn derive_census_query(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    let args = CensusQueryArgs::from_derive_input(&input);

    match args {
        Ok(args) => {
            println!("{:?}", &args);

            match args.data {
                Data::Enum(_) => todo!(),
                Data::Struct(data) => {
                    let main_fields: Vec<&CensusQueryField> =
                        data.fields.iter().filter(|field| field.main).collect();

                    let main = if data.fields.len() == 1 {
                        Some(&data.fields[0])
                    } else if data.fields.is_empty() {
                        Diagnostic::spanned(args.ident.span().unwrap(), Level::Error, "Empty struct not allowed").emit();

                        None
                    } else {
                        match main_fields.len() {
                            0 => {
                                if let Some(main_field_name) = args.main {
                                    let main_field = data.fields.iter().find(|field| {
                                        *field.ident.as_ref().unwrap() == main_field_name
                                    });

                                    if main_field.is_none() {
                                        Diagnostic::spanned(
                                            args.ident.span().unwrap(),
                                            proc_macro::Level::Error,
                                            format!(
                                                "{} is not a valid field identifier",
                                                &main_field_name
                                            ),
                                        )
                                            .emit();
                                    }

                                    main_field
                                } else {
                                    Diagnostic::spanned(
                                        args.ident.span().unwrap(),
                                        proc_macro::Level::Error,
                                        "No main field identifier present",
                                    )
                                        .emit();

                                    None
                                }
                            }
                            1 => Some(main_fields[0]),
                            _ => {
                                for main_field in main_fields {
                                    Diagnostic::spanned(
                                        main_field.ident.span().unwrap(),
                                        proc_macro::Level::Error,
                                        "Multiple main fields specified for query. Please choose one.",
                                    )
                                        .emit()
                                }

                                None
                            }
                        }
                    }.expect("No main field found");

                    let collections: Vec<String> = data
                        .fields
                        .iter()
                        .map(|field| field.ident.as_ref().unwrap().clone().to_string())
                        .collect();

                    let query_name = input.ident;
                    let model = &main.ty;
                    let main_collection = main.ident.as_ref().unwrap().to_string();

                    let expanded = quote! {
                        #[async_trait::async_trait]
                        impl auraxis::api::Query<#model> for #query_name {
                            type Output = #model;

                            async fn execute(client: &auraxis::api::client::ApiClient) -> Result<Vec<Self::Output>, Box<dyn std::error::Error>> {
                                use auraxis::api::CensusModel;

                                let CensusResponse { count, items} = client.get(#model::collection()).build().await?;

                                let items: Vec<#model> = items.into_iter().map(|item| {
                                    let char: #model = serde_json::from_value(item).unwrap();

                                    char
                                }).collect();

                                Ok(items)
                            }
                        }
                    };

                    TokenStream::from(expanded)
                }
            }
        }
        Err(err) => err.write_errors().into(),
    }
}