nifty_asset_types/extensions/
grouping.rs1use bytemuck::{Pod, Zeroable};
2use podded::pod::{Nullable, PodOption};
3use solana_program::pubkey::Pubkey;
4use std::{
5 fmt::Debug,
6 ops::{Deref, DerefMut},
7};
8
9use crate::{error::Error, state::NullablePubkey};
10
11use super::{ExtensionBuilder, ExtensionData, ExtensionDataMut, ExtensionType, Lifecycle};
12
13pub struct Grouping<'a> {
21 pub size: &'a u64,
23
24 pub max_size: &'a PodOption<NullableU64>,
28
29 pub delegate: &'a PodOption<NullablePubkey>,
31}
32
33impl<'a> ExtensionData<'a> for Grouping<'a> {
34 const TYPE: ExtensionType = ExtensionType::Grouping;
35
36 fn from_bytes(bytes: &'a [u8]) -> Self {
37 let (size, rest) = bytes.split_at(std::mem::size_of::<u64>());
38 let (max_size, delegate) = rest.split_at(std::mem::size_of::<u64>());
39 Self {
40 size: bytemuck::from_bytes(size),
41 max_size: bytemuck::from_bytes(max_size),
42 delegate: bytemuck::from_bytes(delegate),
43 }
44 }
45
46 fn length(&self) -> usize {
47 std::mem::size_of::<u64>() + std::mem::size_of::<u64>() + std::mem::size_of::<Pubkey>()
48 }
49}
50
51impl Debug for Grouping<'_> {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 f.debug_struct("Group")
54 .field("size", &self.size)
55 .field("max_size", &self.max_size.value())
56 .field("delegate", &self.delegate.value())
57 .finish()
58 }
59}
60
61pub struct GroupingMut<'a> {
62 pub size: &'a mut u64,
63
64 pub max_size: &'a mut PodOption<NullableU64>,
65
66 pub delegate: &'a mut PodOption<NullablePubkey>,
67}
68
69impl<'a> ExtensionDataMut<'a> for GroupingMut<'a> {
70 const TYPE: ExtensionType = ExtensionType::Grouping;
71
72 fn from_bytes_mut(bytes: &'a mut [u8]) -> Self {
73 let (size, rest) = bytes.split_at_mut(std::mem::size_of::<u64>());
74 let (max_size, delegate) = rest.split_at_mut(std::mem::size_of::<u64>());
75 Self {
76 size: bytemuck::from_bytes_mut(size),
77 max_size: bytemuck::from_bytes_mut(max_size),
78 delegate: bytemuck::from_bytes_mut(delegate),
79 }
80 }
81}
82
83impl Lifecycle for GroupingMut<'_> {
84 fn on_create(&mut self, _authority: Option<&Pubkey>) -> Result<(), super::Error> {
85 if *self.size > 0 {
86 Err(Error::InvalidGroupSize)
87 } else {
88 Ok(())
89 }
90 }
91
92 fn on_update(&mut self, other: &mut Self, _authority: Option<&Pubkey>) -> Result<(), Error> {
93 *other.size = *self.size;
95
96 if let Some(max_size) = other.max_size.value() {
97 if **max_size < *other.size {
99 return Err(Error::InvalidMaximumGroupSize(*other.size, **max_size));
100 }
101 }
102
103 Ok(())
104 }
105}
106
107#[repr(C)]
108#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
109pub struct NullableU64(u64);
110
111impl NullableU64 {
112 pub fn new(value: u64) -> Self {
113 Self(value)
114 }
115}
116
117impl Deref for NullableU64 {
118 type Target = u64;
119
120 fn deref(&self) -> &Self::Target {
121 &self.0
122 }
123}
124
125impl DerefMut for NullableU64 {
126 fn deref_mut(&mut self) -> &mut Self::Target {
127 &mut self.0
128 }
129}
130
131impl Nullable for NullableU64 {
132 fn is_some(&self) -> bool {
133 self.0 != 0
134 }
135
136 fn is_none(&self) -> bool {
137 self.0 == 0
138 }
139}
140
141pub struct GroupingBuilder(Vec<u8>);
143
144impl Default for GroupingBuilder {
145 fn default() -> Self {
146 Self(vec![
147 0;
148 (std::mem::size_of::<u64>() * 2)
149 + std::mem::size_of::<Pubkey>()
150 ])
151 }
152}
153
154impl GroupingBuilder {
155 pub fn with_buffer(buffer: Vec<u8>) -> Self {
156 Self(buffer)
157 }
158
159 pub fn set(&mut self, max_size: Option<u64>, delegate: Option<&Pubkey>) -> &mut Self {
161 self.0.clear();
163
164 self.0.extend_from_slice(&u64::to_le_bytes(0));
165 self.0
166 .extend_from_slice(&u64::to_le_bytes(max_size.unwrap_or(0)));
167
168 if let Some(delegate) = delegate {
169 self.0.extend_from_slice(delegate.as_ref());
170 } else {
171 self.0.extend_from_slice(Pubkey::default().as_ref());
172 }
173
174 self
175 }
176}
177
178impl<'a> ExtensionBuilder<'a, Grouping<'a>> for GroupingBuilder {
179 fn build(&'a self) -> Grouping<'a> {
180 Grouping::from_bytes(&self.0)
181 }
182
183 fn data(&mut self) -> Vec<u8> {
184 std::mem::take(&mut self.0)
185 }
186}
187
188impl Deref for GroupingBuilder {
189 type Target = Vec<u8>;
190
191 fn deref(&self) -> &Self::Target {
192 &self.0
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use solana_program::sysvar;
199 use std::ops::Deref;
200
201 use crate::extensions::{ExtensionBuilder, GroupingBuilder};
202
203 #[test]
204 fn test_set_max_size() {
205 let mut builder = GroupingBuilder::default();
207 builder.set(Some(10), None);
208 let grouping = builder.build();
209
210 assert_eq!(*grouping.size, 0);
211 assert!(grouping.max_size.value().is_some());
212
213 let max_size = grouping.max_size.value().unwrap();
214 assert_eq!(**max_size, 10);
215
216 let builder = GroupingBuilder::default();
219 let grouping = builder.build();
220
221 assert_eq!(*grouping.size, 0);
222 assert!(grouping.max_size.value().is_none());
223 assert!(grouping.delegate.value().is_none());
224 }
225
226 #[test]
227 fn test_set_delegate() {
228 let mut builder = GroupingBuilder::default();
230 builder.set(None, Some(&sysvar::ID));
231 let grouping = builder.build();
232
233 assert!(grouping.delegate.value().is_some());
234
235 if let Some(delegate) = grouping.delegate.value() {
236 assert_eq!(delegate.deref(), &sysvar::ID);
237 }
238
239 builder.set(None, None);
241 let grouping = builder.build();
242
243 assert!(grouping.delegate.value().is_none());
244 }
245}