diff --git a/rslib/src/backend/http_sync_server.rs b/rslib/src/backend/http_sync_server.rs index 971e21ddd..efdc9f3ab 100644 --- a/rslib/src/backend/http_sync_server.rs +++ b/rslib/src/backend/http_sync_server.rs @@ -189,7 +189,7 @@ impl Backend { fn download(&self) -> Result> { let server = Box::new(self.col_into_server()?); let mut rt = Runtime::new().unwrap(); - let file = rt.block_on(server.full_download())?; + let file = rt.block_on(server.full_download(None))?; let path = file.into_temp_path().keep()?; Ok(path.to_str().expect("path was not in utf8").into()) } diff --git a/rslib/src/sync/http_client.rs b/rslib/src/sync/http_client.rs index 5b5faf3a7..bd1b47d9f 100644 --- a/rslib/src/sync/http_client.rs +++ b/rslib/src/sync/http_client.rs @@ -142,11 +142,19 @@ impl SyncServer for HTTPSyncClient { Ok(()) } - /// Download collection into a temporary file, returning it. - /// Caller should persist the file in the correct path after checking it. - /// Progress func must be set first. - async fn full_download(mut self: Box) -> Result { - let mut temp_file = NamedTempFile::new()?; + /// Download collection into a temporary file, returning it. Caller should + /// persist the file in the correct path after checking it. Progress func + /// must be set first. The caller should pass the collection's folder in as + /// the temp folder if it wishes to atomically .persist() it. + async fn full_download( + mut self: Box, + col_folder: Option<&Path>, + ) -> Result { + let mut temp_file = if let Some(folder) = col_folder { + NamedTempFile::new_in(folder) + } else { + NamedTempFile::new() + }?; let (size, mut stream) = self.download_inner().await?; let mut progress = FullSyncProgress { transferred_bytes: 0, @@ -410,7 +418,7 @@ mod test { syncer.set_full_sync_progress_fn(Some(Box::new(|progress, _throttle| { println!("progress: {:?}", progress); }))); - let out_path = syncer.full_download().await?; + let out_path = syncer.full_download(None).await?; let mut syncer = Box::new(HTTPSyncClient::new(None, 0)); syncer.set_full_sync_progress_fn(Some(Box::new(|progress, _throttle| { diff --git a/rslib/src/sync/mod.rs b/rslib/src/sync/mod.rs index 48baa82f0..49a4578fd 100644 --- a/rslib/src/sync/mod.rs +++ b/rslib/src/sync/mod.rs @@ -672,8 +672,11 @@ impl Collection { pub(crate) async fn full_download_inner(self, server: Box) -> Result<()> { let col_path = self.col_path.clone(); + let col_folder = col_path + .parent() + .ok_or_else(|| AnkiError::invalid_input("couldn't get col_folder"))?; self.close(false)?; - let out_file = server.full_download().await?; + let out_file = server.full_download(Some(col_folder)).await?; // check file ok let db = open_and_check_sqlite_file(out_file.path())?; db.execute_batch("update col set ls=mod")?; diff --git a/rslib/src/sync/server.rs b/rslib/src/sync/server.rs index 93822c063..cdb55af6f 100644 --- a/rslib/src/sync/server.rs +++ b/rslib/src/sync/server.rs @@ -36,7 +36,10 @@ pub trait SyncServer { /// If `can_consume` is true, the local server will move or remove the file, instead /// creating a copy. The remote server ignores this argument. async fn full_upload(self: Box, col_path: &Path, can_consume: bool) -> Result<()>; - async fn full_download(self: Box) -> Result; + /// If the calling code intends to .persist() the named temp file to + /// atomically update the collection, it should pass in the collection's + /// folder, as .persist() can't work across filesystems. + async fn full_download(self: Box, temp_folder: Option<&Path>) -> Result; } pub struct LocalServer { @@ -199,7 +202,12 @@ impl SyncServer for LocalServer { fs::rename(col_path, &target_col_path).map_err(Into::into) } - async fn full_download(mut self: Box) -> Result { + /// The provided folder is ignored, as in the server case the local data + /// will be sent over the network, instead of written into a local file. + async fn full_download( + mut self: Box, + _col_folder: Option<&Path>, + ) -> Result { // bump usn/mod & close self.col.transact(None, |col| col.storage.increment_usn())?; let col_path = self.col.col_path.clone();