fuel_core_chain_config/config/
snapshot_metadata.rs1use anyhow::Context;
2use std::{
3 io::Read,
4 path::{
5 Path,
6 PathBuf,
7 },
8};
9
10#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
11pub enum TableEncoding {
12 Json {
13 filepath: PathBuf,
14 },
15 #[cfg(feature = "parquet")]
16 Parquet {
17 tables: std::collections::HashMap<String, PathBuf>,
18 latest_block_config_path: PathBuf,
19 },
20}
21impl TableEncoding {
22 #[allow(clippy::assigning_clones)] fn strip_prefix(&mut self, dir: &Path) -> anyhow::Result<()> {
24 match self {
25 TableEncoding::Json { filepath } => {
26 *filepath = filepath.strip_prefix(dir)?.to_owned();
27 }
28 #[cfg(feature = "parquet")]
29 TableEncoding::Parquet {
30 tables,
31 latest_block_config_path,
32 ..
33 } => {
34 for path in tables.values_mut() {
35 *path = path.strip_prefix(dir)?.to_owned();
36 }
37 *latest_block_config_path =
38 latest_block_config_path.strip_prefix(dir)?.to_owned();
39 }
40 }
41 Ok(())
42 }
43
44 fn prepend_path(&mut self, dir: &Path) {
45 match self {
46 TableEncoding::Json { filepath } => {
47 *filepath = dir.join(&filepath);
48 }
49 #[cfg(feature = "parquet")]
50 TableEncoding::Parquet {
51 tables,
52 latest_block_config_path,
53 ..
54 } => {
55 for path in tables.values_mut() {
56 *path = dir.join(&path);
57 }
58 *latest_block_config_path = dir.join(&latest_block_config_path);
59 }
60 }
61 }
62}
63
64#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
65pub struct SnapshotMetadata {
66 pub chain_config: PathBuf,
67 pub table_encoding: TableEncoding,
68}
69
70impl SnapshotMetadata {
71 const METADATA_FILENAME: &'static str = "metadata.json";
72 pub fn read(dir: impl AsRef<Path>) -> anyhow::Result<Self> {
73 let path = dir.as_ref().join(Self::METADATA_FILENAME);
74 let mut json = String::new();
75 std::fs::File::open(&path)
76 .with_context(|| format!("Could not open snapshot file: {path:?}"))?
77 .read_to_string(&mut json)?;
78 let mut snapshot: Self = serde_json::from_str(json.as_str())?;
79 snapshot.prepend_path(dir.as_ref());
80
81 Ok(snapshot)
82 }
83
84 #[allow(clippy::assigning_clones)] fn strip_prefix(&mut self, dir: &Path) -> anyhow::Result<&mut Self> {
86 self.chain_config = self.chain_config.strip_prefix(dir)?.to_owned();
87 self.table_encoding.strip_prefix(dir)?;
88 Ok(self)
89 }
90
91 fn prepend_path(&mut self, dir: &Path) {
92 self.chain_config = dir.join(&self.chain_config);
93 self.table_encoding.prepend_path(dir);
94 }
95
96 pub fn write(mut self, dir: &Path) -> anyhow::Result<()> {
97 self.strip_prefix(dir)?;
98 let path = dir.join(Self::METADATA_FILENAME);
99 let file = std::fs::File::create(path)?;
100 serde_json::to_writer_pretty(file, &self)?;
101 Ok(())
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 mod json {
110 use super::*;
111
112 #[test]
113 fn directory_added_to_paths_upon_load() {
114 let temp_dir = tempfile::tempdir().unwrap();
116 let dir = temp_dir.path();
117 let data = SnapshotMetadata {
118 chain_config: "some_chain_config.json".into(),
119 table_encoding: TableEncoding::Json {
120 filepath: "some_state_file.json".into(),
121 },
122 };
123 serde_json::to_writer(
124 std::fs::File::create(dir.join("metadata.json")).unwrap(),
125 &data,
126 )
127 .unwrap();
128
129 let snapshot = SnapshotMetadata::read(temp_dir.path()).unwrap();
131
132 assert_eq!(
134 snapshot,
135 SnapshotMetadata {
136 chain_config: dir.join("some_chain_config.json"),
137 table_encoding: TableEncoding::Json {
138 filepath: temp_dir.path().join("some_state_file.json"),
139 }
140 }
141 );
142 }
143
144 #[test]
145 fn directory_removed_from_paths_upon_save() {
146 let temp_dir = tempfile::tempdir().unwrap();
148 let dir = temp_dir.path();
149 let snapshot = SnapshotMetadata {
150 chain_config: dir.join("some_chain_config.json"),
151 table_encoding: TableEncoding::Json {
152 filepath: dir.join("some_state_file.json"),
153 },
154 };
155
156 snapshot.write(temp_dir.path()).unwrap();
158
159 let data: SnapshotMetadata = serde_json::from_reader(
161 std::fs::File::open(temp_dir.path().join("metadata.json")).unwrap(),
162 )
163 .unwrap();
164 assert_eq!(
165 data,
166 SnapshotMetadata {
167 chain_config: "some_chain_config.json".into(),
168 table_encoding: TableEncoding::Json {
169 filepath: "some_state_file.json".into(),
170 }
171 }
172 );
173 }
174 }
175
176 #[cfg(feature = "parquet")]
177 mod parquet {
178 use super::*;
179 #[test]
180 fn directory_added_to_paths_upon_load() {
181 let temp_dir = tempfile::tempdir().unwrap();
183 let dir = temp_dir.path();
184 let data = SnapshotMetadata {
185 chain_config: "some_chain_config.json".into(),
186 table_encoding: TableEncoding::Parquet {
187 tables: std::collections::HashMap::from_iter(vec![(
188 "coins".into(),
189 "coins.parquet".into(),
190 )]),
191 latest_block_config_path: "latest_block_config.parquet".into(),
192 },
193 };
194 serde_json::to_writer(
195 std::fs::File::create(dir.join("metadata.json")).unwrap(),
196 &data,
197 )
198 .unwrap();
199
200 let snapshot = SnapshotMetadata::read(temp_dir.path()).unwrap();
202
203 assert_eq!(
205 snapshot,
206 SnapshotMetadata {
207 chain_config: dir.join("some_chain_config.json"),
208 table_encoding: TableEncoding::Parquet {
209 tables: std::collections::HashMap::from_iter(vec![(
210 "coins".into(),
211 temp_dir.path().join("coins.parquet")
212 )]),
213 latest_block_config_path: temp_dir
214 .path()
215 .join("latest_block_config.parquet"),
216 }
217 }
218 );
219 }
220
221 #[test]
222 fn directory_removed_from_paths_upon_save() {
223 let temp_dir = tempfile::tempdir().unwrap();
225 let dir = temp_dir.path();
226 let snapshot = SnapshotMetadata {
227 chain_config: dir.join("some_chain_config.json"),
228 table_encoding: TableEncoding::Parquet {
229 tables: std::collections::HashMap::from_iter([(
230 "coins".into(),
231 dir.join("coins.parquet"),
232 )]),
233 latest_block_config_path: dir.join("latest_block_config.parquet"),
234 },
235 };
236
237 snapshot.write(temp_dir.path()).unwrap();
239
240 let data: SnapshotMetadata = serde_json::from_reader(
242 std::fs::File::open(temp_dir.path().join("metadata.json")).unwrap(),
243 )
244 .unwrap();
245 assert_eq!(
246 data,
247 SnapshotMetadata {
248 chain_config: "some_chain_config.json".into(),
249 table_encoding: TableEncoding::Parquet {
250 tables: std::collections::HashMap::from_iter([(
251 "coins".into(),
252 "coins.parquet".into(),
253 )]),
254 latest_block_config_path: "latest_block_config.parquet".into(),
255 }
256 }
257 );
258 }
259 }
260}