@@ -5,7 +5,6 @@ use std::path::Path;
55
66use anyhow:: Context ;
77pub use anyhow:: Result ;
8- use tokio:: { runtime:: Handle , task} ;
98use url:: Url ;
109
1110mod errors;
@@ -50,7 +49,7 @@ async fn download_with_backend(
5049
5150type DownloadCallback < ' a > = & ' a dyn Fn ( Event < ' _ > ) -> Result < ( ) > ;
5251
53- pub fn download_to_path_with_backend (
52+ pub async fn download_to_path_with_backend (
5453 backend : Backend ,
5554 url : & Url ,
5655 path : & Path ,
@@ -62,100 +61,80 @@ pub fn download_to_path_with_backend(
6261 use std:: fs:: OpenOptions ;
6362 use std:: io:: { Read , Seek , SeekFrom , Write } ;
6463
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] ) ) ?;
8083 }
81- cb ( Event :: DownloadDataReceived ( & buf[ ..n] ) ) ?;
82- }
8384
84- downloaded_so_far
85+ downloaded_so_far
86+ } else {
87+ let file_info = partial. metadata ( ) ?;
88+ file_info. len ( )
89+ }
8590 } 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+ } ;
9893
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 ( )
10595 . write ( true )
10696 . create ( true )
10797 . open ( path)
108- . context ( "error creating file for download" ) ?,
109- 0 ,
110- )
111- } ;
98+ . context ( "error opening file for download" ) ?;
11299
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 ) ) ?;
152101
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+ } ;
156113
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
159138 . map_err ( |e| {
160139 // TODO: We currently clear up the cached download on any error, should we restrict it to a subset?
161140 if let Err ( file_err) = remove_file ( path) . context ( "cleaning up cached downloads" ) {
0 commit comments