hydracache-macros 0.19.0

Procedural macros for HydraCache.
Documentation
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::DeriveInput;

use crate::config::EntityConfig;
use crate::paths::cache_entity_trait_path;

pub(crate) fn expand(input: DeriveInput) -> syn::Result<TokenStream2> {
    let config = EntityConfig::from_attrs(&input.attrs)?;
    let entity = config.required_entity(&input)?;
    let id = config.required_id(&input)?;
    let collection = config.collection_tokens();
    let trait_path = cache_entity_trait_path();
    let ident = input.ident;
    let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl();

    Ok(quote! {
        impl #impl_generics #trait_path for #ident #type_generics #where_clause {
            type Id = #id;

            const ENTITY: &'static str = #entity;
            const COLLECTION: Option<&'static str> = #collection;
        }
    })
}

#[cfg(test)]
mod tests {
    use syn::{parse_quote, DeriveInput};

    use super::*;

    #[test]
    fn rejects_missing_entity() {
        let input: DeriveInput = parse_quote! {
            #[hydracache(id = i64)]
            struct User;
        };

        let error = expand(input).unwrap_err();

        assert!(error
            .to_string()
            .contains("missing #[hydracache(entity = \"...\")]"));
    }

    #[test]
    fn rejects_missing_id() {
        let input: DeriveInput = parse_quote! {
            #[hydracache(entity = "user")]
            struct User;
        };

        let error = expand(input).unwrap_err();

        assert!(error
            .to_string()
            .contains("missing #[hydracache(id = Type)]"));
    }

    #[test]
    fn expands_impl_for_generics() {
        let input: DeriveInput = parse_quote! {
            #[hydracache(entity = "wrapper", id = String)]
            struct Wrapper<T>
            where
                T: Clone,
            {
                value: T,
            }
        };

        let output = expand(input).unwrap().to_string();

        assert!(output.contains("impl < T >"));
        assert!(output.contains("CacheEntity for Wrapper < T >"));
        assert!(output.contains("type Id = String"));
        assert!(output.contains("const ENTITY"));
        assert!(output.contains("\"wrapper\""));
        assert!(output.contains("const COLLECTION"));
        assert!(output.contains("None"));
    }
}