1use std::path::Path;
2
3use anyhow::anyhow;
4use async_trait::async_trait;
5
6use mithril_common::{
7 entities::{
8 AncillaryLocation, CompressionAlgorithm, DigestLocation, FileUri, ImmutableFileNumber,
9 },
10 StdError, StdResult,
11};
12
13use crate::feedback::{MithrilEvent, MithrilEventCardanoDatabase};
14
15#[derive(Debug, PartialEq, Eq, Clone)]
17pub enum FileDownloaderUri {
18 FileUri(FileUri),
20}
21
22impl FileDownloaderUri {
23 pub fn as_str(&self) -> &str {
25 match self {
26 FileDownloaderUri::FileUri(file_uri) => file_uri.0.as_str(),
27 }
28 }
29}
30
31impl From<String> for FileDownloaderUri {
32 fn from(location: String) -> Self {
33 Self::FileUri(FileUri(location))
34 }
35}
36
37impl From<FileUri> for FileDownloaderUri {
38 fn from(file_uri: FileUri) -> Self {
39 Self::FileUri(file_uri)
40 }
41}
42
43impl TryFrom<AncillaryLocation> for FileDownloaderUri {
44 type Error = StdError;
45
46 fn try_from(location: AncillaryLocation) -> Result<Self, Self::Error> {
47 match location {
48 AncillaryLocation::CloudStorage {
49 uri,
50 compression_algorithm: _,
51 } => Ok(Self::FileUri(FileUri(uri))),
52 AncillaryLocation::Unknown => {
53 Err(anyhow!("Unknown location type to download ancillary"))
54 }
55 }
56 }
57}
58
59impl TryFrom<DigestLocation> for FileDownloaderUri {
60 type Error = StdError;
61
62 fn try_from(location: DigestLocation) -> Result<Self, Self::Error> {
63 match location {
64 DigestLocation::CloudStorage {
65 uri,
66 compression_algorithm: _,
67 }
68 | DigestLocation::Aggregator { uri } => Ok(Self::FileUri(FileUri(uri))),
69 DigestLocation::Unknown => Err(anyhow!("Unknown location type to download digest")),
70 }
71 }
72}
73
74#[derive(Debug, Clone)]
79pub enum DownloadEvent {
80 Immutable {
82 download_id: String,
84 immutable_file_number: ImmutableFileNumber,
86 },
87 Ancillary {
89 download_id: String,
91 },
92 Digest {
94 download_id: String,
96 },
97 Full {
99 download_id: String,
101 digest: String,
103 },
104 FullAncillary {
106 download_id: String,
108 },
109}
110
111impl DownloadEvent {
112 pub fn download_id(&self) -> &str {
114 match self {
115 DownloadEvent::Immutable { download_id, .. }
116 | DownloadEvent::Ancillary { download_id }
117 | DownloadEvent::Digest { download_id }
118 | DownloadEvent::Full { download_id, .. }
119 | DownloadEvent::FullAncillary { download_id } => download_id,
120 }
121 }
122
123 pub fn build_download_started_event(&self, size: u64) -> MithrilEvent {
125 match self {
126 DownloadEvent::Immutable {
127 download_id,
128 immutable_file_number,
129 } => MithrilEvent::CardanoDatabase(
130 MithrilEventCardanoDatabase::ImmutableDownloadStarted {
131 download_id: download_id.to_string(),
132 immutable_file_number: *immutable_file_number,
133 size,
134 },
135 ),
136 DownloadEvent::Ancillary { download_id } => MithrilEvent::CardanoDatabase(
137 MithrilEventCardanoDatabase::AncillaryDownloadStarted {
138 download_id: download_id.to_string(),
139 size,
140 },
141 ),
142 DownloadEvent::Digest { download_id } => {
143 MithrilEvent::CardanoDatabase(MithrilEventCardanoDatabase::DigestDownloadStarted {
144 download_id: download_id.to_string(),
145 size,
146 })
147 }
148 DownloadEvent::Full {
149 download_id,
150 digest,
151 } => MithrilEvent::SnapshotDownloadStarted {
152 download_id: download_id.to_string(),
153 digest: digest.to_string(),
154 size,
155 },
156 DownloadEvent::FullAncillary { download_id } => {
157 MithrilEvent::SnapshotAncillaryDownloadStarted {
158 download_id: download_id.to_string(),
159 size,
160 }
161 }
162 }
163 }
164
165 pub fn build_download_progress_event(
167 &self,
168 downloaded_bytes: u64,
169 total_bytes: u64,
170 ) -> MithrilEvent {
171 match self {
172 DownloadEvent::Immutable {
173 immutable_file_number,
174 download_id,
175 } => MithrilEvent::CardanoDatabase(
176 MithrilEventCardanoDatabase::ImmutableDownloadProgress {
177 download_id: download_id.to_string(),
178 downloaded_bytes,
179 size: total_bytes,
180 immutable_file_number: *immutable_file_number,
181 },
182 ),
183 DownloadEvent::Ancillary { download_id } => MithrilEvent::CardanoDatabase(
184 MithrilEventCardanoDatabase::AncillaryDownloadProgress {
185 download_id: download_id.to_string(),
186 downloaded_bytes,
187 size: total_bytes,
188 },
189 ),
190 DownloadEvent::Digest { download_id } => {
191 MithrilEvent::CardanoDatabase(MithrilEventCardanoDatabase::DigestDownloadProgress {
192 download_id: download_id.to_string(),
193 downloaded_bytes,
194 size: total_bytes,
195 })
196 }
197 DownloadEvent::Full { download_id, .. } => MithrilEvent::SnapshotDownloadProgress {
198 download_id: download_id.to_string(),
199 downloaded_bytes,
200 size: total_bytes,
201 },
202 DownloadEvent::FullAncillary { download_id } => {
203 MithrilEvent::SnapshotAncillaryDownloadProgress {
204 download_id: download_id.to_string(),
205 downloaded_bytes,
206 size: total_bytes,
207 }
208 }
209 }
210 }
211
212 pub fn build_download_completed_event(&self) -> MithrilEvent {
214 match self {
215 DownloadEvent::Immutable {
216 download_id,
217 immutable_file_number,
218 } => MithrilEvent::CardanoDatabase(
219 MithrilEventCardanoDatabase::ImmutableDownloadCompleted {
220 download_id: download_id.to_string(),
221 immutable_file_number: *immutable_file_number,
222 },
223 ),
224 DownloadEvent::Ancillary { download_id } => MithrilEvent::CardanoDatabase(
225 MithrilEventCardanoDatabase::AncillaryDownloadCompleted {
226 download_id: download_id.to_string(),
227 },
228 ),
229 DownloadEvent::Digest { download_id } => MithrilEvent::CardanoDatabase(
230 MithrilEventCardanoDatabase::DigestDownloadCompleted {
231 download_id: download_id.to_string(),
232 },
233 ),
234 DownloadEvent::Full { download_id, .. } => MithrilEvent::SnapshotDownloadCompleted {
235 download_id: download_id.to_string(),
236 },
237 DownloadEvent::FullAncillary { download_id, .. } => {
238 MithrilEvent::SnapshotAncillaryDownloadCompleted {
239 download_id: download_id.to_string(),
240 }
241 }
242 }
243 }
244}
245
246#[cfg_attr(test, mockall::automock)]
248#[async_trait]
249pub trait FileDownloader: Sync + Send {
250 async fn download_unpack(
253 &self,
254 location: &FileDownloaderUri,
255 file_size: u64,
256 target_dir: &Path,
257 compression_algorithm: Option<CompressionAlgorithm>,
258 download_event_type: DownloadEvent,
259 ) -> StdResult<()>;
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265
266 #[test]
267 fn download_event_type_builds_started_event() {
268 let download_event_type = DownloadEvent::Immutable {
269 download_id: "download-123".to_string(),
270 immutable_file_number: 123,
271 };
272 let event = download_event_type.build_download_started_event(1234);
273 assert_eq!(
274 MithrilEvent::CardanoDatabase(MithrilEventCardanoDatabase::ImmutableDownloadStarted {
275 immutable_file_number: 123,
276 download_id: "download-123".to_string(),
277 size: 1234,
278 }),
279 event,
280 );
281
282 let download_event_type = DownloadEvent::Ancillary {
283 download_id: "download-123".to_string(),
284 };
285 let event = download_event_type.build_download_started_event(1234);
286 assert_eq!(
287 MithrilEvent::CardanoDatabase(MithrilEventCardanoDatabase::AncillaryDownloadStarted {
288 download_id: "download-123".to_string(),
289 size: 1234,
290 }),
291 event,
292 );
293
294 let download_event_type = DownloadEvent::Digest {
295 download_id: "download-123".to_string(),
296 };
297 let event = download_event_type.build_download_started_event(1234);
298 assert_eq!(
299 MithrilEvent::CardanoDatabase(MithrilEventCardanoDatabase::DigestDownloadStarted {
300 download_id: "download-123".to_string(),
301 size: 1234,
302 }),
303 event,
304 );
305
306 let download_event_type = DownloadEvent::Full {
307 download_id: "download-123".to_string(),
308 digest: "digest-123".to_string(),
309 };
310 let event = download_event_type.build_download_started_event(1234);
311 assert_eq!(
312 MithrilEvent::SnapshotDownloadStarted {
313 digest: "digest-123".to_string(),
314 download_id: "download-123".to_string(),
315 size: 1234,
316 },
317 event,
318 );
319
320 let download_event_type = DownloadEvent::FullAncillary {
321 download_id: "download-123".to_string(),
322 };
323 let event = download_event_type.build_download_started_event(1234);
324 assert_eq!(
325 MithrilEvent::SnapshotAncillaryDownloadStarted {
326 download_id: "download-123".to_string(),
327 size: 1234,
328 },
329 event,
330 );
331 }
332
333 #[test]
334 fn download_event_type_builds_progress_event() {
335 let download_event_type = DownloadEvent::Immutable {
336 download_id: "download-123".to_string(),
337 immutable_file_number: 123,
338 };
339 let event = download_event_type.build_download_progress_event(123, 1234);
340 assert_eq!(
341 MithrilEvent::CardanoDatabase(MithrilEventCardanoDatabase::ImmutableDownloadProgress {
342 immutable_file_number: 123,
343 download_id: "download-123".to_string(),
344 downloaded_bytes: 123,
345 size: 1234,
346 }),
347 event,
348 );
349
350 let download_event_type = DownloadEvent::Ancillary {
351 download_id: "download-123".to_string(),
352 };
353 let event = download_event_type.build_download_progress_event(123, 1234);
354 assert_eq!(
355 MithrilEvent::CardanoDatabase(MithrilEventCardanoDatabase::AncillaryDownloadProgress {
356 download_id: "download-123".to_string(),
357 downloaded_bytes: 123,
358 size: 1234,
359 }),
360 event,
361 );
362
363 let download_event_type = DownloadEvent::Digest {
364 download_id: "download-123".to_string(),
365 };
366 let event = download_event_type.build_download_progress_event(123, 1234);
367 assert_eq!(
368 MithrilEvent::CardanoDatabase(MithrilEventCardanoDatabase::DigestDownloadProgress {
369 download_id: "download-123".to_string(),
370 downloaded_bytes: 123,
371 size: 1234,
372 }),
373 event,
374 );
375
376 let download_event_type = DownloadEvent::Full {
377 download_id: "download-123".to_string(),
378 digest: "whatever".to_string(),
379 };
380 let event = download_event_type.build_download_progress_event(123, 1234);
381 assert_eq!(
382 MithrilEvent::SnapshotDownloadProgress {
383 download_id: "download-123".to_string(),
384 downloaded_bytes: 123,
385 size: 1234,
386 },
387 event,
388 );
389
390 let download_event_type = DownloadEvent::FullAncillary {
391 download_id: "download-123".to_string(),
392 };
393 let event = download_event_type.build_download_progress_event(123, 1234);
394 assert_eq!(
395 MithrilEvent::SnapshotAncillaryDownloadProgress {
396 download_id: "download-123".to_string(),
397 downloaded_bytes: 123,
398 size: 1234,
399 },
400 event,
401 );
402 }
403
404 #[test]
405 fn file_downloader_uri_from_ancillary_location() {
406 let location = AncillaryLocation::CloudStorage {
407 uri: "http://whatever/ancillary-1".to_string(),
408 compression_algorithm: Some(CompressionAlgorithm::Gzip),
409 };
410 let file_downloader_uri: FileDownloaderUri = location.try_into().unwrap();
411
412 assert_eq!(
413 FileDownloaderUri::FileUri(FileUri("http://whatever/ancillary-1".to_string())),
414 file_downloader_uri
415 );
416 }
417 #[test]
418 fn file_downloader_uri_from_unknown_ancillary_location() {
419 let location = AncillaryLocation::Unknown;
420 let file_downloader_uri: StdResult<FileDownloaderUri> = location.try_into();
421
422 file_downloader_uri.expect_err("try_into should fail on Unknown ancillary location");
423 }
424
425 #[test]
426 fn file_downloader_uri_from_digest_location() {
427 let location = DigestLocation::CloudStorage {
428 uri: "http://whatever/digest-1".to_string(),
429 compression_algorithm: None,
430 };
431 let file_downloader_uri: FileDownloaderUri = location.try_into().unwrap();
432
433 assert_eq!(
434 FileDownloaderUri::FileUri(FileUri("http://whatever/digest-1".to_string())),
435 file_downloader_uri
436 );
437 }
438 #[test]
439 fn file_downloader_uri_from_unknown_digest_location() {
440 let location = DigestLocation::Unknown;
441 let file_downloader_uri: StdResult<FileDownloaderUri> = location.try_into();
442
443 file_downloader_uri.expect_err("try_into should fail on Unknown digest location");
444 }
445
446 #[test]
447 fn download_event_type_builds_completed_event() {
448 let download_event_type = DownloadEvent::Immutable {
449 download_id: "download-123".to_string(),
450 immutable_file_number: 123,
451 };
452 let event = download_event_type.build_download_completed_event();
453 assert_eq!(
454 MithrilEvent::CardanoDatabase(
455 MithrilEventCardanoDatabase::ImmutableDownloadCompleted {
456 immutable_file_number: 123,
457 download_id: "download-123".to_string()
458 }
459 ),
460 event,
461 );
462
463 let download_event_type = DownloadEvent::Ancillary {
464 download_id: "download-123".to_string(),
465 };
466 let event = download_event_type.build_download_completed_event();
467 assert_eq!(
468 MithrilEvent::CardanoDatabase(
469 MithrilEventCardanoDatabase::AncillaryDownloadCompleted {
470 download_id: "download-123".to_string(),
471 }
472 ),
473 event,
474 );
475
476 let download_event_type = DownloadEvent::Digest {
477 download_id: "download-123".to_string(),
478 };
479 let event = download_event_type.build_download_completed_event();
480 assert_eq!(
481 MithrilEvent::CardanoDatabase(MithrilEventCardanoDatabase::DigestDownloadCompleted {
482 download_id: "download-123".to_string(),
483 }),
484 event,
485 );
486
487 let download_event_type = DownloadEvent::Full {
488 download_id: "download-123".to_string(),
489 digest: "whatever".to_string(),
490 };
491 let event = download_event_type.build_download_completed_event();
492 assert_eq!(
493 MithrilEvent::SnapshotDownloadCompleted {
494 download_id: "download-123".to_string(),
495 },
496 event,
497 );
498
499 let download_event_type = DownloadEvent::FullAncillary {
500 download_id: "download-123".to_string(),
501 };
502 let event = download_event_type.build_download_completed_event();
503 assert_eq!(
504 MithrilEvent::SnapshotAncillaryDownloadCompleted {
505 download_id: "download-123".to_string(),
506 },
507 event,
508 );
509 }
510}