cipherstash-dynamodb-derive 0.8.0

Derive macros for the CipherStash client for DynamoDB
Documentation
use crate::settings::Settings;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::DeriveInput;

pub(crate) fn derive_identifiable(input: DeriveInput) -> Result<TokenStream, syn::Error> {
    let settings = Settings::builder(&input)
        .container_attributes(&input)?
        .field_attributes(&input)?
        .build()?;

    let Some(partition_key_field) = settings.get_partition_key() else {
        return Err(syn::Error::new(
            proc_macro2::Span::call_site(),
            "Missing required attribute for Identifiable: #[partition_key]",
        ));
    };

    let partition_key_attr = format_ident!("{partition_key_field}");

    let protected_attributes = settings.protected_attributes();
    let ident = settings.ident();

    let is_partition_key_encrypted = protected_attributes.contains(&partition_key_field.as_str());

    let is_sort_key_encrypted = settings
        .sort_key_field
        .as_ref()
        .map(|x| protected_attributes.contains(&x.as_str()))
        .unwrap_or(true);

    let primary_key_impl = if let Some(sort_key_field) = &settings.sort_key_field {
        let sort_key_attr = format_ident!("{sort_key_field}");

        quote! {
            type PrimaryKey = cipherstash_dynamodb::PkSk;

            fn get_primary_key(&self) -> Self::PrimaryKey {
                cipherstash_dynamodb::PkSk(
                    self.#partition_key_attr.to_string(),
                    self.#sort_key_attr.to_string()
                )
            }
        }
    } else {
        quote! {
            type PrimaryKey = cipherstash_dynamodb::Pk;

            fn get_primary_key(&self) -> Self::PrimaryKey {
                cipherstash_dynamodb::Pk(
                    self.#partition_key_attr.to_string()
                )
            }
        }
    };
    let type_name = &settings.type_name;

    let sort_key_prefix_impl = if let Some(prefix) = &settings.sort_key_prefix {
        quote! { Some(std::borrow::Cow::Borrowed(#prefix)) }
    } else {
        quote! { None }
    };

    let expanded = quote! {
        impl cipherstash_dynamodb::traits::Identifiable for #ident {
            #primary_key_impl

            #[inline]
            fn type_name() -> std::borrow::Cow<'static, str> {
                std::borrow::Cow::Borrowed(#type_name)
            }

            #[inline]
            fn sort_key_prefix() -> Option<std::borrow::Cow<'static, str>> {
                #sort_key_prefix_impl
            }

            fn is_pk_encrypted() -> bool {
                #is_partition_key_encrypted
            }

            fn is_sk_encrypted() -> bool {
                #is_sort_key_encrypted
            }
        }
    };

    Ok(expanded)
}