diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index 02f743ae0..197004aca 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -8,7 +8,7 @@ use crate::collection::{open_collection, Collection}; use crate::err::{AnkiError, NetworkErrorKind, Result, SyncErrorKind}; use crate::i18n::{tr_args, FString, I18n}; use crate::latex::{extract_latex, extract_latex_expanding_clozes, ExtractedLatex}; -use crate::log::default_logger; +use crate::log::{default_logger, Logger}; use crate::media::check::MediaChecker; use crate::media::sync::MediaSyncProgress; use crate::media::MediaManager; @@ -23,6 +23,7 @@ use crate::{backend_proto as pb, log}; use fluent::FluentValue; use prost::Message; use std::collections::{HashMap, HashSet}; +use std::path::PathBuf; use std::sync::{Arc, Mutex}; use tokio::runtime::Runtime; @@ -157,13 +158,13 @@ impl Backend { /// If collection is not open, return an error. fn with_col(&self, func: F) -> Result where - F: FnOnce(&Collection) -> Result, + F: FnOnce(&mut Collection) -> Result, { func( self.col .lock() .unwrap() - .as_ref() + .as_mut() .ok_or(AnkiError::CollectionNotOpen)?, ) } @@ -249,8 +250,8 @@ impl Backend { } fn open_collection(&self, input: pb::OpenCollectionIn) -> Result<()> { - let mut mutex = self.col.lock().unwrap(); - if mutex.is_some() { + let mut col = self.col.lock().unwrap(); + if col.is_some() { return Err(AnkiError::CollectionAlreadyOpen); } @@ -263,7 +264,7 @@ impl Backend { }; let logger = default_logger(log_path)?; - let col = open_collection( + let new_col = open_collection( input.collection_path, input.media_folder_path, input.media_db_path, @@ -272,18 +273,22 @@ impl Backend { logger, )?; - *mutex = Some(col); + *col = Some(new_col); Ok(()) } fn close_collection(&self) -> Result<()> { - let mut mutex = self.col.lock().unwrap(); - if mutex.is_none() { + let mut col = self.col.lock().unwrap(); + if col.is_none() { return Err(AnkiError::CollectionNotOpen); } - *mutex = None; + if !col.as_ref().unwrap().can_close() { + return Err(AnkiError::invalid_input("can't close yet")); + } + + *col = None; Ok(()) } @@ -445,15 +450,38 @@ impl Backend { // fixme: will block other db access fn sync_media(&self, input: SyncMediaIn) -> Result<()> { + let mut guard = self.col.lock().unwrap(); + + let col = guard.as_mut().unwrap(); + col.set_media_sync_running()?; + + let folder = col.media_folder.clone(); + let db = col.media_db.clone(); + let log = col.log.clone(); + + drop(guard); + + let res = self.sync_media_inner(input, folder, db, log); + + self.with_col(|col| col.set_media_sync_finished())?; + + res + } + + fn sync_media_inner( + &self, + input: pb::SyncMediaIn, + folder: PathBuf, + db: PathBuf, + log: Logger, + ) -> Result<()> { let callback = |progress: &MediaSyncProgress| { self.fire_progress_callback(Progress::MediaSync(progress)) }; - self.with_col(|col| { - let mgr = MediaManager::new(&col.media_folder, &col.media_db)?; - let mut rt = Runtime::new().unwrap(); - rt.block_on(mgr.sync_media(callback, &input.endpoint, &input.hkey, col.log.clone())) - }) + let mgr = MediaManager::new(&folder, &db)?; + let mut rt = Runtime::new().unwrap(); + rt.block_on(mgr.sync_media(callback, &input.endpoint, &input.hkey, log)) } fn check_media(&self) -> Result { diff --git a/rslib/src/collection.rs b/rslib/src/collection.rs index 92e96044c..5db81874e 100644 --- a/rslib/src/collection.rs +++ b/rslib/src/collection.rs @@ -1,7 +1,7 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -use crate::err::Result; +use crate::err::{AnkiError, Result}; use crate::i18n::I18n; use crate::log::Logger; use crate::storage::{SqliteStorage, StorageContext}; @@ -26,11 +26,19 @@ pub fn open_collection>( server, i18n, log, + state: CollectionState::Normal, }; Ok(col) } +#[derive(Debug, PartialEq)] +pub enum CollectionState { + Normal, + // in this state, the DB must not be closed + MediaSyncRunning, +} + pub struct Collection { pub(crate) storage: SqliteStorage, #[allow(dead_code)] @@ -40,6 +48,7 @@ pub struct Collection { pub(crate) server: bool, pub(crate) i18n: I18n, pub(crate) log: Logger, + state: CollectionState, } pub(crate) enum CollectionOp {} @@ -97,4 +106,26 @@ impl Collection { res }) } + + pub(crate) fn set_media_sync_running(&mut self) -> Result<()> { + if self.state == CollectionState::Normal { + self.state = CollectionState::MediaSyncRunning; + Ok(()) + } else { + Err(AnkiError::invalid_input("media sync already running")) + } + } + + pub(crate) fn set_media_sync_finished(&mut self) -> Result<()> { + if self.state == CollectionState::MediaSyncRunning { + self.state = CollectionState::Normal; + Ok(()) + } else { + Err(AnkiError::invalid_input("media sync not running")) + } + } + + pub(crate) fn can_close(&self) -> bool { + self.state == CollectionState::Normal + } }