gmsol-utils 0.8.0

GMX-Solana is an extension of GMX on the Solana blockchain.
Documentation
#[macro_export]
macro_rules! flags {
    ($flags:ty, $max_flags:expr, $flag_value: ty) => {
        $crate::flags!($flags, $max_flags, $flag_value, u8);
    };

    ($flags:ty, $max_flags:expr, $flag_value: ty, $flag_index:ty) => {
        $crate::paste::paste! {
            /// Flags container generated by the macro.
            #[anchor_lang::zero_copy]
            #[derive(PartialEq, Eq)]
            #[cfg_attr(feature = "debug", derive(Debug))]
            #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
            pub struct [<$flags Container>] {
                value: $flag_value,
            }

            impl Default for [<$flags Container>] {
                fn default() -> Self {
                    type [<$flags Map>] = $crate::bitmaps::Bitmap<$max_flags>;

                    Self {
                        value: [<$flags Map>]::new().into_value(),
                    }
                }
            }

            $crate::impl_flags!($flags, $max_flags, $flag_value, $flag_index);
        }
    };
}

#[macro_export]
macro_rules! impl_flags {
    ($flags:ty, $max_flags:expr, $flag_value: ty) => {
        $crate::impl_flags!($flags, $max_flags, $flag_value, u8);
    };

    ($flags:ty, $max_flags:expr, $flag_value: ty, $flag_index:ty) => {
        $crate::paste::paste! {
            type [<$flags Map>] = $crate::bitmaps::Bitmap<$max_flags>;
            // Note: The `$flag_value` can be replaced with the following type alias:
            //
            // type [<$flags Value>] = <$crate::bitmaps::BitsImpl<$max_flags> as $crate::bitmaps::Bits>::Store;
            //
            // But this causes IDL build failures in `anchor v0.30.1`.
            // Currently using `$flag_value` as a temporary workaround.
            type [<$flags Value>] = $flag_value;
            type [<$flags Index>] = $flag_index;

            #[allow(dead_code)]
            impl [<$flags Container>] {
                fn flag_to_index(flag: $flags) -> usize {
                    usize::from( [<$flags Index>]::from(flag))
                }

                fn into_map(self) -> [<$flags Map>] {
                    [<$flags Map>]::from_value(self.value)
                }

                /// Get flag.
                pub fn get_flag(&self, flag: $flags) -> bool {
                    let index = Self::flag_to_index(flag);
                    let map = self.into_map();
                    map.get(index)
                }

                /// Set flag.
                pub fn set_flag(&mut self, flag: $flags, value: bool) -> bool {
                    let index = Self::flag_to_index(flag);
                    let mut map = self.into_map();
                    let previous = map.set(index, value);
                    self.value = map.into_value();
                    previous
                }

                /// Convert into value.
                pub fn into_value(self) -> [<$flags Value>] {
                    self.into_map().into_value()
                }

                /// Create from value.
                pub fn from_value(value: [<$flags Value>]) -> Self {
                    Self {
                        value,
                    }
                }
            }
        }
    };
}

#[cfg(test)]
mod tests {
    use bytemuck::Zeroable;

    #[derive(num_enum::IntoPrimitive)]
    #[repr(u8)]
    enum Flags {
        Enabled,
    }

    #[test]
    fn basic() {
        flags!(Flags, 8, u8);

        let mut flags = FlagsContainer::zeroed();
        assert!(!flags.get_flag(Flags::Enabled));
        let previous = flags.set_flag(Flags::Enabled, true);
        assert!(!previous);
        assert!(flags.get_flag(Flags::Enabled));

        let value = flags.into_value();

        assert_eq!(value, 1);
    }
}