1
1
use std:: collections:: { HashMap , HashSet } ;
2
2
use std:: ffi:: OsString ;
3
+ use std:: fmt;
3
4
use std:: io:: Read ;
4
5
use std:: path:: { Path , PathBuf } ;
5
6
@@ -18,9 +19,20 @@ struct Config {
18
19
log_level : LevelFilter ,
19
20
}
20
21
22
+ struct AssetWriteError {
23
+ error : io:: Error ,
24
+ path : String ,
25
+ }
26
+
27
+ impl fmt:: Display for AssetWriteError {
28
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
29
+ write ! ( f, "{:?}: {}" , self . path, self . error)
30
+ }
31
+ }
32
+
21
33
type AssetMap = HashMap < PathBuf , Vec < u8 > > ;
22
34
type FolderSet = HashSet < OsString > ;
23
- type ExtractTask = Vec < JoinHandle < Result < ( ) , io :: Error > > > ;
35
+ type ExtractTask = Vec < JoinHandle < Result < ( ) , AssetWriteError > > > ;
24
36
25
37
fn parse_arguments ( ) -> Config {
26
38
let mut verbose = 0 ;
@@ -102,11 +114,9 @@ fn read_destination_path_and_write<R: Read>(
102
114
103
115
let asset_path = path. parent ( ) . unwrap ( ) . join ( "asset" ) ;
104
116
if let Some ( asset_data) = assets. remove ( & asset_path) {
105
- tasks. push ( tokio:: spawn ( write_asset_to_pathname (
106
- asset_data,
107
- path. to_string_lossy ( ) . to_string ( ) ,
108
- path_name,
109
- ) ) ) ;
117
+ tasks. push ( tokio:: spawn ( async move {
118
+ write_asset_to_pathname ( asset_data, path. to_string_lossy ( ) . to_string ( ) , path_name) . await
119
+ } ) ) ;
110
120
} else {
111
121
let path_string = path. into_os_string ( ) ;
112
122
if folders. contains ( & path_string) {
@@ -120,8 +130,12 @@ async fn write_asset_to_pathname(
120
130
asset_data : Vec < u8 > ,
121
131
entry_hash : String ,
122
132
path_name : String ,
123
- ) -> Result < ( ) , io:: Error > {
124
- let target_path = sanitize_path:: sanitize_path ( & path_name) ?;
133
+ ) -> Result < ( ) , AssetWriteError > {
134
+ let to_asset_error = |error : io:: Error | AssetWriteError {
135
+ error,
136
+ path : path_name. clone ( ) ,
137
+ } ;
138
+ let target_path = sanitize_path:: sanitize_path ( & path_name) . map_err ( to_asset_error) ?;
125
139
let asset_hash: & str ;
126
140
127
141
match entry_hash. find ( '/' ) {
@@ -134,18 +148,23 @@ async fn write_asset_to_pathname(
134
148
}
135
149
136
150
if path_name != target_path {
137
- debug ! ( "sanitizing path {:?} => {:?}" , path_name, target_path, ) ;
151
+ debug ! ( "sanitizing path {:?} => {:?}" , path_name, target_path) ;
138
152
}
139
153
140
154
if let Some ( parent) = Path :: new ( & target_path) . parent ( ) {
141
- fs:: create_dir_all ( parent) . await ?;
155
+ fs:: create_dir_all ( parent) . await . map_err ( to_asset_error ) ?;
142
156
}
143
157
144
158
info ! ( "extracting {} to {:?}" , asset_hash, target_path) ;
145
- let file = fs:: File :: create ( & target_path) . await ?;
159
+ let file = fs:: File :: create ( & target_path)
160
+ . await
161
+ . map_err ( to_asset_error) ?;
146
162
let mut file_writer = io:: BufWriter :: new ( file) ;
147
- file_writer. write_all ( & asset_data) . await ?;
148
- file_writer. flush ( ) . await ?;
163
+ file_writer
164
+ . write_all ( & asset_data)
165
+ . await
166
+ . map_err ( to_asset_error) ?;
167
+ file_writer. flush ( ) . await . map_err ( to_asset_error) ?;
149
168
trace ! ( "{} is written to disk" , asset_hash) ;
150
169
Ok ( ( ) )
151
170
}
@@ -201,8 +220,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
201
220
202
221
debug ! ( "end of archive" ) ;
203
222
for task in tasks {
204
- if let Err ( e) = task. await {
205
- warn ! ( "an extraction task has failed: {}" , e) ;
223
+ match task. await {
224
+ Ok ( Ok ( ( ) ) ) => { }
225
+ Ok ( Err ( e) ) => {
226
+ warn ! ( "failed to write asset: {}" , e) ;
227
+ }
228
+ Err ( e) => {
229
+ warn ! ( "an extraction task has failed: {}" , e) ;
230
+ }
206
231
}
207
232
}
208
233
info ! ( "done" ) ;
0 commit comments