sprattus-derive 0.0.1

The derive macros for a async orm for Postgres
extern crate proc_macro;

use crate::functions::*;
use proc_macro2::{Ident, Literal, TokenStream};
use quote::quote;

#[derive(Debug, Eq, PartialEq)]
pub(crate) enum KeyType {
    PrimaryKey,
    PrimaryKeyCandidate,
    NoKey,
}

pub(crate) enum StructName {
    Renamed { original: Ident, new: Literal },
    Named { name: Ident },
}
pub(crate) struct StructFieldData {
    pub name: StructName,
    pub key_type: KeyType,
    pub field_type: Ident,
    pub pg_field_type: String,
}

impl quote::ToTokens for StructName {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match &self {
            StructName::Renamed { original, .. } => {
                let n = original.clone();
                tokens.extend(quote!(#n));
            }
            StructName::Named { name } => {
                let n = name.clone();
                tokens.extend(quote!(#n));
            }
        }
    }
}

impl ToString for StructName {
    fn to_string(&self) -> String {
        match self {
            StructName::Renamed { new, .. } => new.to_string(),
            StructName::Named { name } => name.to_string(),
        }
    }
}

pub(crate) fn build_to_sql_implementation(
    name: &Ident,
    table_name: String,
    field_list: &mut Vec<StructFieldData>,
) -> proc_macro::TokenStream {
    let (primary_key, primary_key_type) = field_list
        .iter()
        .filter(|field| field.key_type == KeyType::PrimaryKey)
        .map(|field| (&field.name, &field.field_type))
        .next()
        .unwrap_or_else(|| {
            panic!("no field field with the 'primary_key' attribute found");
        });
    let primary_key_string = primary_key.to_string();
    let arguments_list_with_types = generate_argument_list_with_types(&field_list);

    let non_pk_field_list: Vec<&StructName> = field_list
        .iter()
        .filter(|field| field.key_type != KeyType::PrimaryKey)
        .map(|field| &field.name)
        .collect();

    let field_list_string = generate_field_list(
        non_pk_field_list
            .iter()
            .map(|item| item.to_string())
            .collect::<Vec<String>>()
            .as_slice(),
    );

    let all_fields_list_string = generate_field_list(
        field_list
            .iter()
            .map(|field| field.name.to_string())
            .collect::<Vec<String>>()
            .as_slice(),
    );
    let field_list_len = non_pk_field_list.len();
    let prepared_arguments_list = generate_argument_list(field_list_len);

    let tokens = quote!(
        impl ToSql for #name {

            #[inline]
            fn get_table_name() -> &'static str {
                stringify!(#table_name)
            }

            #[inline]
            fn get_primary_key() -> &'static str {
                #primary_key_string
            }

            type PK = #primary_key_type;

            #[inline]
            fn get_primary_key_value(&self) -> Self::PK
            where
                Self::PK: ToSqlItem + Sized + Sync
            {
                self.#primary_key
            }

            #[inline]
            fn get_all_fields() -> &'static str {
                #all_fields_list_string
            }

            #[inline]
            fn get_fields() -> &'static str {
               #field_list_string
            }

            #[inline]
            fn get_values_of_all_fields(&self) -> Vec<&(dyn ToSqlItem + Sync)> {
                vec![&self.#primary_key,#(&self.#non_pk_field_list),*]
            }

            #[inline]
            fn get_query_params(&self) -> Vec<&(dyn ToSqlItem + Sync)> {
                vec![#(&self.#non_pk_field_list),*]
            }

            #[inline]
            fn get_prepared_arguments_list() -> &'static str {
                #prepared_arguments_list
            }

            #[inline]
            fn get_prepared_arguments_list_with_types() -> &'static str {
                #arguments_list_with_types
            }

            #[inline]
            fn get_argument_count() -> usize {
                #field_list_len
            }
        }
    );
    tokens.into()
}