Skip to content

Commit 2b51edd

Browse files
committed
Properly propagate errors for both extraction tasks and writes.
1 parent 9054e5e commit 2b51edd

File tree

1 file changed

+40
-15
lines changed

1 file changed

+40
-15
lines changed

src/main.rs

+40-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::collections::{HashMap, HashSet};
22
use std::ffi::OsString;
3+
use std::fmt;
34
use std::io::Read;
45
use std::path::{Path, PathBuf};
56

@@ -18,9 +19,20 @@ struct Config {
1819
log_level: LevelFilter,
1920
}
2021

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+
2133
type AssetMap = HashMap<PathBuf, Vec<u8>>;
2234
type FolderSet = HashSet<OsString>;
23-
type ExtractTask = Vec<JoinHandle<Result<(), io::Error>>>;
35+
type ExtractTask = Vec<JoinHandle<Result<(), AssetWriteError>>>;
2436

2537
fn parse_arguments() -> Config {
2638
let mut verbose = 0;
@@ -102,11 +114,9 @@ fn read_destination_path_and_write<R: Read>(
102114

103115
let asset_path = path.parent().unwrap().join("asset");
104116
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+
}));
110120
} else {
111121
let path_string = path.into_os_string();
112122
if folders.contains(&path_string) {
@@ -120,8 +130,12 @@ async fn write_asset_to_pathname(
120130
asset_data: Vec<u8>,
121131
entry_hash: String,
122132
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)?;
125139
let asset_hash: &str;
126140

127141
match entry_hash.find('/') {
@@ -134,18 +148,23 @@ async fn write_asset_to_pathname(
134148
}
135149

136150
if path_name != target_path {
137-
debug!("sanitizing path {:?} => {:?}", path_name, target_path,);
151+
debug!("sanitizing path {:?} => {:?}", path_name, target_path);
138152
}
139153

140154
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)?;
142156
}
143157

144158
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)?;
146162
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)?;
149168
trace!("{} is written to disk", asset_hash);
150169
Ok(())
151170
}
@@ -201,8 +220,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
201220

202221
debug!("end of archive");
203222
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+
}
206231
}
207232
}
208233
info!("done");

0 commit comments

Comments
 (0)