Skip to content

Commit e0d99e5

Browse files
committed
refactor: more explicit error handling, remove unnecessary obscurity
1 parent 7d94276 commit e0d99e5

File tree

3 files changed

+150
-105
lines changed

3 files changed

+150
-105
lines changed

src/navdata_updater/src/download/downloader.rs

Lines changed: 112 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
use std::cell::RefCell;
2-
use std::collections::HashMap;
32
use std::fs;
43
use std::io::Cursor;
54
use std::path::{Path, PathBuf};
65
use std::rc::Rc;
76

87
use msfs::{commbus::*, network::*};
98

10-
use crate::{download::zip_handler::ZipFileHandler, util};
9+
use crate::{
10+
download::zip_handler::{BatchReturn, ZipFileHandler},
11+
util,
12+
};
1113

1214
pub struct DownloadOptions {
1315
batch_size: usize,
@@ -24,7 +26,6 @@ pub enum DownloadStatus {
2426
NoDownload,
2527
Downloading,
2628
Extracting,
27-
Done,
2829
Failed(String),
2930
}
3031

@@ -44,64 +45,100 @@ impl NavdataDownloader {
4445
}
4546

4647
pub fn on_update(&self) {
47-
let status = self.update_and_get_status();
48-
// If we are extracting, extract the next batch of files
49-
if status == DownloadStatus::Extracting {
50-
// Send the statistics to the JS side
51-
let statistics: DownloadStatistics = self.get_download_statistics().unwrap();
52-
let mut map = HashMap::new();
53-
map.insert("total", statistics.total_files);
54-
map.insert("unzipped", statistics.files_unzipped);
55-
let data = serde_json::to_string(&map).unwrap();
56-
CommBus::call(
57-
"NAVIGRAPH_UnzippedFilesRemaining",
58-
&data,
59-
CommBusBroadcastFlags::All,
60-
);
48+
// Check if we failed and need to send an error message
49+
// We need to do this in its own variable since we can't borrow_mut and borrow at the same time (self.reset_download() borrows mutably)
50+
let failed_message = {
51+
let borrowed_status = self.status.borrow();
52+
if let DownloadStatus::Failed(ref message) = *borrowed_status {
53+
Some(message.clone())
54+
} else {
55+
None
56+
}
57+
};
6158

62-
// Unzip the next batch of files
63-
let has_more_files = self.unzip_batch(10);
64-
if !has_more_files {
65-
println!("[WASM] finished unzip");
59+
if let Some(message) = failed_message {
60+
// Send the error message to the JS side
61+
let error_message = serde_json::json!({
62+
"error": message
63+
});
64+
if let Ok(data) = serde_json::to_string(&error_message) {
65+
println!("[WASM] Sending error message: {}", message);
6666
CommBus::call(
67-
"NAVIGRAPH_NavdataDownloaded",
68-
"",
67+
"NAVIGRAPH_DownloadFailed",
68+
&data,
6969
CommBusBroadcastFlags::All,
7070
);
71+
}
7172

72-
self.clear_zip_handler();
73+
self.reset_download();
74+
}
75+
76+
// Check if we are extracting
77+
// We need to do this in its own variable since we can't borrow_mut and borrow at the same time (self.unzip_batch() borrows mutably)
78+
let extract_next_batch = {
79+
let borrowed_zip_handler = self.zip_handler.borrow();
80+
if let Some(zip_handler) = borrowed_zip_handler.as_ref() {
81+
zip_handler.zip_file_count > zip_handler.current_file_index
82+
} else {
83+
// If there is no zip handler, we are not downloading and we don't need to do anything
84+
return;
85+
}
86+
};
87+
88+
// Only proceed if there are zip files to process
89+
if extract_next_batch {
90+
// Send the statistics to the JS side
91+
if let Ok(statistics) = self.get_download_statistics() {
92+
let data = serde_json::json!({
93+
"total": statistics.total_files,
94+
"unzipped": statistics.files_unzipped,
95+
});
96+
if let Ok(data) = serde_json::to_string(&data) {
97+
CommBus::call(
98+
"NAVIGRAPH_UnzippedFilesRemaining",
99+
&data,
100+
CommBusBroadcastFlags::All,
101+
);
102+
}
73103
}
74-
} else if let DownloadStatus::Failed(_) = status {
75-
let error_message = match status {
76-
DownloadStatus::Failed(message) => message,
77-
_ => "Unknown error".to_owned(),
78-
};
79-
// Send the error message to the JS side
80-
let mut map = HashMap::new();
81-
map.insert("error", &error_message);
82-
let data = serde_json::to_string(&map).unwrap();
83-
CommBus::call(
84-
"NAVIGRAPH_DownloadFailed",
85-
&data,
86-
CommBusBroadcastFlags::All,
87-
);
88104

89-
self.clear_zip_handler();
105+
// Unzip the next batch of files
106+
let unzip_status = self.unzip_batch(self.options.borrow().batch_size);
107+
match unzip_status {
108+
Ok(BatchReturn::Finished) => {
109+
println!("[WASM] Finished extracting");
110+
CommBus::call(
111+
"NAVIGRAPH_NavdataDownloaded",
112+
"",
113+
CommBusBroadcastFlags::All,
114+
);
115+
116+
self.reset_download();
117+
}
118+
Err(e) => {
119+
println!("[WASM] Failed to unzip: {}", e);
120+
let mut status = self.status.borrow_mut();
121+
*status = DownloadStatus::Failed(format!("Failed to unzip: {}", e));
122+
}
123+
_ => (),
124+
}
90125
}
91126
}
92127

93128
pub fn set_download_options(self: &Rc<Self>, args: &str) {
94129
// Parse the JSON
95130
let json_result: Result<serde_json::Value, serde_json::Error> = serde_json::from_str(args);
96-
if json_result.is_err() {
97-
println!(
98-
"[WASM] Failed to parse JSON: {}",
99-
json_result.err().unwrap()
100-
);
131+
if let Err(err) = json_result {
132+
println!("[WASM] Failed to parse JSON: {}", err);
101133
return;
102134
}
135+
// Safe to unwrap since we already checked if it was an error
103136
let json = json_result.unwrap();
104-
let batch_size = json["batchSize"].as_u64().unwrap_or_default() as usize;
137+
// Get batch size, if it fails to parse then just return
138+
let batch_size = match json["batchSize"].as_u64() {
139+
Some(batch_size) => batch_size as usize,
140+
None => return,
141+
};
105142

106143
// Set the options (only batch size for now)
107144
let mut options = self.options.borrow_mut();
@@ -110,7 +147,7 @@ impl NavdataDownloader {
110147

111148
pub fn download(self: &Rc<Self>, args: &str) {
112149
// Silently fail if we are already downloading (maybe we should send an error message?)
113-
if self.update_and_get_status() != DownloadStatus::NoDownload {
150+
if *self.status.borrow() == DownloadStatus::Downloading {
114151
println!("[WASM] Already downloading");
115152
return;
116153
}
@@ -125,6 +162,7 @@ impl NavdataDownloader {
125162
} else {
126163
// If we failed to parse the JSON, set our status to failed (read above for why this is in its own scope)
127164
let mut status = self.status.borrow_mut();
165+
// Safe to unwrap since we already checked if it was an error
128166
let error = json_result.err().unwrap();
129167
*status = DownloadStatus::Failed(format!("JSON Parsing error from JS: {}", error));
130168
println!("[WASM] Failed: {}", error);
@@ -137,14 +175,23 @@ impl NavdataDownloader {
137175
// Check if json has "folder"
138176
let folder = json["folder"].as_str().unwrap_or_default().to_owned();
139177

178+
// Create the request
140179
let captured_self = self.clone();
141-
NetworkRequestBuilder::new(url)
180+
println!("[WASM] Creating request");
181+
match NetworkRequestBuilder::new(url)
142182
.unwrap()
143183
.with_callback(move |request, status_code| {
144184
captured_self.request_finished_callback(request, status_code, folder)
145185
})
146186
.get()
147-
.unwrap();
187+
{
188+
Some(_) => (),
189+
None => {
190+
let mut status = self.status.borrow_mut();
191+
*status = DownloadStatus::Failed("Failed to create request".to_string());
192+
return;
193+
}
194+
}
148195
}
149196

150197
fn request_finished_callback(&self, request: NetworkRequest, status_code: i32, folder: String) {
@@ -165,6 +212,7 @@ impl NavdataDownloader {
165212
match util::delete_folder_recursively(&path) {
166213
Ok(_) => (),
167214
Err(e) => {
215+
println!("[WASM] Failed to delete directory: {}", e);
168216
let mut status = self.status.borrow_mut();
169217
*status = DownloadStatus::Failed(format!("Failed to delete directory: {}", e));
170218
return;
@@ -185,7 +233,7 @@ impl NavdataDownloader {
185233
*status = DownloadStatus::Failed("No data received".to_string());
186234
return;
187235
}
188-
// Extract the data from the request
236+
// Extract the data from the request (safe to unwrap since we already checked if data was none)
189237
let data = data.unwrap();
190238
let cursor = Cursor::new(data);
191239
let zip = zip::ZipArchive::new(cursor);
@@ -229,48 +277,25 @@ impl NavdataDownloader {
229277
})
230278
}
231279

232-
pub fn update_and_get_status(&self) -> DownloadStatus {
233-
// This basically either sets the status to no download, extracting, or done
234-
235-
let mut status = self.status.borrow_mut();
236-
let zip_handler_option = self.zip_handler.borrow();
237-
238-
// If there is no zip handler, we are not downloading
239-
*status = match zip_handler_option.as_ref() {
240-
None => DownloadStatus::NoDownload,
241-
Some(zip_handler) => {
242-
// Downloaded all files
243-
match zip_handler
244-
.zip_file_count
245-
.cmp(&zip_handler.current_file_index)
246-
{
247-
std::cmp::Ordering::Equal => DownloadStatus::Done,
248-
std::cmp::Ordering::Greater => DownloadStatus::Extracting,
249-
_ => return status.clone(),
250-
}
251-
}
252-
};
280+
pub fn unzip_batch(
281+
&self,
282+
batch_size: usize,
283+
) -> Result<BatchReturn, Box<dyn std::error::Error>> {
284+
let mut zip_handler = self.zip_handler.borrow_mut();
253285

254-
// Clone here to return the updated status
255-
status.clone()
256-
}
286+
let handler = zip_handler
287+
.as_mut()
288+
.ok_or_else(|| "Zip handler not found".to_string())?;
289+
let res = handler.unzip_batch(batch_size)?;
257290

258-
pub fn unzip_batch(&self, batch_size: usize) -> bool {
259-
let mut zip_handler = self.zip_handler.borrow_mut();
260-
match zip_handler.as_mut() {
261-
Some(handler) => handler.unzip_batch(batch_size),
262-
None => false,
263-
}
291+
Ok(res)
264292
}
265293

266-
pub fn clear_zip_handler(&self) {
267-
// Borrow mutably and set the zip handler to None. We need to do this in its own scope so that the borrow_mut is dropped
268-
// I really don't like this since update_and_get_status also borrows mutably but I don't know how else to do it/what the best way is
269-
{
270-
let mut zip_handler = self.zip_handler.borrow_mut();
271-
*zip_handler = None;
272-
}
273-
self.update_and_get_status();
294+
pub fn reset_download(&self) {
295+
// Use the take method to replace the current value with None and drop the old value.
296+
self.zip_handler.borrow_mut().take();
297+
298+
*self.status.borrow_mut() = DownloadStatus::NoDownload;
274299
}
275300

276301
pub fn delete_all_navdata(&self) {

src/navdata_updater/src/download/zip_handler.rs

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ use std::path::PathBuf;
44

55
use crate::util;
66

7+
#[derive(PartialEq, Eq)]
8+
pub enum BatchReturn {
9+
MoreFilesToUnzip,
10+
Finished,
11+
}
12+
713
pub struct ZipFileHandler<R: io::Read + io::Seek> {
814
pub zip_archive: Option<zip::ZipArchive<R>>,
915
path_buf: PathBuf,
@@ -23,48 +29,61 @@ impl<R: io::Read + io::Seek> ZipFileHandler<R> {
2329
}
2430
}
2531

26-
pub fn unzip_batch(&mut self, batch_size: usize) -> bool {
32+
pub fn unzip_batch(
33+
&mut self,
34+
batch_size: usize,
35+
) -> Result<BatchReturn, Box<dyn std::error::Error>> {
2736
if self.zip_archive.is_none() {
28-
return false;
37+
return Err("No zip archive to extract".to_string().into());
2938
}
30-
let unwrapped_zip_archive = self.zip_archive.as_mut().unwrap();
39+
40+
let zip_archive = self
41+
.zip_archive
42+
.as_mut()
43+
.ok_or_else(|| "Failed to access zip archive".to_string())?;
44+
3145
for _ in 0..batch_size {
3246
if self.current_file_index >= self.zip_file_count {
3347
// Done extracting, drop the zip archive
3448
self.zip_archive = None;
35-
return false;
49+
return Ok(BatchReturn::Finished);
3650
}
3751

38-
let mut file = match unwrapped_zip_archive.by_index(self.current_file_index) {
39-
Ok(file) => file,
40-
Err(_) => continue,
41-
};
42-
let outpath = match file.enclosed_name() {
43-
Some(path) => self.path_buf.join(path),
44-
None => continue,
45-
};
52+
let mut file = zip_archive.by_index(self.current_file_index)?;
53+
let outpath = self.path_buf.join(
54+
file.enclosed_name()
55+
.ok_or_else(|| "Failed to get enclosed file name".to_string())?,
56+
);
4657

4758
// Check how many times "." appears in the file name
48-
let dot_count = outpath.to_str().unwrap_or_default().matches('.').count();
59+
let dot_count = outpath
60+
.to_str()
61+
.ok_or_else(|| "Failed to convert path to string".to_string())?
62+
.matches('.')
63+
.count();
64+
4965
// Skip if there are more than 1 "." in the file name (MSFS crashes if we try to extract these files for some reason)
5066
if dot_count > 1 {
5167
self.current_file_index += 1;
5268
continue;
5369
}
5470

5571
if (*file.name()).ends_with('/') {
56-
fs::create_dir_all(outpath).unwrap();
72+
fs::create_dir_all(outpath)
73+
.map_err(|_| "Failed to create directory".to_string())?;
5774
} else {
5875
if let Some(p) = outpath.parent() {
5976
if !util::path_exists(p) {
60-
fs::create_dir_all(p).unwrap();
77+
fs::create_dir_all(p)
78+
.map_err(|_| "Failed to create directory".to_string())?;
6179
}
6280
}
63-
let mut outfile = fs::File::create(outpath).unwrap();
64-
io::copy(&mut file, &mut outfile).unwrap();
81+
let mut outfile =
82+
fs::File::create(outpath).map_err(|_| "Failed to create file".to_string())?;
83+
io::copy(&mut file, &mut outfile).map_err(|_| "Failed to copy file".to_string())?;
6584
}
6685
self.current_file_index += 1;
6786
}
68-
true
87+
Ok(BatchReturn::MoreFilesToUnzip)
6988
}
7089
}

src/navdata_updater/src/util.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub fn get_path_type(path: &Path) -> PathType {
2525
let next = dir_res.next();
2626

2727
if next.is_some() {
28+
// Safe to unwrap since we know next is some
2829
if next.unwrap().is_ok() {
2930
return PathType::Directory;
3031
}

0 commit comments

Comments
 (0)