1
1
use reqwest:: { Client , Url } ;
2
- use std:: { fs:: File , io:: Write , path:: PathBuf , sync:: Arc } ;
3
- use tokio:: sync:: { broadcast, mpsc, oneshot, watch, Semaphore } ;
2
+ use std:: {
3
+ fs:: File ,
4
+ io:: { Seek , SeekFrom , Write } ,
5
+ path:: PathBuf ,
6
+ sync:: Arc ,
7
+ } ;
8
+ use tokio:: sync:: { mpsc, oneshot, watch, Semaphore } ;
9
+
10
+ use crate :: Error ;
4
11
5
12
const QUEUE_SIZE : usize = 100 ;
6
13
7
14
#[ derive( Debug ) ]
8
15
struct DownloadRequest {
9
16
url : Url ,
10
17
destination : PathBuf ,
11
- result : oneshot:: Sender < Result < File , reqwest :: Error > > ,
18
+ result : oneshot:: Sender < Result < File , Error > > ,
12
19
status : watch:: Sender < Status > ,
13
- progress : broadcast:: Sender < DownloadProgress > ,
14
20
}
15
21
16
- #[ derive( Debug , Clone , Copy ) ]
22
+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
17
23
pub struct DownloadProgress {
18
24
pub bytes_downloaded : u64 ,
19
25
pub total_bytes : Option < u64 > ,
20
26
}
21
27
22
28
#[ derive( Debug ) ]
23
29
pub struct DownloadHandle {
24
- result : oneshot:: Receiver < Result < File , reqwest :: Error > > ,
30
+ result : oneshot:: Receiver < Result < File , Error > > ,
25
31
status : watch:: Receiver < Status > ,
26
- progress : broadcast:: Receiver < DownloadProgress > ,
27
32
}
28
33
29
34
impl DownloadHandle {
30
- pub async fn r#await ( self ) -> Result < std:: fs:: File , reqwest :: Error > {
35
+ pub async fn r#await ( self ) -> Result < std:: fs:: File , Error > {
31
36
match self . result . await {
32
37
Ok ( result) => result,
33
38
Err ( _) => todo ! ( ) ,
@@ -37,16 +42,12 @@ impl DownloadHandle {
37
42
pub fn status ( & self ) -> Status {
38
43
self . status . borrow ( ) . clone ( )
39
44
}
40
-
41
- pub fn subscribe_progress ( & self ) -> & broadcast:: Receiver < DownloadProgress > {
42
- & self . progress
43
- }
44
45
}
45
46
46
47
#[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
47
48
pub enum Status {
48
49
Pending ,
49
- InProgress ,
50
+ InProgress ( DownloadProgress ) ,
50
51
Completed ,
51
52
Failed ,
52
53
}
@@ -93,22 +94,19 @@ impl DownloadManager {
93
94
pub fn add_request ( & self , url : Url , destination : PathBuf ) -> DownloadHandle {
94
95
let ( result_tx, result_rx) = oneshot:: channel ( ) ;
95
96
let ( status_tx, status_rx) = watch:: channel ( Status :: Pending ) ;
96
- let ( progress_tx, progress_rx) = broadcast:: channel ( 16 ) ;
97
97
98
98
let req = DownloadRequest {
99
99
url,
100
100
destination,
101
101
result : result_tx,
102
102
status : status_tx,
103
- progress : progress_tx,
104
103
} ;
105
104
106
105
let _ = self . queue . try_send ( req) ;
107
106
108
107
DownloadHandle {
109
108
result : result_rx,
110
109
status : status_rx,
111
- progress : progress_rx,
112
110
}
113
111
}
114
112
}
@@ -127,28 +125,42 @@ async fn dispatcher_thread(
127
125
tokio:: spawn ( async move {
128
126
// Move the permit into the worker thread so it's automatically released when the thread finishes
129
127
let _permit = permit;
130
- let _ = download_thread ( client, request) . await ;
128
+ match download_thread ( client, & request) . await {
129
+ Ok ( file) => {
130
+ let _ = request. status . send ( Status :: Completed ) ;
131
+ let _ = request. result . send ( Ok ( file) ) ;
132
+ }
133
+ Err ( e) => {
134
+ let _ = request. status . send ( Status :: Failed ) ;
135
+ let _ = request. result . send ( Err ( e) ) ;
136
+ }
137
+ }
131
138
} ) ;
132
139
}
133
140
}
134
141
135
- async fn download_thread (
136
- client : Client ,
137
- req : DownloadRequest ,
138
- ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
139
- let mut resp = client. get ( req. url ) . send ( ) . await ?;
140
- let total = resp. content_length ( ) ;
141
- let mut file = File :: create ( & req. destination ) ?;
142
- // let mut stream = resp.bytes().await?;
143
- let mut downloaded = 0u64 ;
142
+ async fn download_thread ( client : Client , req : & DownloadRequest ) -> Result < File , Error > {
143
+ println ! ( "Download Thread Started for: {}" , req. destination. display( ) ) ;
144
+ let mut resp = client. get ( req. url . as_ref ( ) ) . send ( ) . await ?;
145
+ let total_bytes = resp. content_length ( ) ;
146
+ let mut bytes_downloaded = 0u64 ;
147
+ let mut file = File :: options ( )
148
+ . read ( true )
149
+ . write ( true )
150
+ . create ( true )
151
+ . open ( & req. destination ) ?;
152
+
144
153
while let Some ( chunk) = resp. chunk ( ) . await . transpose ( ) {
145
154
let chunk = chunk?;
146
155
file. write_all ( & chunk) ?;
147
- downloaded += chunk. len ( ) as u64 ;
148
- let _ = req. progress . send ( DownloadProgress {
149
- bytes_downloaded : downloaded ,
150
- total_bytes : total ,
151
- } ) ;
156
+ bytes_downloaded += chunk. len ( ) as u64 ;
157
+ let _ = req. status . send ( Status :: InProgress ( DownloadProgress {
158
+ bytes_downloaded,
159
+ total_bytes,
160
+ } ) ) ;
152
161
}
153
- Ok ( ( ) )
162
+
163
+ // Reset the cursor to the beginning of the file
164
+ file. seek ( SeekFrom :: Start ( 0 ) ) ?;
165
+ Ok ( file)
154
166
}
0 commit comments