1use std::collections::HashMap;
6use std::path::{Path, PathBuf};
7use std::sync::Arc;
8
9use modde_core::manifest::wabbajack::DownloadDirective;
10
11use crate::error::SourceResult;
12
13pub type ProgressCallback = Arc<dyn Fn(u64, u64) + Send + Sync>;
16
17#[derive(Debug, Clone)]
19pub struct DownloadHandle {
20 pub url: String,
21 pub candidate_urls: Vec<String>,
22 pub headers: HashMap<String, String>,
23 pub expected_hash: u64,
24 pub size_hint: Option<u64>,
25}
26
27#[derive(Debug, Clone)]
29pub struct VerifiedFile {
30 pub path: PathBuf,
31 pub hash: u64,
32}
33
34pub trait DownloadSource: Send + Sync {
40 fn can_handle(&self, directive: &DownloadDirective) -> bool;
42
43 fn resolve(
45 &self,
46 directive: &DownloadDirective,
47 ) -> impl std::future::Future<Output = SourceResult<DownloadHandle>> + Send;
48
49 fn download_with_progress(
51 &self,
52 handle: DownloadHandle,
53 dest: &Path,
54 progress: ProgressCallback,
55 ) -> impl std::future::Future<Output = SourceResult<VerifiedFile>> + Send;
56
57 fn download(
59 &self,
60 handle: DownloadHandle,
61 dest: &Path,
62 ) -> impl std::future::Future<Output = SourceResult<VerifiedFile>> + Send {
63 let noop: ProgressCallback = Arc::new(|_, _| {});
64 self.download_with_progress(handle, dest, noop)
65 }
66}
67
68pub enum AnySource {
73 Nexus(crate::nexus::NexusSource),
74 GitHub(crate::github::GitHubSource),
75 GoogleDrive(crate::gdrive::GoogleDriveSource),
76 Mega(crate::mega::MegaSource),
77 MediaFire(crate::mediafire::MediaFireSource),
78 Manual(crate::manual::ManualSource),
79 Direct(crate::direct::DirectSource),
80 WabbajackCdn(crate::wabbajack::cdn::WabbajackCdnSource),
81}
82
83macro_rules! dispatch {
85 ($self:expr, $method:ident ( $($arg:expr),* )) => {
86 match $self {
87 AnySource::Nexus(s) => s.$method($($arg),*),
88 AnySource::GitHub(s) => s.$method($($arg),*),
89 AnySource::GoogleDrive(s) => s.$method($($arg),*),
90 AnySource::Mega(s) => s.$method($($arg),*),
91 AnySource::MediaFire(s) => s.$method($($arg),*),
92 AnySource::Manual(s) => s.$method($($arg),*),
93 AnySource::Direct(s) => s.$method($($arg),*),
94 AnySource::WabbajackCdn(s) => s.$method($($arg),*),
95 }
96 };
97}
98
99macro_rules! dispatch_async {
101 ($self:expr, $method:ident ( $($arg:expr),* )) => {
102 match $self {
103 AnySource::Nexus(s) => s.$method($($arg),*).await,
104 AnySource::GitHub(s) => s.$method($($arg),*).await,
105 AnySource::GoogleDrive(s) => s.$method($($arg),*).await,
106 AnySource::Mega(s) => s.$method($($arg),*).await,
107 AnySource::MediaFire(s) => s.$method($($arg),*).await,
108 AnySource::Manual(s) => s.$method($($arg),*).await,
109 AnySource::Direct(s) => s.$method($($arg),*).await,
110 AnySource::WabbajackCdn(s) => s.$method($($arg),*).await,
111 }
112 };
113}
114
115impl DownloadSource for AnySource {
116 fn can_handle(&self, directive: &DownloadDirective) -> bool {
117 dispatch!(self, can_handle(directive))
118 }
119
120 async fn resolve(&self, directive: &DownloadDirective) -> SourceResult<DownloadHandle> {
121 dispatch_async!(self, resolve(directive))
122 }
123
124 async fn download_with_progress(
125 &self,
126 handle: DownloadHandle,
127 dest: &Path,
128 progress: ProgressCallback,
129 ) -> SourceResult<VerifiedFile> {
130 dispatch_async!(self, download_with_progress(handle, dest, progress))
131 }
132}