shadow_nft_standard/common/
collection.rs1use std::ops::{Deref, DerefMut};
2
3use anchor_lang::prelude::*;
4
5use super::creator_group::MAX_GROUP_SIZE;
6
7#[account]
8#[derive(PartialEq, Debug)]
9pub struct Collection {
10 pub creator_group_key: Pubkey,
12
13 pub size: u32,
15
16 pub sigs: u8,
18
19 pub for_minter: bool,
21
22 pub royalty_50bps: [u8; MAX_GROUP_SIZE],
35
36 pub symbol: Symbol,
38
39 pub name: String,
41}
42
43impl Collection {
44 pub fn get_pda(creator_group: Pubkey, collection_name: &str) -> Pubkey {
46 Pubkey::find_program_address(
47 &[creator_group.as_ref(), collection_name.as_ref()],
48 &crate::ID,
49 )
50 .0
51 }
52
53 pub(crate) fn initialized(&self, creators: &[Pubkey]) -> bool {
54 self.sigs.count_ones() == creators.len() as u32
55 }
56}
57
58pub const MAX_SYMBOL_BYTE_LENGTH: usize = 8;
59
60#[derive(PartialEq, Eq, AnchorSerialize, AnchorDeserialize, Clone, Copy)]
65pub struct Symbol {
66 inner: [u8; MAX_SYMBOL_BYTE_LENGTH],
67}
68
69impl Symbol {
70 pub fn new(input: impl AsRef<[u8]>) -> std::result::Result<Symbol, SymbolError> {
72 let input_data = input.as_ref();
74 if input_data.len() > MAX_SYMBOL_BYTE_LENGTH {
75 return Err(SymbolError::ExceedsMaxLength);
76 }
77
78 let input_str = core::str::from_utf8(input_data).map_err(|_| SymbolError::InvalidAscii)?;
80
81 let uppercase_ascii = input_str.chars().all(|c| c.is_ascii_uppercase());
83 if !uppercase_ascii {
84 return Err(SymbolError::IncorrectCaseOrInvalidCharacters);
85 }
86
87 let mut inner = [0; MAX_SYMBOL_BYTE_LENGTH];
89 inner[..input_data.len()].copy_from_slice(input_data);
90
91 Ok(Symbol { inner })
92 }
93
94 pub fn get(&self) -> &str {
96 core::str::from_utf8(&self.inner)
97 .expect("should have been checked for valid ASCII")
98 .trim_end()
99 .trim_end_matches('\0')
100 }
101}
102
103#[derive(Debug, PartialEq)]
104pub enum SymbolError {
105 InvalidAscii,
106 IncorrectCaseOrInvalidCharacters,
107 ExceedsMaxLength,
108}
109
110impl Deref for Symbol {
111 type Target = [u8];
112 fn deref(&self) -> &Self::Target {
113 &self.inner
114 }
115}
116
117impl DerefMut for Symbol {
118 fn deref_mut(&mut self) -> &mut Self::Target {
119 &mut self.inner
120 }
121}
122
123impl std::fmt::Debug for Symbol {
124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 let symbol = core::str::from_utf8(&self.inner)
126 .expect("this should have been checked")
127 .trim_end()
128 .trim_end_matches('\0');
129 write!(f, "symbol = {symbol}")
130 }
131}
132
133#[test]
134fn test_symbol_uppercase() {
135 let upper_case = "SHDW";
136 let symbol = Symbol::new(upper_case).unwrap();
137 assert_eq!(symbol.get(), upper_case);
138
139 let upper_case_bytes = b"SHDW";
140 let symbol = Symbol::new(upper_case_bytes).unwrap();
141 assert_eq!(upper_case_bytes.as_ref(), symbol.get().as_bytes());
142}
143
144#[test]
145fn test_symbol_lowercase() {
146 let lower_case = "shdw";
147 assert_eq!(
148 Symbol::new(lower_case).unwrap_err(),
149 SymbolError::IncorrectCaseOrInvalidCharacters
150 );
151}