ic-stable-memory-derive 0.4.2

Derive macros for ic-stable-memory
Documentation
use proc_macro2::{self, TokenStream};
use quote::{format_ident, quote};
use syn::{Data, Fields, Generics, Ident, Index};

pub fn derive_stable_type_impl(ident: &Ident, data: &Data, generics: &Generics) -> TokenStream {
    if !generics.params.is_empty() {
        panic!("Generics not supported");
    }

    let (flag_off_body, flag_on_body) = match data {
        Data::Struct(d) => {
            let mut flag_off_body = quote! {};
            let mut flag_on_body = quote! {};

            for (idx, f) in d.fields.iter().enumerate() {
                let t = &f.ty;

                if let Some(i) = f.ident.clone() {
                    flag_off_body = quote! { #flag_off_body <#t as ic_stable_memory::StableType>::stable_drop_flag_off(&mut self.#i); };
                    flag_on_body = quote! { #flag_on_body <#t as ic_stable_memory::StableType>::stable_drop_flag_on(&mut self.#i); };
                } else {
                    let idx = Index::from(idx);

                    flag_off_body = quote! { #flag_off_body <#t as ic_stable_memory::StableType>::stable_drop_flag_off(&mut self.#idx); };
                    flag_on_body = quote! { #flag_on_body <#t as ic_stable_memory::StableType>::stable_drop_flag_on(&mut self.#idx); };
                };
            }

            (flag_off_body, flag_on_body)
        }
        Data::Enum(d) => {
            let mut flag_off_body_total = quote! {};
            let mut flag_on_body_total = quote! {};

            for v in d.variants.iter() {
                let v_name = &v.ident;

                let mut flag_off_body = quote! {};
                let mut flag_on_body = quote! {};

                let mut enum_header = quote! {};

                for (idx, f) in v.fields.iter().enumerate() {
                    let t = &f.ty;

                    if let Some(i) = f.ident.clone() {
                        enum_header = quote! { #enum_header #i, };

                        flag_off_body = quote! { #flag_off_body <#t as ic_stable_memory::StableType>::stable_drop_flag_off(#i); };
                        flag_on_body = quote! { #flag_on_body <#t as ic_stable_memory::StableType>::stable_drop_flag_on(#i); };
                    } else {
                        let val_i = format_ident!("val_{}", idx);

                        enum_header = quote! { #enum_header #val_i, };

                        flag_off_body = quote! { #flag_off_body <#t as ic_stable_memory::StableType>::stable_drop_flag_off(#val_i); };
                        flag_on_body = quote! { #flag_on_body <#t as ic_stable_memory::StableType>::stable_drop_flag_on(#val_i); };
                    };
                }

                (flag_off_body_total, flag_on_body_total) = match &v.fields {
                    Fields::Unit => {
                        let owned = quote! {
                            #flag_off_body_total
                            Self::#v_name => {}
                        };

                        let not_owned = quote! {
                            #flag_on_body_total
                            Self::#v_name => {}
                        };

                        (owned, not_owned)
                    }
                    Fields::Named(_) => {
                        let owned = quote! {
                            #flag_off_body_total
                            Self::#v_name { #enum_header } => {
                                #flag_off_body
                            }
                        };

                        let not_owned = quote! {
                            #flag_on_body_total
                            Self::#v_name { #enum_header } => {
                                #flag_on_body
                            }
                        };

                        (owned, not_owned)
                    }
                    Fields::Unnamed(_) => {
                        let owned = quote! {
                            #flag_off_body_total
                            Self::#v_name(#enum_header) => {
                                #flag_off_body
                            }
                        };

                        let not_owned = quote! {
                            #flag_on_body_total
                            Self::#v_name(#enum_header) => {
                                #flag_on_body
                            }
                        };

                        (owned, not_owned)
                    }
                };
            }

            flag_off_body_total = quote! {
                unsafe {
                    match self {
                        #flag_off_body_total
                    }
                }
            };

            flag_on_body_total = quote! {
                unsafe {
                    match self {
                        #flag_on_body_total
                    }
                }
            };

            (flag_off_body_total, flag_on_body_total)
        }
        _ => panic!("Unions not supported!"),
    };

    quote! {
        impl ic_stable_memory::StableType for #ident {
            #[inline]
            unsafe fn stable_drop_flag_off(&mut self) {
                #flag_off_body
            }

            #[inline]
            unsafe fn stable_drop_flag_on(&mut self) {
                #flag_on_body
            }
        }
    }
}