@@ -17,6 +17,7 @@ struct DownloadRequest {
17
17
destination : PathBuf ,
18
18
result : oneshot:: Sender < Result < File , Error > > ,
19
19
status : watch:: Sender < Status > ,
20
+ cancel : oneshot:: Receiver < ( ) > ,
20
21
}
21
22
22
23
#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
@@ -29,6 +30,7 @@ pub struct DownloadProgress {
29
30
pub struct DownloadHandle {
30
31
result : oneshot:: Receiver < Result < File , Error > > ,
31
32
status : watch:: Receiver < Status > ,
33
+ cancel : oneshot:: Sender < ( ) > ,
32
34
}
33
35
34
36
impl DownloadHandle {
@@ -42,13 +44,18 @@ impl DownloadHandle {
42
44
pub fn status ( & self ) -> Status {
43
45
self . status . borrow ( ) . clone ( )
44
46
}
47
+
48
+ pub fn cancel ( self ) {
49
+ self . cancel . send ( ( ) ) . ok ( ) ;
50
+ }
45
51
}
46
52
47
53
#[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
48
54
pub enum Status {
49
55
Pending ,
50
56
InProgress ( DownloadProgress ) ,
51
57
Completed ,
58
+ Cancelled ,
52
59
Failed ,
53
60
}
54
61
@@ -94,19 +101,22 @@ impl DownloadManager {
94
101
pub fn add_request ( & self , url : Url , destination : PathBuf ) -> DownloadHandle {
95
102
let ( result_tx, result_rx) = oneshot:: channel ( ) ;
96
103
let ( status_tx, status_rx) = watch:: channel ( Status :: Pending ) ;
104
+ let ( cancel_tx, cancel_rx) = oneshot:: channel ( ) ;
97
105
98
106
let req = DownloadRequest {
99
107
url,
100
108
destination,
101
109
result : result_tx,
102
110
status : status_tx,
111
+ cancel : cancel_rx,
103
112
} ;
104
113
105
114
let _ = self . queue . try_send ( req) ;
106
115
107
116
DownloadHandle {
108
117
result : result_rx,
109
118
status : status_rx,
119
+ cancel : cancel_tx,
110
120
}
111
121
}
112
122
}
@@ -131,7 +141,17 @@ async fn dispatcher_thread(
131
141
let _ = request. result . send ( Ok ( file) ) ;
132
142
}
133
143
Err ( e) => {
134
- let _ = request. status . send ( Status :: Failed ) ;
144
+ let status = match e {
145
+ Error :: Io ( ref io_err) => {
146
+ if io_err. kind ( ) == std:: io:: ErrorKind :: Interrupted {
147
+ Status :: Cancelled
148
+ } else {
149
+ Status :: Failed
150
+ }
151
+ }
152
+ _ => Status :: Failed ,
153
+ } ;
154
+ let _ = request. status . send ( status) ;
135
155
let _ = request. result . send ( Err ( e) ) ;
136
156
}
137
157
}
@@ -143,13 +163,18 @@ async fn download_thread(client: Client, req: &DownloadRequest) -> Result<File,
143
163
let mut resp = client. get ( req. url . as_ref ( ) ) . send ( ) . await ?;
144
164
let total_bytes = resp. content_length ( ) ;
145
165
let mut bytes_downloaded = 0u64 ;
166
+ let mut cancelled = false ;
146
167
let mut file = File :: options ( )
147
168
. read ( true )
148
169
. write ( true )
149
170
. create ( true )
150
171
. open ( & req. destination ) ?;
151
172
152
173
while let Some ( chunk) = resp. chunk ( ) . await . transpose ( ) {
174
+ cancelled = !req. cancel . is_empty ( ) ;
175
+ if cancelled {
176
+ break ;
177
+ }
153
178
let chunk = chunk?;
154
179
file. write_all ( & chunk) ?;
155
180
bytes_downloaded += chunk. len ( ) as u64 ;
@@ -159,7 +184,15 @@ async fn download_thread(client: Client, req: &DownloadRequest) -> Result<File,
159
184
} ) ) ;
160
185
}
161
186
162
- // Reset the cursor to the beginning of the file
163
- file. seek ( SeekFrom :: Start ( 0 ) ) ?;
164
- Ok ( file)
187
+ if cancelled {
188
+ std:: fs:: remove_file ( & req. destination ) ?;
189
+ Err ( Error :: Io ( std:: io:: Error :: new (
190
+ std:: io:: ErrorKind :: Interrupted ,
191
+ "Download cancelled" ,
192
+ ) ) )
193
+ } else {
194
+ // Reset the cursor to the beginning of the file
195
+ file. seek ( SeekFrom :: Start ( 0 ) ) ?;
196
+ Ok ( file)
197
+ }
165
198
}
0 commit comments