nifty_asset_types/state/
delegate.rs1use std::fmt::Debug;
2
3use borsh::{BorshDeserialize, BorshSerialize};
4use bytemuck::{Pod, Zeroable};
5use podded::{pod::PodOption, ZeroCopy};
6
7use super::{Nullable, NullablePubkey};
8
9const 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 let roles = 0b101;
143 let expected_result = vec![DelegateRole::Transfer, DelegateRole::Burn];
144 assert_eq!(Delegate::decode_roles(roles), expected_result);
145
146 let roles = 0b010;
148 let expected_result = vec![DelegateRole::Lock];
149 assert_eq!(Delegate::decode_roles(roles), expected_result);
150
151 let roles = 0b000;
153 let expected_result = vec![DelegateRole::None];
154 assert_eq!(Delegate::decode_roles(roles), expected_result);
155
156 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}