Log to a file, and explain a failed card data swap
A Windows release build has no console, so mlog! lines and panics went nowhere. They now also go to mtgc.log in the app data folder, which the Info tab's Show log file button reveals, and each launch logs the version. A card data update logs each stage, and a failed swap logs every attempt plus what's blocking it: both files' details, whether the folder refuses changes in general, and on Windows whether the files can be opened exclusively and which processes the Restart Manager says hold them.
This commit is contained in:
@@ -19,7 +19,13 @@ fn main() -> ExitCode {
|
||||
eprintln!(" [{}] {:.1}% {}", p.stage, p.overall_percent, p.message)
|
||||
})
|
||||
.and_then(|(tmp, stats)| {
|
||||
mtgc_core::ingest::install_reference_db(&tmp, &dest)?;
|
||||
let installed = mtgc_core::ingest::install_reference_db(&tmp, &dest, |attempt, e| {
|
||||
eprintln!(" install attempt {attempt} failed: {e}")
|
||||
});
|
||||
if installed.is_err() {
|
||||
let _ = std::fs::remove_file(&tmp);
|
||||
}
|
||||
installed?;
|
||||
Ok(stats)
|
||||
});
|
||||
match built {
|
||||
|
||||
@@ -163,13 +163,19 @@ pub fn build_reference_db(
|
||||
const INSTALL_ATTEMPTS: u32 = 5;
|
||||
const INSTALL_RETRY_DELAY: Duration = Duration::from_millis(200);
|
||||
|
||||
/// Move a built reference db over `dest`, consuming `built` even on failure.
|
||||
/// Move a built reference db over `dest`, telling `on_failure` about each
|
||||
/// failed attempt. On failure `built` stays put, for the caller to inspect and
|
||||
/// remove.
|
||||
///
|
||||
/// Anything with `dest` open must let go first: Windows refuses to replace a
|
||||
/// file SQLite has open. A virus scanner can also hold a file this size for a
|
||||
/// moment after it's written, and giving up here would throw away a download
|
||||
/// of several minutes, hence the retries.
|
||||
pub fn install_reference_db(built: &Path, dest: &Path) -> std::io::Result<()> {
|
||||
pub fn install_reference_db(
|
||||
built: &Path,
|
||||
dest: &Path,
|
||||
mut on_failure: impl FnMut(u32, &std::io::Error),
|
||||
) -> std::io::Result<()> {
|
||||
let mut attempt = 1;
|
||||
loop {
|
||||
// Rename straight over `dest`: rename replaces the destination
|
||||
@@ -179,14 +185,14 @@ pub fn install_reference_db(built: &Path, dest: &Path) -> std::io::Result<()> {
|
||||
// with no card data at all.)
|
||||
match std::fs::rename(built, dest) {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(_) if attempt < INSTALL_ATTEMPTS => {
|
||||
Err(e) => {
|
||||
on_failure(attempt, &e);
|
||||
if attempt == INSTALL_ATTEMPTS {
|
||||
return Err(e);
|
||||
}
|
||||
attempt += 1;
|
||||
std::thread::sleep(INSTALL_RETRY_DELAY);
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = std::fs::remove_file(built);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1517,14 +1523,14 @@ mod tests {
|
||||
std::fs::write(&dest, b"old").unwrap();
|
||||
std::fs::write(&built, b"new").unwrap();
|
||||
|
||||
install_reference_db(&built, &dest).unwrap();
|
||||
install_reference_db(&built, &dest, |_, e| panic!("{e}")).unwrap();
|
||||
|
||||
assert_eq!(std::fs::read(&dest).unwrap(), b"new");
|
||||
assert!(!built.exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_failed_install_cleans_up_and_keeps_what_was_there() {
|
||||
fn a_failed_install_reports_every_attempt_and_touches_neither_file() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
// A folder can't be renamed over, on any platform.
|
||||
let dest = dir.path().join("reference.db");
|
||||
@@ -1532,9 +1538,11 @@ mod tests {
|
||||
let built = dest.with_extension("db.tmp");
|
||||
std::fs::write(&built, b"new").unwrap();
|
||||
|
||||
install_reference_db(&built, &dest).unwrap_err();
|
||||
let mut attempts = Vec::new();
|
||||
install_reference_db(&built, &dest, |attempt, _| attempts.push(attempt)).unwrap_err();
|
||||
|
||||
assert_eq!(attempts, [1, 2, 3, 4, 5]);
|
||||
assert!(dest.is_dir());
|
||||
assert!(!built.exists());
|
||||
assert!(built.exists());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user