nifty_asset_types/state/
delegate.rs

1use std::fmt::Debug;
2
3use borsh::{BorshDeserialize, BorshSerialize};
4use bytemuck::{Pod, Zeroable};
5use podded::{pod::PodOption, ZeroCopy};
6
7use super::{Nullable, NullablePubkey};
8
9/// Default capacity for roles `Vec`.
10///
11/// Currently is equal to the number of roles.
12const DEFAULT_CAPACITY: usize = 3;
13
14#[repr(C)]
15#[derive(Clone, Copy, Default, Pod, Zeroable)]
16pub struct Delegate {
17    pub address: NullablePubkey,
18    pub roles: u8,
19}
20
21impl Delegate {
22    pub const ALL_ROLES_MASK: u8 = 0b111;
23
24    pub fn is_active(&self, role: DelegateRole) -> bool {
25        self.roles & role.mask() > 0
26    }
27
28    pub fn enable(&mut self, role: DelegateRole) {
29        self.roles |= role.mask();
30    }
31
32    pub fn disable(&mut self, role: DelegateRole) {
33        self.roles &= !role.mask();
34    }
35
36    pub fn has_active_roles(&self) -> bool {
37        self.roles > 0
38    }
39
40    pub fn decode_roles(roles: u8) -> Vec<DelegateRole> {
41        let mut result = Vec::with_capacity(DEFAULT_CAPACITY);
42        if roles == 0 {
43            result.push(DelegateRole::None);
44            return result;
45        }
46        for i in 0..3 {
47            if roles & (0b1u8 << i) > 0 {
48                result.push(DelegateRole::from(i + 1));
49            }
50        }
51        result
52    }
53}
54
55impl Nullable for Delegate {
56    fn is_some(&self) -> bool {
57        self.address.is_some()
58    }
59
60    fn is_none(&self) -> bool {
61        self.address.is_none()
62    }
63}
64
65impl From<Delegate> for PodOption<Delegate> {
66    fn from(value: Delegate) -> Self {
67        PodOption::new(value)
68    }
69}
70
71impl Debug for Delegate {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        f.debug_struct("Delegate")
74            .field(
75                "address",
76                if self.address.is_some() {
77                    &self.address
78                } else {
79                    &"None"
80                },
81            )
82            .field("roles", &Delegate::decode_roles(self.roles))
83            .finish()
84    }
85}
86
87impl ZeroCopy<'_, Delegate> for Delegate {}
88
89#[repr(u8)]
90#[derive(BorshSerialize, BorshDeserialize, Clone, Copy, Debug, Default, PartialEq)]
91pub enum DelegateRole {
92    #[default]
93    None,
94    Transfer,
95    Lock,
96    Burn,
97}
98
99impl DelegateRole {
100    pub fn mask(&self) -> u8 {
101        match *self {
102            DelegateRole::None => 0,
103            _ => 0b1u8 << ((*self as u8) - 1),
104        }
105    }
106}
107
108impl From<u8> for DelegateRole {
109    fn from(value: u8) -> Self {
110        match value {
111            0 => DelegateRole::None,
112            1 => DelegateRole::Transfer,
113            2 => DelegateRole::Lock,
114            3 => DelegateRole::Burn,
115            _ => panic!("invalid delegate role value: {value}"),
116        }
117    }
118}
119
120impl From<DelegateRole> for u8 {
121    fn from(value: DelegateRole) -> Self {
122        match value {
123            DelegateRole::None => 0,
124            DelegateRole::Transfer => 1,
125            DelegateRole::Lock => 2,
126            DelegateRole::Burn => 3,
127        }
128    }
129}
130
131unsafe impl Pod for DelegateRole {}
132
133unsafe impl Zeroable for DelegateRole {}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn test_decode_roles() {
141        // Test case 1: roles = 0b101 (Transfer and Burn)
142        let roles = 0b101;
143        let expected_result = vec![DelegateRole::Transfer, DelegateRole::Burn];
144        assert_eq!(Delegate::decode_roles(roles), expected_result);
145
146        // Test case 2: roles = 0b010 (Lock)
147        let roles = 0b010;
148        let expected_result = vec![DelegateRole::Lock];
149        assert_eq!(Delegate::decode_roles(roles), expected_result);
150
151        // Test case 3: roles = 0b000 (None)
152        let roles = 0b000;
153        let expected_result = vec![DelegateRole::None];
154        assert_eq!(Delegate::decode_roles(roles), expected_result);
155
156        // Test case 4: roles = 0b111 (All roles)
157        let roles = 0b111;
158        let expected_result = vec![
159            DelegateRole::Transfer,
160            DelegateRole::Lock,
161            DelegateRole::Burn,
162        ];
163        assert_eq!(Delegate::decode_roles(roles), expected_result);
164    }
165}