gmsol_utils/
flags.rs

1#[macro_export]
2macro_rules! flags {
3    ($flags:ty, $max_flags:expr, $flag_value: ty) => {
4        $crate::flags!($flags, $max_flags, $flag_value, u8);
5    };
6
7    ($flags:ty, $max_flags:expr, $flag_value: ty, $flag_index:ty) => {
8        $crate::paste::paste! {
9            /// Flags container generated by the macro.
10            #[anchor_lang::zero_copy]
11            #[derive(PartialEq, Eq)]
12            #[cfg_attr(feature = "debug", derive(Debug))]
13            #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14            pub struct [<$flags Container>] {
15                value: $flag_value,
16            }
17
18            impl Default for [<$flags Container>] {
19                fn default() -> Self {
20                    type [<$flags Map>] = $crate::bitmaps::Bitmap<$max_flags>;
21
22                    Self {
23                        value: [<$flags Map>]::new().into_value(),
24                    }
25                }
26            }
27
28            $crate::impl_flags!($flags, $max_flags, $flag_value, $flag_index);
29        }
30    };
31}
32
33#[macro_export]
34macro_rules! impl_flags {
35    ($flags:ty, $max_flags:expr, $flag_value: ty) => {
36        $crate::impl_flags!($flags, $max_flags, $flag_value, u8);
37    };
38
39    ($flags:ty, $max_flags:expr, $flag_value: ty, $flag_index:ty) => {
40        $crate::paste::paste! {
41            type [<$flags Map>] = $crate::bitmaps::Bitmap<$max_flags>;
42            // Note: The `$flag_value` can be replaced with the following type alias:
43            //
44            // type [<$flags Value>] = <$crate::bitmaps::BitsImpl<$max_flags> as $crate::bitmaps::Bits>::Store;
45            //
46            // But this causes IDL build failures in `anchor v0.30.1`.
47            // Currently using `$flag_value` as a temporary workaround.
48            type [<$flags Value>] = $flag_value;
49            type [<$flags Index>] = $flag_index;
50
51            #[allow(dead_code)]
52            impl [<$flags Container>] {
53                fn flag_to_index(flag: $flags) -> usize {
54                    usize::from( [<$flags Index>]::from(flag))
55                }
56
57                fn into_map(self) -> [<$flags Map>] {
58                    [<$flags Map>]::from_value(self.value)
59                }
60
61                /// Get flag.
62                pub fn get_flag(&self, flag: $flags) -> bool {
63                    let index = Self::flag_to_index(flag);
64                    let map = self.into_map();
65                    map.get(index)
66                }
67
68                /// Set flag.
69                pub fn set_flag(&mut self, flag: $flags, value: bool) -> bool {
70                    let index = Self::flag_to_index(flag);
71                    let mut map = self.into_map();
72                    let previous = map.set(index, value);
73                    self.value = map.into_value();
74                    previous
75                }
76
77                /// Convert into value.
78                pub fn into_value(self) -> [<$flags Value>] {
79                    self.into_map().into_value()
80                }
81
82                /// Create from value.
83                pub fn from_value(value: [<$flags Value>]) -> Self {
84                    Self {
85                        value,
86                    }
87                }
88            }
89        }
90    };
91}
92
93#[cfg(test)]
94mod tests {
95    use bytemuck::Zeroable;
96
97    #[derive(num_enum::IntoPrimitive)]
98    #[repr(u8)]
99    enum Flags {
100        Enabled,
101    }
102
103    #[test]
104    fn basic() {
105        flags!(Flags, 8, u8);
106
107        let mut flags = FlagsContainer::zeroed();
108        assert!(!flags.get_flag(Flags::Enabled));
109        let previous = flags.set_flag(Flags::Enabled, true);
110        assert!(!previous);
111        assert!(flags.get_flag(Flags::Enabled));
112
113        let value = flags.into_value();
114
115        assert_eq!(value, 1);
116    }
117}