Generalise IncrementableProgress

And use it in entire import_export code instead.
This commit is contained in:
RumovZ 2022-04-29 20:38:01 +02:00
parent 06cc44a3ed
commit aba7b0830c
10 changed files with 140 additions and 96 deletions

View file

@ -56,7 +56,7 @@ impl ImportExportService for Backend {
&self, &self,
input: pb::ImportAnkiPackageRequest, input: pb::ImportAnkiPackageRequest,
) -> Result<pb::ImportAnkiPackageResponse> { ) -> Result<pb::ImportAnkiPackageResponse> {
self.with_col(|col| col.import_apkg(&input.package_path, &mut self.import_progress_fn())) self.with_col(|col| col.import_apkg(&input.package_path, self.import_progress_fn()))
.map(Into::into) .map(Into::into)
} }
@ -90,30 +90,14 @@ impl SearchNode {
} }
impl Backend { impl Backend {
fn import_progress_fn(&self) -> impl FnMut(ImportProgress) -> Result<()> { fn import_progress_fn(&self) -> impl FnMut(ImportProgress, bool) -> bool {
let mut handler = self.new_progress_handler(); let mut handler = self.new_progress_handler();
move |progress| { move |progress, throttle| handler.update(Progress::Import(progress), throttle)
let throttle = matches!(
progress,
ImportProgress::Media(_) | ImportProgress::Notes(_)
);
if handler.update(Progress::Import(progress), throttle) {
Ok(())
} else {
Err(AnkiError::Interrupted)
}
}
} }
fn export_progress_fn(&self) -> impl FnMut(usize) -> Result<()> { fn export_progress_fn(&self) -> impl FnMut(usize, bool) -> bool {
let mut handler = self.new_progress_handler(); let mut handler = self.new_progress_handler();
move |media_files| { move |progress, throttle| handler.update(Progress::Export(progress), throttle)
if handler.update(Progress::Export(media_files), true) {
Ok(())
} else {
Err(AnkiError::Interrupted)
}
}
} }
} }

View file

@ -15,23 +15,84 @@ pub enum ImportProgress {
Notes(usize), Notes(usize),
} }
pub(crate) struct IncrementalProgress<F: FnMut(usize) -> Result<()>> { /// Wrapper around a progress function, usually passed by the [crate::backend::Backend],
progress_fn: F, /// to make repeated calls more ergonomic.
counter: usize, pub(crate) struct IncrementableProgress<P> {
progress_fn: Box<dyn FnMut(P, bool) -> bool>,
count_map: Option<Box<dyn FnMut(usize) -> P>>,
count: usize,
} }
impl<F: FnMut(usize) -> Result<()>> IncrementalProgress<F> { const UPDATE_INTERVAL: usize = 17;
pub(crate) fn new(progress_fn: F) -> Self {
impl<P> IncrementableProgress<P> {
/// `progress_fn: (progress, throttle) -> should_continue`
pub(crate) fn new(progress_fn: impl 'static + FnMut(P, bool) -> bool) -> Self {
Self { Self {
progress_fn, progress_fn: Box::new(progress_fn),
counter: 0, count_map: None,
count: 0,
} }
} }
/// Resets the count, and defines how it should be mapped to a progress value
/// in the future.
pub(crate) fn set_count_map(&mut self, count_map: impl 'static + FnMut(usize) -> P) {
self.count_map = Some(Box::new(count_map));
self.count = 0;
}
/// Increment the progress counter, periodically triggering an update.
/// Returns [AnkiError::Interrupted] if the operation should be cancelled.
/// Must have called `set_count_map()` before calling this.
pub(crate) fn increment(&mut self) -> Result<()> { pub(crate) fn increment(&mut self) -> Result<()> {
self.counter += 1; self.count += 1;
if self.counter % 17 == 0 { if self.count % UPDATE_INTERVAL != 0 {
(self.progress_fn)(self.counter) return Ok(());
}
let progress = self.mapped_progress()?;
self.update(progress, true)
}
/// Manually trigger an update.
/// Returns [AnkiError::Interrupted] if the operation should be cancelled.
pub(crate) fn call(&mut self, progress: P) -> Result<()> {
self.update(progress, false)
}
fn mapped_progress(&mut self) -> Result<P> {
if let Some(count_map) = self.count_map.as_mut() {
Ok(count_map(self.count))
} else {
Err(AnkiError::invalid_input("count_map not set"))
}
}
fn update(&mut self, progress: P, throttle: bool) -> Result<()> {
if (self.progress_fn)(progress, throttle) {
Ok(())
} else {
Err(AnkiError::Interrupted)
}
}
/// Stopgap for returning a progress fn compliant with the media code.
pub(crate) fn get_inner(&mut self) -> Result<impl FnMut(usize) -> bool + '_> {
let count_map = self
.count_map
.as_mut()
.ok_or_else(|| AnkiError::invalid_input("count_map not set"))?;
let progress_fn = &mut self.progress_fn;
Ok(move |count| progress_fn(count_map(count), true))
}
}
impl IncrementableProgress<usize> {
/// Allows incrementing without a map, if the progress is of type [usize].
pub(crate) fn increment_flat(&mut self) -> Result<()> {
self.count += 1;
if self.count % 17 == 0 {
self.update(self.count, true)
} else { } else {
Ok(()) Ok(())
} }

View file

@ -16,6 +16,7 @@ use crate::{
colpkg::export::{export_collection, MediaIter}, colpkg::export::{export_collection, MediaIter},
Meta, Meta,
}, },
IncrementableProgress,
}, },
io::{atomic_rename, tempfile_in_parent_of}, io::{atomic_rename, tempfile_in_parent_of},
prelude::*, prelude::*,
@ -32,8 +33,9 @@ impl Collection {
with_media: bool, with_media: bool,
legacy: bool, legacy: bool,
media_fn: Option<Box<dyn FnOnce(HashSet<PathBuf>) -> MediaIter>>, media_fn: Option<Box<dyn FnOnce(HashSet<PathBuf>) -> MediaIter>>,
progress_fn: impl FnMut(usize) -> Result<()>, progress_fn: impl 'static + FnMut(usize, bool) -> bool,
) -> Result<usize> { ) -> Result<usize> {
let mut progress = IncrementableProgress::new(progress_fn);
let temp_apkg = tempfile_in_parent_of(out_path.as_ref())?; let temp_apkg = tempfile_in_parent_of(out_path.as_ref())?;
let mut temp_col = NamedTempFile::new()?; let mut temp_col = NamedTempFile::new()?;
let temp_col_path = temp_col let temp_col_path = temp_col
@ -67,7 +69,7 @@ impl Collection {
col_size, col_size,
media, media,
&self.tr, &self.tr,
progress_fn, &mut progress,
)?; )?;
atomic_rename(temp_apkg, out_path.as_ref(), true)?; atomic_rename(temp_apkg, out_path.as_ref(), true)?;
Ok(data.notes.len()) Ok(data.notes.len())

View file

@ -12,7 +12,7 @@ use crate::{
media::{extract_media_entries, SafeMediaEntry}, media::{extract_media_entries, SafeMediaEntry},
Meta, Meta,
}, },
ImportProgress, IncrementalProgress, ImportProgress, IncrementableProgress,
}, },
media::{ media::{
files::{add_hash_suffix_to_file_stem, sha1_of_reader}, files::{add_hash_suffix_to_file_stem, sha1_of_reader},
@ -34,21 +34,22 @@ pub(super) struct MediaUseMap {
impl Context<'_> { impl Context<'_> {
pub(super) fn prepare_media(&mut self) -> Result<MediaUseMap> { pub(super) fn prepare_media(&mut self) -> Result<MediaUseMap> {
let progress_fn = |u| (&mut self.progress_fn)(ImportProgress::MediaCheck(u)).is_ok(); self.progress.set_count_map(ImportProgress::MediaCheck);
let existing_sha1s = self.target_col.all_existing_sha1s(progress_fn)?; let existing_sha1s = self
.target_col
.all_existing_sha1s(self.progress.get_inner()?)?;
prepare_media( prepare_media(
&self.meta, &self.meta,
&mut self.archive, &mut self.archive,
&existing_sha1s, &existing_sha1s,
&mut self.progress_fn, &mut self.progress,
) )
} }
pub(super) fn copy_media(&mut self, media_map: &mut MediaUseMap) -> Result<()> { pub(super) fn copy_media(&mut self, media_map: &mut MediaUseMap) -> Result<()> {
let mut progress = self.progress.set_count_map(ImportProgress::Media);
IncrementalProgress::new(|u| (&mut self.progress_fn)(ImportProgress::Media(u)));
for entry in media_map.used_entries() { for entry in media_map.used_entries() {
progress.increment()?; self.progress.increment()?;
entry.copy_from_archive(&mut self.archive, &self.target_col.media_folder)?; entry.copy_from_archive(&mut self.archive, &self.target_col.media_folder)?;
} }
Ok(()) Ok(())
@ -69,10 +70,10 @@ fn prepare_media(
meta: &Meta, meta: &Meta,
archive: &mut ZipArchive<File>, archive: &mut ZipArchive<File>,
existing_sha1s: &HashMap<String, Sha1Hash>, existing_sha1s: &HashMap<String, Sha1Hash>,
progress_fn: &mut impl FnMut(ImportProgress) -> Result<()>, progress: &mut IncrementableProgress<ImportProgress>,
) -> Result<MediaUseMap> { ) -> Result<MediaUseMap> {
let mut media_map = MediaUseMap::default(); let mut media_map = MediaUseMap::default();
let mut progress = IncrementalProgress::new(|u| progress_fn(ImportProgress::MediaCheck(u))); progress.set_count_map(ImportProgress::MediaCheck);
for mut entry in extract_media_entries(meta, archive)? { for mut entry in extract_media_entries(meta, archive)? {
progress.increment()?; progress.increment()?;

View file

@ -19,30 +19,27 @@ use crate::{
import_export::{ import_export::{
gather::ExchangeData, gather::ExchangeData,
package::{Meta, NoteLog}, package::{Meta, NoteLog},
ImportProgress, ImportProgress, IncrementableProgress,
}, },
prelude::*, prelude::*,
search::SearchNode, search::SearchNode,
}; };
type ProgressFn = dyn FnMut(ImportProgress) -> Result<()>;
struct Context<'a> { struct Context<'a> {
target_col: &'a mut Collection, target_col: &'a mut Collection,
archive: ZipArchive<File>, archive: ZipArchive<File>,
meta: Meta, meta: Meta,
data: ExchangeData, data: ExchangeData,
usn: Usn, usn: Usn,
progress_fn: &'a mut ProgressFn, progress: IncrementableProgress<ImportProgress>,
} }
impl Collection { impl Collection {
pub fn import_apkg( pub fn import_apkg(
&mut self, &mut self,
path: impl AsRef<Path>, path: impl AsRef<Path>,
progress_fn: &mut ProgressFn, progress_fn: impl 'static + FnMut(ImportProgress, bool) -> bool,
) -> Result<OpOutput<NoteLog>> { ) -> Result<OpOutput<NoteLog>> {
progress_fn(ImportProgress::File)?;
let file = File::open(path)?; let file = File::open(path)?;
let archive = ZipArchive::new(file)?; let archive = ZipArchive::new(file)?;
@ -57,8 +54,9 @@ impl<'a> Context<'a> {
fn new( fn new(
mut archive: ZipArchive<File>, mut archive: ZipArchive<File>,
target_col: &'a mut Collection, target_col: &'a mut Collection,
progress_fn: &'a mut ProgressFn, progress_fn: impl 'static + FnMut(ImportProgress, bool) -> bool,
) -> Result<Self> { ) -> Result<Self> {
let progress = IncrementableProgress::new(progress_fn);
let meta = Meta::from_archive(&mut archive)?; let meta = Meta::from_archive(&mut archive)?;
let data = ExchangeData::gather_from_archive( let data = ExchangeData::gather_from_archive(
&mut archive, &mut archive,
@ -73,13 +71,13 @@ impl<'a> Context<'a> {
meta, meta,
data, data,
usn, usn,
progress_fn, progress,
}) })
} }
fn import(&mut self) -> Result<NoteLog> { fn import(&mut self) -> Result<NoteLog> {
let mut media_map = self.prepare_media()?; let mut media_map = self.prepare_media()?;
(self.progress_fn)(ImportProgress::File)?; self.progress.call(ImportProgress::File)?;
let note_imports = self.import_notes_and_notetypes(&mut media_map)?; let note_imports = self.import_notes_and_notetypes(&mut media_map)?;
let imported_decks = self.import_decks_and_configs()?; let imported_decks = self.import_decks_and_configs()?;
self.import_cards_and_revlog(&note_imports.id_map, &imported_decks)?; self.import_cards_and_revlog(&note_imports.id_map, &imported_decks)?;

View file

@ -10,11 +10,11 @@ use std::{
use sha1::Sha1; use sha1::Sha1;
use super::{media::MediaUseMap, Context, ProgressFn}; use super::{media::MediaUseMap, Context};
use crate::{ use crate::{
import_export::{ import_export::{
package::{media::safe_normalized_file_name, LogNote, NoteLog}, package::{media::safe_normalized_file_name, LogNote, NoteLog},
ImportProgress, IncrementalProgress, ImportProgress, IncrementableProgress,
}, },
prelude::*, prelude::*,
text::{ text::{
@ -108,7 +108,7 @@ impl Context<'_> {
) -> Result<NoteImports> { ) -> Result<NoteImports> {
let mut ctx = NoteContext::new(self.usn, self.target_col, media_map)?; let mut ctx = NoteContext::new(self.usn, self.target_col, media_map)?;
ctx.import_notetypes(mem::take(&mut self.data.notetypes))?; ctx.import_notetypes(mem::take(&mut self.data.notetypes))?;
ctx.import_notes(mem::take(&mut self.data.notes), self.progress_fn)?; ctx.import_notes(mem::take(&mut self.data.notes), &mut self.progress)?;
Ok(ctx.imports) Ok(ctx.imports)
} }
} }
@ -184,8 +184,12 @@ impl<'n> NoteContext<'n> {
Ok(()) Ok(())
} }
fn import_notes(&mut self, notes: Vec<Note>, progress_fn: &mut ProgressFn) -> Result<()> { fn import_notes(
let mut progress = IncrementalProgress::new(|u| progress_fn(ImportProgress::Notes(u))); &mut self,
notes: Vec<Note>,
progress: &mut IncrementableProgress<ImportProgress>,
) -> Result<()> {
progress.set_count_map(ImportProgress::Notes);
for mut note in notes { for mut note in notes {
progress.increment()?; progress.increment()?;
@ -325,14 +329,14 @@ mod test {
let mut media_map = MediaUseMap::default(); let mut media_map = MediaUseMap::default();
let mut ctx = NoteContext::new(Usn(1), &mut $col, &mut media_map).unwrap(); let mut ctx = NoteContext::new(Usn(1), &mut $col, &mut media_map).unwrap();
ctx.remapped_notetypes.insert($old_notetype, $new_notetype); ctx.remapped_notetypes.insert($old_notetype, $new_notetype);
ctx.import_notes(vec![$note], &mut |_progress| Ok(())) let mut progress = IncrementableProgress::new(|_, _| true);
.unwrap(); ctx.import_notes(vec![$note], &mut progress).unwrap();
ctx.imports.log ctx.imports.log
}}; }};
($col:expr, $note:expr, $media_map:expr) => {{ ($col:expr, $note:expr, $media_map:expr) => {{
let mut ctx = NoteContext::new(Usn(1), &mut $col, &mut $media_map).unwrap(); let mut ctx = NoteContext::new(Usn(1), &mut $col, &mut $media_map).unwrap();
ctx.import_notes(vec![$note], &mut |_progress| Ok(())) let mut progress = IncrementableProgress::new(|_, _| true);
.unwrap(); ctx.import_notes(vec![$note], &mut progress).unwrap();
ctx.imports.log ctx.imports.log
}}; }};
($col:expr, $note:expr) => {{ ($col:expr, $note:expr) => {{

View file

@ -37,10 +37,10 @@ fn roundtrip() {
true, true,
true, true,
None, None,
|_| Ok(()), |_, _| true,
) )
.unwrap(); .unwrap();
target_col.import_apkg(&apkg_path, &mut |_| Ok(())).unwrap(); target_col.import_apkg(&apkg_path, |_, _| true).unwrap();
target_col.assert_decks(); target_col.assert_decks();
target_col.assert_notetype(&notetype); target_col.assert_notetype(&notetype);

View file

@ -22,6 +22,7 @@ use zstd::{
use super::super::{MediaEntries, MediaEntry, Meta, Version}; use super::super::{MediaEntries, MediaEntry, Meta, Version};
use crate::{ use crate::{
collection::CollectionBuilder, collection::CollectionBuilder,
import_export::IncrementableProgress,
io::{atomic_rename, read_dir_files, tempfile_in_parent_of}, io::{atomic_rename, read_dir_files, tempfile_in_parent_of},
media::files::filename_if_normalized, media::files::filename_if_normalized,
prelude::*, prelude::*,
@ -39,8 +40,9 @@ impl Collection {
out_path: impl AsRef<Path>, out_path: impl AsRef<Path>,
include_media: bool, include_media: bool,
legacy: bool, legacy: bool,
progress_fn: impl FnMut(usize) -> Result<()>, progress_fn: impl 'static + FnMut(usize, bool) -> bool,
) -> Result<()> { ) -> Result<()> {
let mut progress = IncrementableProgress::new(progress_fn);
let colpkg_name = out_path.as_ref(); let colpkg_name = out_path.as_ref();
let temp_colpkg = tempfile_in_parent_of(colpkg_name)?; let temp_colpkg = tempfile_in_parent_of(colpkg_name)?;
let src_path = self.col_path.clone(); let src_path = self.col_path.clone();
@ -62,7 +64,7 @@ impl Collection {
src_media_folder, src_media_folder,
legacy, legacy,
&tr, &tr,
progress_fn, &mut progress,
)?; )?;
atomic_rename(temp_colpkg, colpkg_name, true) atomic_rename(temp_colpkg, colpkg_name, true)
} }
@ -103,7 +105,7 @@ fn export_collection_file(
media_dir: Option<PathBuf>, media_dir: Option<PathBuf>,
legacy: bool, legacy: bool,
tr: &I18n, tr: &I18n,
progress_fn: impl FnMut(usize) -> Result<()>, progress: &mut IncrementableProgress<usize>,
) -> Result<()> { ) -> Result<()> {
let meta = if legacy { let meta = if legacy {
Meta::new_legacy() Meta::new_legacy()
@ -118,15 +120,7 @@ fn export_collection_file(
MediaIter::empty() MediaIter::empty()
}; };
export_collection( export_collection(meta, out_path, &mut col_file, col_size, media, tr, progress)
meta,
out_path,
&mut col_file,
col_size,
media,
tr,
progress_fn,
)
} }
/// Write copied collection data without any media. /// Write copied collection data without any media.
@ -143,7 +137,7 @@ pub(crate) fn export_colpkg_from_data(
col_size, col_size,
MediaIter::empty(), MediaIter::empty(),
tr, tr,
|_| Ok(()), &mut IncrementableProgress::new(|_, _| true),
) )
} }
@ -154,7 +148,7 @@ pub(crate) fn export_collection(
col_size: usize, col_size: usize,
media: MediaIter, media: MediaIter,
tr: &I18n, tr: &I18n,
progress_fn: impl FnMut(usize) -> Result<()>, progress: &mut IncrementableProgress<usize>,
) -> Result<()> { ) -> Result<()> {
let out_file = File::create(&out_path)?; let out_file = File::create(&out_path)?;
let mut zip = ZipWriter::new(out_file); let mut zip = ZipWriter::new(out_file);
@ -165,7 +159,7 @@ pub(crate) fn export_collection(
zip.write_all(&meta_bytes)?; zip.write_all(&meta_bytes)?;
write_collection(&meta, &mut zip, col, col_size)?; write_collection(&meta, &mut zip, col, col_size)?;
write_dummy_collection(&mut zip, tr)?; write_dummy_collection(&mut zip, tr)?;
write_media(&meta, &mut zip, media, progress_fn)?; write_media(&meta, &mut zip, media, progress)?;
zip.finish()?; zip.finish()?;
Ok(()) Ok(())
@ -240,10 +234,10 @@ fn write_media(
meta: &Meta, meta: &Meta,
zip: &mut ZipWriter<File>, zip: &mut ZipWriter<File>,
media: MediaIter, media: MediaIter,
progress_fn: impl FnMut(usize) -> Result<()>, progress: &mut IncrementableProgress<usize>,
) -> Result<()> { ) -> Result<()> {
let mut media_entries = vec![]; let mut media_entries = vec![];
write_media_files(meta, zip, media, &mut media_entries, progress_fn)?; write_media_files(meta, zip, media, &mut media_entries, progress)?;
write_media_map(meta, media_entries, zip)?; write_media_map(meta, media_entries, zip)?;
Ok(()) Ok(())
} }
@ -284,12 +278,12 @@ fn write_media_files(
zip: &mut ZipWriter<File>, zip: &mut ZipWriter<File>,
media: MediaIter, media: MediaIter,
media_entries: &mut Vec<MediaEntry>, media_entries: &mut Vec<MediaEntry>,
mut progress_fn: impl FnMut(usize) -> Result<()>, progress: &mut IncrementableProgress<usize>,
) -> Result<()> { ) -> Result<()> {
let mut copier = MediaCopier::new(meta); let mut copier = MediaCopier::new(meta);
for (index, res) in media.0.enumerate() { for (index, res) in media.0.enumerate() {
progress.increment_flat()?;
let path = res?; let path = res?;
progress_fn(index)?;
zip.start_file(index.to_string(), file_options_stored())?; zip.start_file(index.to_string(), file_options_stored())?;

View file

@ -18,7 +18,7 @@ use crate::{
media::{extract_media_entries, SafeMediaEntry}, media::{extract_media_entries, SafeMediaEntry},
Meta, Meta,
}, },
ImportProgress, IncrementalProgress, ImportProgress, IncrementableProgress,
}, },
io::{atomic_rename, tempfile_in_parent_of}, io::{atomic_rename, tempfile_in_parent_of},
media::MediaManager, media::MediaManager,
@ -30,10 +30,11 @@ pub fn import_colpkg(
target_col_path: &str, target_col_path: &str,
target_media_folder: &Path, target_media_folder: &Path,
media_db: &Path, media_db: &Path,
mut progress_fn: impl FnMut(ImportProgress) -> Result<()>, progress_fn: impl 'static + FnMut(ImportProgress, bool) -> bool,
log: &Logger, log: &Logger,
) -> Result<()> { ) -> Result<()> {
progress_fn(ImportProgress::File)?; let mut progress = IncrementableProgress::new(progress_fn);
progress.call(ImportProgress::File)?;
let col_path = PathBuf::from(target_col_path); let col_path = PathBuf::from(target_col_path);
let mut tempfile = tempfile_in_parent_of(&col_path)?; let mut tempfile = tempfile_in_parent_of(&col_path)?;
@ -42,13 +43,13 @@ pub fn import_colpkg(
let meta = Meta::from_archive(&mut archive)?; let meta = Meta::from_archive(&mut archive)?;
copy_collection(&mut archive, &mut tempfile, &meta)?; copy_collection(&mut archive, &mut tempfile, &meta)?;
progress_fn(ImportProgress::File)?; progress.call(ImportProgress::File)?;
check_collection_and_mod_schema(tempfile.path())?; check_collection_and_mod_schema(tempfile.path())?;
progress_fn(ImportProgress::File)?; progress.call(ImportProgress::File)?;
restore_media( restore_media(
&meta, &meta,
&mut progress_fn, &mut progress,
&mut archive, &mut archive,
target_media_folder, target_media_folder,
media_db, media_db,
@ -76,7 +77,7 @@ fn check_collection_and_mod_schema(col_path: &Path) -> Result<()> {
fn restore_media( fn restore_media(
meta: &Meta, meta: &Meta,
mut progress_fn: impl FnMut(ImportProgress) -> Result<()>, progress: &mut IncrementableProgress<ImportProgress>,
archive: &mut ZipArchive<File>, archive: &mut ZipArchive<File>,
media_folder: &Path, media_folder: &Path,
media_db: &Path, media_db: &Path,
@ -89,9 +90,9 @@ fn restore_media(
std::fs::create_dir_all(media_folder)?; std::fs::create_dir_all(media_folder)?;
let media_manager = MediaManager::new(media_folder, media_db)?; let media_manager = MediaManager::new(media_folder, media_db)?;
let mut media_comparer = MediaComparer::new(meta, &mut progress_fn, &media_manager, log)?; let mut media_comparer = MediaComparer::new(meta, progress, &media_manager, log)?;
let mut progress = IncrementalProgress::new(|u| progress_fn(ImportProgress::Media(u))); progress.set_count_map(ImportProgress::Media);
for mut entry in media_entries { for mut entry in media_entries {
progress.increment()?; progress.increment()?;
maybe_restore_media_file(meta, media_folder, archive, &mut entry, &mut media_comparer)?; maybe_restore_media_file(meta, media_folder, archive, &mut entry, &mut media_comparer)?;
@ -157,15 +158,14 @@ struct MediaComparer<'a>(Option<Box<dyn FnMut(&str) -> Result<Option<Sha1Hash>>
impl<'a> MediaComparer<'a> { impl<'a> MediaComparer<'a> {
fn new( fn new(
meta: &Meta, meta: &Meta,
mut progress_fn: impl FnMut(ImportProgress) -> Result<()>, progress: &mut IncrementableProgress<ImportProgress>,
media_manager: &'a MediaManager, media_manager: &'a MediaManager,
log: &Logger, log: &Logger,
) -> Result<Self> { ) -> Result<Self> {
Ok(Self(if meta.media_list_is_hashmap() { Ok(Self(if meta.media_list_is_hashmap() {
None None
} else { } else {
let mut db_progress_fn = |u| progress_fn(ImportProgress::MediaCheck(u)).is_ok(); media_manager.register_changes(&mut progress.get_inner()?, log)?;
media_manager.register_changes(&mut db_progress_fn, log)?;
Some(Box::new(media_manager.checksum_getter())) Some(Box::new(media_manager.checksum_getter()))
})) }))
} }

View file

@ -41,7 +41,7 @@ fn roundtrip() -> Result<()> {
// export to a file // export to a file
let col = collection_with_media(dir, name)?; let col = collection_with_media(dir, name)?;
let colpkg_name = dir.join(format!("{name}.colpkg")); let colpkg_name = dir.join(format!("{name}.colpkg"));
col.export_colpkg(&colpkg_name, true, legacy, |_| Ok(()))?; col.export_colpkg(&colpkg_name, true, legacy, |_, _| true)?;
// import into a new collection // import into a new collection
let anki2_name = dir let anki2_name = dir
@ -57,7 +57,7 @@ fn roundtrip() -> Result<()> {
&anki2_name, &anki2_name,
&import_media_dir, &import_media_dir,
&import_media_db, &import_media_db,
|_| Ok(()), |_, _| true,
&terminal(), &terminal(),
)?; )?;
@ -89,7 +89,7 @@ fn normalization_check_on_export() -> Result<()> {
// manually write a file in the wrong encoding. // manually write a file in the wrong encoding.
std::fs::write(col.media_folder.join("ぱぱ.jpg"), "nfd encoding")?; std::fs::write(col.media_folder.join("ぱぱ.jpg"), "nfd encoding")?;
assert_eq!( assert_eq!(
col.export_colpkg(&colpkg_name, true, false, |_| Ok(())) col.export_colpkg(&colpkg_name, true, false, |_, _| true,)
.unwrap_err(), .unwrap_err(),
AnkiError::MediaCheckRequired AnkiError::MediaCheckRequired
); );