@@ -30,143 +30,146 @@ pub enum Backend {
30
30
Reqwest ( TlsBackend ) ,
31
31
}
32
32
33
- #[ derive( Debug , Copy , Clone ) ]
34
- pub enum TlsBackend {
35
- Rustls ,
36
- NativeTls ,
37
- }
38
-
39
- #[ derive( Debug , Copy , Clone ) ]
40
- pub enum Event < ' a > {
41
- ResumingPartialDownload ,
42
- /// Received the Content-Length of the to-be downloaded data.
43
- DownloadContentLengthReceived ( u64 ) ,
44
- /// Received some data.
45
- DownloadDataReceived ( & ' a [ u8 ] ) ,
46
- }
47
-
48
- type DownloadCallback < ' a > = & ' a dyn Fn ( Event < ' _ > ) -> Result < ( ) > ;
33
+ impl Backend {
34
+ pub async fn download_to_path (
35
+ self ,
36
+ url : & Url ,
37
+ path : & Path ,
38
+ resume_from_partial : bool ,
39
+ callback : Option < DownloadCallback < ' _ > > ,
40
+ ) -> Result < ( ) > {
41
+ let Err ( err) = self
42
+ . download_impl ( url, path, resume_from_partial, callback)
43
+ . await
44
+ else {
45
+ return Ok ( ( ) ) ;
46
+ } ;
49
47
50
- async fn download_with_backend (
51
- backend : Backend ,
52
- url : & Url ,
53
- resume_from : u64 ,
54
- callback : DownloadCallback < ' _ > ,
55
- ) -> Result < ( ) > {
56
- match backend {
57
- Backend :: Curl => curl:: download ( url, resume_from, callback) ,
58
- Backend :: Reqwest ( tls) => reqwest_be:: download ( url, resume_from, callback, tls) . await ,
48
+ // TODO: We currently clear up the cached download on any error, should we restrict it to a subset?
49
+ Err (
50
+ if let Err ( file_err) = remove_file ( path) . context ( "cleaning up cached downloads" ) {
51
+ file_err. context ( err)
52
+ } else {
53
+ err
54
+ } ,
55
+ )
59
56
}
60
- }
61
-
62
- pub async fn download_to_path_with_backend (
63
- backend : Backend ,
64
- url : & Url ,
65
- path : & Path ,
66
- resume_from_partial : bool ,
67
- callback : Option < DownloadCallback < ' _ > > ,
68
- ) -> Result < ( ) > {
69
- let Err ( err) =
70
- download_to_path_with_backend_ ( backend, url, path, resume_from_partial, callback) . await
71
- else {
72
- return Ok ( ( ) ) ;
73
- } ;
74
-
75
- // TODO: We currently clear up the cached download on any error, should we restrict it to a subset?
76
- Err (
77
- if let Err ( file_err) = remove_file ( path) . context ( "cleaning up cached downloads" ) {
78
- file_err. context ( err)
79
- } else {
80
- err
81
- } ,
82
- )
83
- }
84
57
85
- pub async fn download_to_path_with_backend_ (
86
- backend : Backend ,
87
- url : & Url ,
88
- path : & Path ,
89
- resume_from_partial : bool ,
90
- callback : Option < DownloadCallback < ' _ > > ,
91
- ) -> Result < ( ) > {
92
- use std:: cell:: RefCell ;
93
- use std:: fs:: OpenOptions ;
94
- use std:: io:: { Read , Seek , SeekFrom , Write } ;
95
-
96
- let ( file, resume_from) = if resume_from_partial {
97
- // TODO: blocking call
98
- let possible_partial = OpenOptions :: new ( ) . read ( true ) . open ( path) ;
99
-
100
- let downloaded_so_far = if let Ok ( mut partial) = possible_partial {
101
- if let Some ( cb) = callback {
102
- cb ( Event :: ResumingPartialDownload ) ?;
103
-
104
- let mut buf = vec ! [ 0 ; 32768 ] ;
105
- let mut downloaded_so_far = 0 ;
106
- loop {
107
- let n = partial. read ( & mut buf) ?;
108
- downloaded_so_far += n as u64 ;
109
- if n == 0 {
110
- break ;
58
+ async fn download_impl (
59
+ self ,
60
+ url : & Url ,
61
+ path : & Path ,
62
+ resume_from_partial : bool ,
63
+ callback : Option < DownloadCallback < ' _ > > ,
64
+ ) -> Result < ( ) > {
65
+ use std:: cell:: RefCell ;
66
+ use std:: fs:: OpenOptions ;
67
+ use std:: io:: { Read , Seek , SeekFrom , Write } ;
68
+
69
+ let ( file, resume_from) = if resume_from_partial {
70
+ // TODO: blocking call
71
+ let possible_partial = OpenOptions :: new ( ) . read ( true ) . open ( path) ;
72
+
73
+ let downloaded_so_far = if let Ok ( mut partial) = possible_partial {
74
+ if let Some ( cb) = callback {
75
+ cb ( Event :: ResumingPartialDownload ) ?;
76
+
77
+ let mut buf = vec ! [ 0 ; 32768 ] ;
78
+ let mut downloaded_so_far = 0 ;
79
+ loop {
80
+ let n = partial. read ( & mut buf) ?;
81
+ downloaded_so_far += n as u64 ;
82
+ if n == 0 {
83
+ break ;
84
+ }
85
+ cb ( Event :: DownloadDataReceived ( & buf[ ..n] ) ) ?;
111
86
}
112
- cb ( Event :: DownloadDataReceived ( & buf[ ..n] ) ) ?;
113
- }
114
87
115
- downloaded_so_far
88
+ downloaded_so_far
89
+ } else {
90
+ let file_info = partial. metadata ( ) ?;
91
+ file_info. len ( )
92
+ }
116
93
} else {
117
- let file_info = partial. metadata ( ) ?;
118
- file_info. len ( )
119
- }
94
+ 0
95
+ } ;
96
+
97
+ // TODO: blocking call
98
+ let mut possible_partial = OpenOptions :: new ( )
99
+ . write ( true )
100
+ . create ( true )
101
+ . truncate ( false )
102
+ . open ( path)
103
+ . context ( "error opening file for download" ) ?;
104
+
105
+ possible_partial. seek ( SeekFrom :: End ( 0 ) ) ?;
106
+
107
+ ( possible_partial, downloaded_so_far)
120
108
} else {
121
- 0
109
+ (
110
+ OpenOptions :: new ( )
111
+ . write ( true )
112
+ . create ( true )
113
+ . truncate ( true )
114
+ . open ( path)
115
+ . context ( "error creating file for download" ) ?,
116
+ 0 ,
117
+ )
122
118
} ;
123
119
124
- // TODO: blocking call
125
- let mut possible_partial = OpenOptions :: new ( )
126
- . write ( true )
127
- . create ( true )
128
- . truncate ( false )
129
- . open ( path)
130
- . context ( "error opening file for download" ) ?;
120
+ let file = RefCell :: new ( file) ;
131
121
132
- possible_partial. seek ( SeekFrom :: End ( 0 ) ) ?;
122
+ // TODO: the sync callback will stall the async runtime if IO calls block, which is OS dependent. Rearrange.
123
+ self . download ( url, resume_from, & |event| {
124
+ if let Event :: DownloadDataReceived ( data) = event {
125
+ file. borrow_mut ( )
126
+ . write_all ( data)
127
+ . context ( "unable to write download to disk" ) ?;
128
+ }
129
+ match callback {
130
+ Some ( cb) => cb ( event) ,
131
+ None => Ok ( ( ) ) ,
132
+ }
133
+ } )
134
+ . await ?;
133
135
134
- ( possible_partial, downloaded_so_far)
135
- } else {
136
- (
137
- OpenOptions :: new ( )
138
- . write ( true )
139
- . create ( true )
140
- . truncate ( true )
141
- . open ( path)
142
- . context ( "error creating file for download" ) ?,
143
- 0 ,
144
- )
145
- } ;
136
+ file. borrow_mut ( )
137
+ . sync_data ( )
138
+ . context ( "unable to sync download to disk" ) ?;
146
139
147
- let file = RefCell :: new ( file) ;
140
+ Ok :: < ( ) , anyhow:: Error > ( ( ) )
141
+ }
148
142
149
- // TODO: the sync callback will stall the async runtime if IO calls block, which is OS dependent. Rearrange.
150
- download_with_backend ( backend, url, resume_from, & |event| {
151
- if let Event :: DownloadDataReceived ( data) = event {
152
- file. borrow_mut ( )
153
- . write_all ( data)
154
- . context ( "unable to write download to disk" ) ?;
155
- }
156
- match callback {
157
- Some ( cb) => cb ( event) ,
158
- None => Ok ( ( ) ) ,
143
+ async fn download (
144
+ self ,
145
+ url : & Url ,
146
+ resume_from : u64 ,
147
+ callback : DownloadCallback < ' _ > ,
148
+ ) -> Result < ( ) > {
149
+ match self {
150
+ Self :: Curl => curl:: download ( url, resume_from, callback) ,
151
+ Self :: Reqwest ( tls) => reqwest_be:: download ( url, resume_from, callback, tls) . await ,
159
152
}
160
- } )
161
- . await ? ;
153
+ }
154
+ }
162
155
163
- file. borrow_mut ( )
164
- . sync_data ( )
165
- . context ( "unable to sync download to disk" ) ?;
156
+ #[ derive( Debug , Copy , Clone ) ]
157
+ pub enum TlsBackend {
158
+ Rustls ,
159
+ NativeTls ,
160
+ }
166
161
167
- Ok :: < ( ) , anyhow:: Error > ( ( ) )
162
+ #[ derive( Debug , Copy , Clone ) ]
163
+ pub enum Event < ' a > {
164
+ ResumingPartialDownload ,
165
+ /// Received the Content-Length of the to-be downloaded data.
166
+ DownloadContentLengthReceived ( u64 ) ,
167
+ /// Received some data.
168
+ DownloadDataReceived ( & ' a [ u8 ] ) ,
168
169
}
169
170
171
+ type DownloadCallback < ' a > = & ' a dyn Fn ( Event < ' _ > ) -> Result < ( ) > ;
172
+
170
173
#[ cfg( all(
171
174
not( feature = "reqwest-rustls-tls" ) ,
172
175
not( feature = "reqwest-native-tls" ) ,
0 commit comments