@@ -5,7 +5,6 @@ use std::path::Path;
5
5
6
6
use anyhow:: Context ;
7
7
pub use anyhow:: Result ;
8
- use tokio:: { runtime:: Handle , task} ;
9
8
use url:: Url ;
10
9
11
10
mod errors;
@@ -50,7 +49,7 @@ async fn download_with_backend(
50
49
51
50
type DownloadCallback < ' a > = & ' a dyn Fn ( Event < ' _ > ) -> Result < ( ) > ;
52
51
53
- pub fn download_to_path_with_backend (
52
+ pub async fn download_to_path_with_backend (
54
53
backend : Backend ,
55
54
url : & Url ,
56
55
path : & Path ,
@@ -62,100 +61,80 @@ pub fn download_to_path_with_backend(
62
61
use std:: fs:: OpenOptions ;
63
62
use std:: io:: { Read , Seek , SeekFrom , Write } ;
64
63
65
- || -> Result < ( ) > {
66
- let ( file, resume_from) = if resume_from_partial {
67
- let possible_partial = OpenOptions :: new ( ) . read ( true ) . open ( path) ;
68
-
69
- let downloaded_so_far = if let Ok ( mut partial) = possible_partial {
70
- if let Some ( cb) = callback {
71
- cb ( Event :: ResumingPartialDownload ) ?;
72
-
73
- let mut buf = vec ! [ 0 ; 32768 ] ;
74
- let mut downloaded_so_far = 0 ;
75
- loop {
76
- let n = partial. read ( & mut buf) ?;
77
- downloaded_so_far += n as u64 ;
78
- if n == 0 {
79
- break ;
64
+ ( || {
65
+ async move {
66
+ let ( file, resume_from) = if resume_from_partial {
67
+ // TODO: blocking call
68
+ let possible_partial = OpenOptions :: new ( ) . read ( true ) . open ( path) ;
69
+
70
+ let downloaded_so_far = if let Ok ( mut partial) = possible_partial {
71
+ if let Some ( cb) = callback {
72
+ cb ( Event :: ResumingPartialDownload ) ?;
73
+
74
+ let mut buf = vec ! [ 0 ; 32768 ] ;
75
+ let mut downloaded_so_far = 0 ;
76
+ loop {
77
+ let n = partial. read ( & mut buf) ?;
78
+ downloaded_so_far += n as u64 ;
79
+ if n == 0 {
80
+ break ;
81
+ }
82
+ cb ( Event :: DownloadDataReceived ( & buf[ ..n] ) ) ?;
80
83
}
81
- cb ( Event :: DownloadDataReceived ( & buf[ ..n] ) ) ?;
82
- }
83
84
84
- downloaded_so_far
85
+ downloaded_so_far
86
+ } else {
87
+ let file_info = partial. metadata ( ) ?;
88
+ file_info. len ( )
89
+ }
85
90
} else {
86
- let file_info = partial. metadata ( ) ?;
87
- file_info. len ( )
88
- }
89
- } else {
90
- 0
91
- } ;
92
-
93
- let mut possible_partial = OpenOptions :: new ( )
94
- . write ( true )
95
- . create ( true )
96
- . open ( path)
97
- . context ( "error opening file for download" ) ?;
91
+ 0
92
+ } ;
98
93
99
- possible_partial. seek ( SeekFrom :: End ( 0 ) ) ?;
100
-
101
- ( possible_partial, downloaded_so_far)
102
- } else {
103
- (
104
- OpenOptions :: new ( )
94
+ let mut possible_partial = OpenOptions :: new ( )
105
95
. write ( true )
106
96
. create ( true )
107
97
. open ( path)
108
- . context ( "error creating file for download" ) ?,
109
- 0 ,
110
- )
111
- } ;
98
+ . context ( "error opening file for download" ) ?;
112
99
113
- let file = RefCell :: new ( file) ;
114
-
115
- match Handle :: try_current ( ) {
116
- Ok ( current) => {
117
- // hide the asyncness for now.
118
- task:: block_in_place ( || {
119
- current. block_on ( download_with_backend ( backend, url, resume_from, & |event| {
120
- if let Event :: DownloadDataReceived ( data) = event {
121
- file. borrow_mut ( )
122
- . write_all ( data)
123
- . context ( "unable to write download to disk" ) ?;
124
- }
125
- match callback {
126
- Some ( cb) => cb ( event) ,
127
- None => Ok ( ( ) ) ,
128
- }
129
- } ) )
130
- } )
131
- }
132
- Err ( _) => {
133
- // Make a runtime to hide the asyncness.
134
- tokio:: runtime:: Runtime :: new ( ) ?. block_on ( download_with_backend (
135
- backend,
136
- url,
137
- resume_from,
138
- & |event| {
139
- if let Event :: DownloadDataReceived ( data) = event {
140
- file. borrow_mut ( )
141
- . write_all ( data)
142
- . context ( "unable to write download to disk" ) ?;
143
- }
144
- match callback {
145
- Some ( cb) => cb ( event) ,
146
- None => Ok ( ( ) ) ,
147
- }
148
- } ,
149
- ) )
150
- }
151
- } ?;
100
+ possible_partial. seek ( SeekFrom :: End ( 0 ) ) ?;
152
101
153
- file. borrow_mut ( )
154
- . sync_data ( )
155
- . context ( "unable to sync download to disk" ) ?;
102
+ ( possible_partial, downloaded_so_far)
103
+ } else {
104
+ (
105
+ OpenOptions :: new ( )
106
+ . write ( true )
107
+ . create ( true )
108
+ . open ( path)
109
+ . context ( "error creating file for download" ) ?,
110
+ 0 ,
111
+ )
112
+ } ;
156
113
157
- Ok ( ( ) )
158
- } ( )
114
+ let file = RefCell :: new ( file) ;
115
+
116
+ // TODO: the sync callback will stall the async runtime if IO calls block, which is OS dependent. Rearrange.
117
+ download_with_backend ( backend, url, resume_from, & |event| {
118
+ if let Event :: DownloadDataReceived ( data) = event {
119
+ file. borrow_mut ( )
120
+ . write_all ( data)
121
+ . context ( "unable to write download to disk" ) ?;
122
+ }
123
+ match callback {
124
+ Some ( cb) => cb ( event) ,
125
+ None => Ok ( ( ) ) ,
126
+ }
127
+ } )
128
+ . await ?;
129
+
130
+ file. borrow_mut ( )
131
+ . sync_data ( )
132
+ . context ( "unable to sync download to disk" ) ?;
133
+
134
+ Ok :: < ( ) , anyhow:: Error > ( ( ) )
135
+ }
136
+ } ) ( )
137
+ . await
159
138
. map_err ( |e| {
160
139
// TODO: We currently clear up the cached download on any error, should we restrict it to a subset?
161
140
if let Err ( file_err) = remove_file ( path) . context ( "cleaning up cached downloads" ) {
0 commit comments