1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3use std::sync::Arc;
4
5use anyhow::Result;
6
7use modde_core::manifest::wabbajack::DownloadDirective;
8
9pub type ProgressCallback = Arc<dyn Fn(u64, u64) + Send + Sync>;
12
13#[derive(Debug, Clone)]
15pub struct DownloadHandle {
16 pub url: String,
17 pub headers: HashMap<String, String>,
18 pub expected_hash: u64,
19 pub size_hint: Option<u64>,
20}
21
22#[derive(Debug, Clone)]
24pub struct VerifiedFile {
25 pub path: PathBuf,
26 pub hash: u64,
27}
28
29pub trait DownloadSource: Send + Sync {
35 fn can_handle(&self, directive: &DownloadDirective) -> bool;
37
38 fn resolve(
40 &self,
41 directive: &DownloadDirective,
42 ) -> impl std::future::Future<Output = Result<DownloadHandle>> + Send;
43
44 fn download_with_progress(
46 &self,
47 handle: DownloadHandle,
48 dest: &Path,
49 progress: ProgressCallback,
50 ) -> impl std::future::Future<Output = Result<VerifiedFile>> + Send;
51
52 fn download(
54 &self,
55 handle: DownloadHandle,
56 dest: &Path,
57 ) -> impl std::future::Future<Output = Result<VerifiedFile>> + Send {
58 let noop: ProgressCallback = Arc::new(|_, _| {});
59 self.download_with_progress(handle, dest, noop)
60 }
61}
62
63pub enum AnySource {
68 Nexus(crate::nexus::NexusSource),
69 GitHub(crate::github::GitHubSource),
70 GoogleDrive(crate::gdrive::GoogleDriveSource),
71 Mega(crate::mega::MegaSource),
72 Direct(crate::direct::DirectSource),
73}
74
75macro_rules! dispatch {
77 ($self:expr, $method:ident ( $($arg:expr),* )) => {
78 match $self {
79 AnySource::Nexus(s) => s.$method($($arg),*),
80 AnySource::GitHub(s) => s.$method($($arg),*),
81 AnySource::GoogleDrive(s) => s.$method($($arg),*),
82 AnySource::Mega(s) => s.$method($($arg),*),
83 AnySource::Direct(s) => s.$method($($arg),*),
84 }
85 };
86}
87
88macro_rules! dispatch_async {
90 ($self:expr, $method:ident ( $($arg:expr),* )) => {
91 match $self {
92 AnySource::Nexus(s) => s.$method($($arg),*).await,
93 AnySource::GitHub(s) => s.$method($($arg),*).await,
94 AnySource::GoogleDrive(s) => s.$method($($arg),*).await,
95 AnySource::Mega(s) => s.$method($($arg),*).await,
96 AnySource::Direct(s) => s.$method($($arg),*).await,
97 }
98 };
99}
100
101impl DownloadSource for AnySource {
102 fn can_handle(&self, directive: &DownloadDirective) -> bool {
103 dispatch!(self, can_handle(directive))
104 }
105
106 async fn resolve(&self, directive: &DownloadDirective) -> Result<DownloadHandle> {
107 dispatch_async!(self, resolve(directive))
108 }
109
110 async fn download_with_progress(
111 &self,
112 handle: DownloadHandle,
113 dest: &Path,
114 progress: ProgressCallback,
115 ) -> Result<VerifiedFile> {
116 dispatch_async!(self, download_with_progress(handle, dest, progress))
117 }
118}