make the collection optional

This commit is contained in:
Damien Elmes 2020-03-13 20:49:01 +10:00
parent d0d6aa1433
commit 1322d8c617
3 changed files with 46 additions and 21 deletions

View file

@ -91,6 +91,7 @@ message BackendError {
SyncError sync_error = 7; SyncError sync_error = 7;
// user interrupted operation // user interrupted operation
Empty interrupted = 8; Empty interrupted = 8;
Empty collection_not_open = 9;
} }
} }

View file

@ -32,7 +32,7 @@ mod dbproxy;
pub type ProtoProgressCallback = Box<dyn Fn(Vec<u8>) -> bool + Send>; pub type ProtoProgressCallback = Box<dyn Fn(Vec<u8>) -> bool + Send>;
pub struct Backend { pub struct Backend {
col: Arc<Mutex<Collection>>, col: Arc<Mutex<Option<Collection>>>,
#[allow(dead_code)] #[allow(dead_code)]
col_path: PathBuf, col_path: PathBuf,
media_folder: PathBuf, media_folder: PathBuf,
@ -61,6 +61,7 @@ fn anki_error_to_proto_error(err: AnkiError, i18n: &I18n) -> pb::BackendError {
} }
AnkiError::SyncError { kind, .. } => V::SyncError(pb::SyncError { kind: kind.into() }), AnkiError::SyncError { kind, .. } => V::SyncError(pb::SyncError { kind: kind.into() }),
AnkiError::Interrupted => V::Interrupted(Empty {}), AnkiError::Interrupted => V::Interrupted(Empty {}),
AnkiError::CollectionNotOpen => V::CollectionNotOpen(pb::Empty {}),
}; };
pb::BackendError { pb::BackendError {
@ -156,7 +157,7 @@ impl Backend {
log: Logger, log: Logger,
) -> Result<Backend> { ) -> Result<Backend> {
Ok(Backend { Ok(Backend {
col: Arc::new(Mutex::new(col)), col: Arc::new(Mutex::new(Some(col))),
col_path: col_path.into(), col_path: col_path.into(),
media_folder: media_folder.into(), media_folder: media_folder.into(),
media_db: media_db.into(), media_db: media_db.into(),
@ -193,6 +194,22 @@ impl Backend {
buf buf
} }
/// If collection is open, run the provided closure while holding
/// the mutex.
/// If collection is not open, return an error.
fn with_col<F, T>(&self, func: F) -> Result<T>
where
F: FnOnce(&Collection) -> Result<T>,
{
func(
self.col
.lock()
.unwrap()
.as_ref()
.ok_or(AnkiError::CollectionNotOpen)?,
)
}
fn run_command(&mut self, input: pb::BackendInput) -> pb::BackendOutput { fn run_command(&mut self, input: pb::BackendInput) -> pb::BackendOutput {
let oval = if let Some(ival) = input.value { let oval = if let Some(ival) = input.value {
match self.run_command_inner(ival) { match self.run_command_inner(ival) {
@ -433,7 +450,9 @@ impl Backend {
|progress: usize| self.fire_progress_callback(Progress::MediaCheck(progress as u32)); |progress: usize| self.fire_progress_callback(Progress::MediaCheck(progress as u32));
let mgr = MediaManager::new(&self.media_folder, &self.media_db)?; let mgr = MediaManager::new(&self.media_folder, &self.media_db)?;
self.col.lock().unwrap().transact(None, |ctx| {
self.with_col(|col| {
col.transact(None, |ctx| {
let mut checker = MediaChecker::new(ctx, &mgr, callback); let mut checker = MediaChecker::new(ctx, &mgr, callback);
let mut output = checker.check()?; let mut output = checker.check()?;
@ -446,6 +465,7 @@ impl Backend {
have_trash: output.trash_count > 0, have_trash: output.trash_count > 0,
}) })
}) })
})
} }
fn remove_media_files(&self, fnames: &[String]) -> Result<()> { fn remove_media_files(&self, fnames: &[String]) -> Result<()> {
@ -490,11 +510,13 @@ impl Backend {
|progress: usize| self.fire_progress_callback(Progress::MediaCheck(progress as u32)); |progress: usize| self.fire_progress_callback(Progress::MediaCheck(progress as u32));
let mgr = MediaManager::new(&self.media_folder, &self.media_db)?; let mgr = MediaManager::new(&self.media_folder, &self.media_db)?;
self.col.lock().unwrap().transact(None, |ctx| { self.with_col(|col| {
col.transact(None, |ctx| {
let mut checker = MediaChecker::new(ctx, &mgr, callback); let mut checker = MediaChecker::new(ctx, &mgr, callback);
checker.empty_trash() checker.empty_trash()
}) })
})
} }
fn restore_trash(&self) -> Result<()> { fn restore_trash(&self) -> Result<()> {
@ -502,18 +524,17 @@ impl Backend {
|progress: usize| self.fire_progress_callback(Progress::MediaCheck(progress as u32)); |progress: usize| self.fire_progress_callback(Progress::MediaCheck(progress as u32));
let mgr = MediaManager::new(&self.media_folder, &self.media_db)?; let mgr = MediaManager::new(&self.media_folder, &self.media_db)?;
self.col.lock().unwrap().transact(None, |ctx| { self.with_col(|col| {
col.transact(None, |ctx| {
let mut checker = MediaChecker::new(ctx, &mgr, callback); let mut checker = MediaChecker::new(ctx, &mgr, callback);
checker.restore_trash() checker.restore_trash()
}) })
})
} }
pub fn db_command(&self, input: &[u8]) -> Result<String> { pub fn db_command(&self, input: &[u8]) -> Result<String> {
self.col self.with_col(|col| col.with_ctx(|ctx| db_command_bytes(&ctx.storage, input)))
.lock()
.unwrap()
.with_ctx(|ctx| db_command_bytes(&ctx.storage, input))
} }
} }

View file

@ -33,6 +33,9 @@ pub enum AnkiError {
#[fail(display = "The user interrupted the operation.")] #[fail(display = "The user interrupted the operation.")]
Interrupted, Interrupted,
#[fail(display = "Operation requires an open collection.")]
CollectionNotOpen,
} }
// error helpers // error helpers