1use std::{
2 ops::{Deref, DerefMut},
3 path::Path,
4};
5
6use anchor_client::solana_sdk::pubkey::Pubkey;
7use anyhow::Result;
8use mpl_candy_machine_core::ConfigLine;
9use serde::{Deserialize, Serialize};
10
11use crate::{common::*, pdas::find_candy_machine_creator_pda};
12
13#[derive(Debug, Deserialize, Serialize)]
14pub struct Cache {
15 pub program: CacheProgram,
16 pub items: CacheItems,
17 #[serde(skip_deserializing, skip_serializing)]
18 pub file_path: String,
19}
20
21impl Cache {
22 pub fn new() -> Self {
23 Cache {
24 program: CacheProgram::new(),
25 items: CacheItems::new(),
26 file_path: String::new(),
27 }
28 }
29
30 pub fn write_to_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
31 let f = File::create(path)?;
32 serde_json::to_writer_pretty(f, &self)?;
33
34 Ok(())
35 }
36
37 pub fn sync_file(&mut self) -> Result<()> {
38 let file_path = self.file_path.clone();
39 self.write_to_file(Path::new(&file_path))
40 }
41}
42
43impl Default for Cache {
44 fn default() -> Self {
45 Self::new()
46 }
47}
48
49#[derive(Debug, Deserialize, Serialize)]
50pub struct CacheProgram {
51 #[serde(rename = "candyMachine")]
52 pub candy_machine: String,
53 #[serde(rename = "candyGuard")]
54 pub candy_guard: String,
55 #[serde(rename = "candyMachineCreator")]
56 pub candy_machine_creator: String,
57 #[serde(rename = "collectionMint")]
58 pub collection_mint: String,
59}
60
61impl CacheProgram {
62 pub fn new() -> Self {
63 CacheProgram {
64 candy_machine: String::new(),
65 candy_guard: String::new(),
66 candy_machine_creator: String::new(),
67 collection_mint: String::new(),
68 }
69 }
70
71 pub fn new_from_cm(candy_machine: &Pubkey) -> Self {
72 let (candy_machine_creator_pda, _creator_bump) =
73 find_candy_machine_creator_pda(candy_machine);
74 CacheProgram {
75 candy_machine: candy_machine.to_string(),
76 candy_guard: String::new(),
77 candy_machine_creator: candy_machine_creator_pda.to_string(),
78 collection_mint: String::new(),
79 }
80 }
81}
82
83impl Default for CacheProgram {
84 fn default() -> Self {
85 Self::new()
86 }
87}
88
89#[derive(Debug, Deserialize, Serialize)]
90pub struct CacheItems(pub IndexMap<String, CacheItem>);
91
92impl Deref for CacheItems {
93 type Target = IndexMap<String, CacheItem>; fn deref(&self) -> &IndexMap<String, CacheItem> {
95 &self.0 }
97}
98
99impl DerefMut for CacheItems {
100 fn deref_mut(&mut self) -> &mut IndexMap<String, CacheItem> {
101 &mut self.0
102 }
103}
104
105impl CacheItems {
106 pub fn new() -> Self {
107 CacheItems(IndexMap::new())
108 }
109}
110impl Default for CacheItems {
111 fn default() -> Self {
112 Self::new()
113 }
114}
115
116#[derive(Clone, Debug, Deserialize, Serialize)]
117pub struct CacheItem {
118 pub name: String,
119 #[serde(default = "String::default")]
120 pub image_hash: String,
121 pub image_link: String,
122 #[serde(default = "String::default")]
123 pub metadata_hash: String,
124 pub metadata_link: String,
125 #[serde(rename = "onChain")]
126 pub on_chain: bool,
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub animation_hash: Option<String>,
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub animation_link: Option<String>,
131}
132
133impl CacheItem {
134 pub fn to_config_line(&self) -> Option<ConfigLine> {
135 if !self.on_chain {
136 Some(ConfigLine {
137 name: self.name.clone(),
138 uri: self.metadata_link.clone(),
139 })
140 } else {
141 None
142 }
143 }
144}
145
146pub fn load_cache(cache_file_path: &str, create: bool) -> Result<Cache> {
147 let cache_file_path = Path::new(cache_file_path);
148 if !cache_file_path.exists() {
149 if create {
150 let mut cache = Cache::new();
152 cache.file_path = path_to_string(cache_file_path)?;
153 Ok(cache)
154 } else {
155 let cache_file_string = path_to_string(cache_file_path)?;
156 let error = CacheError::CacheFileNotFound(cache_file_string).into();
157 error!("{:?}", error);
158 Err(error)
159 }
160 } else {
161 info!("Cache exists, loading...");
162 let file = match File::open(cache_file_path) {
163 Ok(file) => file,
164 Err(err) => {
165 let cache_file_string = path_to_string(cache_file_path)?;
166 let error =
167 CacheError::FailedToOpenCacheFile(cache_file_string, err.to_string()).into();
168 error!("{:?}", error);
169 return Err(error);
170 }
171 };
172
173 let mut cache: Cache = match serde_json::from_reader(file) {
174 Ok(cache) => cache,
175 Err(err) => {
176 let error = CacheError::CacheFileWrongFormat(err.to_string()).into();
177 error!("{:?}", error);
178 return Err(error);
179 }
180 };
181 cache.file_path = path_to_string(cache_file_path)?;
182
183 Ok(cache)
184 }
185}