Skip to main content

modde_sources/wabbajack/
cdn.rs

1//! [`DownloadSource`] implementation for archives hosted on the Wabbajack
2//! authored-files CDN, resolving and fetching `WabbajackCdn` download
3//! directives through the chunked authored-files API.
4
5use std::collections::HashMap;
6use std::path::Path;
7
8use anyhow::Result;
9use reqwest::Client;
10
11use modde_core::manifest::wabbajack::DownloadDirective;
12
13use crate::common::ensure_parent;
14use crate::error::{SourceError, SourceResult};
15use crate::traits::{DownloadHandle, DownloadSource, ProgressCallback, VerifiedFile};
16
17use super::catalog::download_authored_file_to_path;
18use super::preflight::preflight_authored_files;
19
20/// Wabbajack-authored CDN archives served through the authored-files chunk API.
21pub struct WabbajackCdnSource {
22    client: Client,
23}
24
25impl WabbajackCdnSource {
26    #[must_use]
27    pub const fn new(client: Client) -> Self {
28        Self { client }
29    }
30
31    pub async fn preflight_archives(
32        &self,
33        archives: &[modde_core::manifest::wabbajack::ArchiveEntry],
34    ) -> Result<()> {
35        preflight_authored_files(&self.client, archives).await
36    }
37}
38
39impl DownloadSource for WabbajackCdnSource {
40    fn can_handle(&self, directive: &DownloadDirective) -> bool {
41        matches!(directive, DownloadDirective::WabbajackCdn { .. })
42    }
43
44    async fn resolve(&self, directive: &DownloadDirective) -> SourceResult<DownloadHandle> {
45        let DownloadDirective::WabbajackCdn { url, hash } = directive else {
46            return Err(SourceError::other(anyhow::anyhow!(
47                "not a WabbajackCdn directive"
48            )));
49        };
50
51        Ok(DownloadHandle {
52            url: url.clone(),
53            candidate_urls: Vec::new(),
54            headers: HashMap::new(),
55            expected_hash: *hash,
56            size_hint: None,
57        })
58    }
59
60    async fn download_with_progress(
61        &self,
62        handle: DownloadHandle,
63        dest: &Path,
64        progress: ProgressCallback,
65    ) -> SourceResult<VerifiedFile> {
66        ensure_parent(dest).await?;
67        download_authored_file_to_path(
68            &self.client,
69            &handle.url,
70            dest,
71            Some(handle.expected_hash),
72            Some(&progress),
73        )
74        .await
75        .map_err(SourceError::other)?;
76        Ok(VerifiedFile {
77            path: dest.to_path_buf(),
78            hash: handle.expected_hash,
79        })
80    }
81}