Add new error variant to frontend; ensure errors are mapped appropriately

This commit is contained in:
Damien Elmes 2022-03-17 20:58:35 +10:00
parent bdfa8387a5
commit f8ed4d89ba
9 changed files with 29 additions and 7 deletions

View file

@ -58,6 +58,8 @@ message BackendError {
SEARCH_ERROR = 14; SEARCH_ERROR = 14;
CUSTOM_STUDY_ERROR = 15; CUSTOM_STUDY_ERROR = 15;
IMPORT_ERROR = 16; IMPORT_ERROR = 16;
// Collection imported, but media import failed.
IMPORT_MEDIA_ERROR = 17;
} }
// localized error description suitable for displaying to the user // localized error description suitable for displaying to the user

View file

@ -33,8 +33,8 @@ message PackageMetadata {
VERSION_LEGACY_1 = 1; VERSION_LEGACY_1 = 1;
// When `meta` missing, and collection.anki21 file present. // When `meta` missing, and collection.anki21 file present.
VERSION_LEGACY_2 = 2; VERSION_LEGACY_2 = 2;
/// Implies MediaEntry media map, and zstd compression. // Implies MediaEntry media map, and zstd compression.
/// collection.21b file // collection.21b file
VERSION_LATEST = 3; VERSION_LATEST = 3;
} }

View file

@ -24,6 +24,7 @@ from ..errors import (
DBError, DBError,
ExistsError, ExistsError,
FilteredDeckError, FilteredDeckError,
ImportMediaError,
Interrupted, Interrupted,
InvalidInput, InvalidInput,
LocalizedError, LocalizedError,
@ -219,6 +220,9 @@ def backend_exception_to_pylib(err: backend_pb2.BackendError) -> Exception:
elif val == kind.CUSTOM_STUDY_ERROR: elif val == kind.CUSTOM_STUDY_ERROR:
return CustomStudyError(err.localized) return CustomStudyError(err.localized)
elif val == kind.IMPORT_MEDIA_ERROR:
return ImportMediaError(err.localized)
else: else:
# sadly we can't do exhaustiveness checking on protobuf enums # sadly we can't do exhaustiveness checking on protobuf enums
# assert_exhaustive(val) # assert_exhaustive(val)

View file

@ -80,6 +80,10 @@ class AbortSchemaModification(Exception):
pass pass
class ImportMediaError(LocalizedError):
pass
# legacy # legacy
DeckRenameError = FilteredDeckError DeckRenameError = FilteredDeckError
AnkiError = AbortSchemaModification AnkiError = AbortSchemaModification

View file

@ -11,7 +11,7 @@ import anki.importing as importing
import aqt.deckchooser import aqt.deckchooser
import aqt.forms import aqt.forms
import aqt.modelchooser import aqt.modelchooser
from anki.errors import Interrupted from anki.errors import ImportMediaError, Interrupted
from anki.importing.anki2 import V2ImportIntoV1 from anki.importing.anki2 import V2ImportIntoV1
from anki.importing.apkg import AnkiPackageImporter from anki.importing.apkg import AnkiPackageImporter
from aqt import AnkiQt, gui_hooks from aqt import AnkiQt, gui_hooks
@ -457,7 +457,8 @@ def replace_with_apkg(
mw: aqt.AnkiQt, filename: str, callback: Callable[[bool], None] mw: aqt.AnkiQt, filename: str, callback: Callable[[bool], None]
) -> None: ) -> None:
"""Tries to replace the provided collection with the provided backup, """Tries to replace the provided collection with the provided backup,
then calls the callback. True if success. then calls the callback. True if collection file was imported (even
if media failed).
""" """
dialog = mw.progress.start(immediate=True) dialog = mw.progress.start(immediate=True)
timer = QTimer() timer = QTimer()
@ -495,7 +496,8 @@ def replace_with_apkg(
except Exception as error: except Exception as error:
if not isinstance(error, Interrupted): if not isinstance(error, Interrupted):
showWarning(str(error)) showWarning(str(error))
callback(False) collection_file_imported = isinstance(error, ImportMediaError)
callback(collection_file_imported)
else: else:
callback(True) callback(True)

View file

@ -4,7 +4,7 @@
use crate::{ use crate::{
backend_proto as pb, backend_proto as pb,
backend_proto::backend_error::Kind, backend_proto::backend_error::Kind,
error::{AnkiError, SyncErrorKind}, error::{AnkiError, ImportError, SyncErrorKind},
prelude::*, prelude::*,
}; };
@ -34,6 +34,7 @@ impl AnkiError {
AnkiError::MultipleNotetypesSelected => Kind::InvalidInput, AnkiError::MultipleNotetypesSelected => Kind::InvalidInput,
AnkiError::DatabaseCheckRequired => Kind::InvalidInput, AnkiError::DatabaseCheckRequired => Kind::InvalidInput,
AnkiError::CustomStudyError(_) => Kind::CustomStudyError, AnkiError::CustomStudyError(_) => Kind::CustomStudyError,
AnkiError::ImportError(ImportError::MediaImportFailed(_)) => Kind::ImportMediaError,
AnkiError::ImportError(_) => Kind::ImportError, AnkiError::ImportError(_) => Kind::ImportError,
AnkiError::FileIoError(_) => Kind::IoError, AnkiError::FileIoError(_) => Kind::IoError,
AnkiError::MediaCheckRequired => Kind::InvalidInput, AnkiError::MediaCheckRequired => Kind::InvalidInput,

View file

@ -39,6 +39,7 @@ impl ImportExportService for Backend {
&input.backup_path, &input.backup_path,
&input.col_path, &input.col_path,
&input.media_folder, &input.media_folder,
&self.tr,
self.import_progress_fn(), self.import_progress_fn(),
) )
.map(Into::into) .map(Into::into)

View file

@ -57,6 +57,7 @@ pub fn import_colpkg(
colpkg_path: &str, colpkg_path: &str,
target_col_path: &str, target_col_path: &str,
target_media_folder: &str, target_media_folder: &str,
tr: &I18n,
mut progress_fn: impl FnMut(ImportProgress) -> Result<()>, mut progress_fn: impl FnMut(ImportProgress) -> Result<()>,
) -> Result<()> { ) -> Result<()> {
progress_fn(ImportProgress::Collection)?; progress_fn(ImportProgress::Collection)?;
@ -76,7 +77,12 @@ pub fn import_colpkg(
progress_fn(ImportProgress::Collection)?; progress_fn(ImportProgress::Collection)?;
let media_folder = Path::new(target_media_folder); let media_folder = Path::new(target_media_folder);
let media_import_result = restore_media(&meta, progress_fn, &mut archive, media_folder); let media_import_result = restore_media(&meta, progress_fn, &mut archive, media_folder)
.map_err(|err| {
AnkiError::ImportError(ImportError::MediaImportFailed(
err.localized_description(tr),
))
});
// Proceed with replacing collection, regardless of media import result // Proceed with replacing collection, regardless of media import result
tempfile.as_file().sync_all()?; tempfile.as_file().sync_all()?;

View file

@ -40,6 +40,7 @@ fn roundtrip() -> Result<()> {
for (legacy, name) in [(true, "legacy"), (false, "v3")] { for (legacy, name) in [(true, "legacy"), (false, "v3")] {
// export to a file // export to a file
let col = collection_with_media(dir, name)?; let col = collection_with_media(dir, name)?;
let tr = col.tr.clone();
let colpkg_name = dir.join(format!("{name}.colpkg")); let colpkg_name = dir.join(format!("{name}.colpkg"));
col.export_colpkg(&colpkg_name, true, legacy, |_| ())?; col.export_colpkg(&colpkg_name, true, legacy, |_| ())?;
// import into a new collection // import into a new collection
@ -52,6 +53,7 @@ fn roundtrip() -> Result<()> {
&colpkg_name.to_string_lossy(), &colpkg_name.to_string_lossy(),
&anki2_name, &anki2_name,
import_media_dir.to_str().unwrap(), import_media_dir.to_str().unwrap(),
&tr,
|_| Ok(()), |_| Ok(()),
)?; )?;
// confirm collection imported // confirm collection imported