diff --git a/rslib/src/backend.rs b/rslib/src/backend.rs
index e0bf8f909..14b1d4a34 100644
--- a/rslib/src/backend.rs
+++ b/rslib/src/backend.rs
@@ -6,7 +6,7 @@ use crate::backend_proto::backend_input::Value;
use crate::backend_proto::{Empty, RenderedTemplateReplacement, SyncMediaIn};
use crate::cloze::expand_clozes_to_reveal_latex;
use crate::err::{AnkiError, NetworkErrorKind, Result, SyncErrorKind};
-use crate::media::sync::{sync_media, Progress as MediaSyncProgress};
+use crate::media::sync::{MediaSyncer, Progress as MediaSyncProgress};
use crate::media::MediaManager;
use crate::sched::{local_minutes_west_for_stamp, sched_timing_today};
use crate::template::{
@@ -318,14 +318,16 @@ impl Backend {
}
fn sync_media(&self, input: SyncMediaIn) -> Result<()> {
- let mut mgr = MediaManager::new(&input.media_folder, &input.media_db)?;
+ let mgr = MediaManager::new(&input.media_folder, &input.media_db)?;
let callback = |progress: MediaSyncProgress| {
self.fire_progress_callback(Progress::MediaSync(progress))
};
let mut rt = Runtime::new().unwrap();
- rt.block_on(sync_media(&mut mgr, &input.hkey, callback, &input.endpoint))
+
+ let mut syncer = MediaSyncer::new(&mgr, callback, &input.endpoint);
+ rt.block_on(syncer.sync(&input.hkey))
}
}
diff --git a/rslib/src/media/sync.rs b/rslib/src/media/sync.rs
index 911a23ab5..efabcda18 100644
--- a/rslib/src/media/sync.rs
+++ b/rslib/src/media/sync.rs
@@ -33,7 +33,7 @@ pub enum Progress {
RemovedFiles(usize),
}
-struct SyncContext<'a, P>
+pub struct MediaSyncer<'a, P>
where
P: Fn(Progress) -> bool,
{
@@ -45,18 +45,101 @@ where
endpoint: &'a str,
}
-impl
SyncContext<'_, P>
+#[derive(Debug, Deserialize)]
+struct SyncBeginResult {
+ data: Option,
+ err: String,
+}
+
+#[derive(Debug, Deserialize)]
+struct SyncBeginResponse {
+ #[serde(rename = "sk")]
+ sync_key: String,
+ usn: i32,
+}
+
+#[derive(Debug, Clone, Copy)]
+enum LocalState {
+ NotInDB,
+ InDBNotPending,
+ InDBAndPending,
+}
+
+#[derive(PartialEq, Debug)]
+enum RequiredChange {
+ // none also covers the case where we'll later upload
+ None,
+ Download,
+ Delete,
+ RemovePending,
+}
+
+#[derive(Debug, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct RecordBatchRequest {
+ last_usn: i32,
+}
+
+#[derive(Debug, Deserialize)]
+struct RecordBatchResult {
+ data: Option>,
+ err: String,
+}
+
+#[derive(Debug, Deserialize)]
+struct ServerMediaRecord {
+ fname: String,
+ usn: i32,
+ sha1: String,
+}
+
+#[derive(Debug, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct ZipRequest<'a> {
+ files: &'a [&'a String],
+}
+
+#[derive(Serialize_tuple)]
+struct UploadEntry<'a> {
+ fname: &'a str,
+ in_zip_name: Option,
+}
+
+#[derive(Deserialize, Debug)]
+struct UploadResult {
+ data: Option,
+ err: String,
+}
+
+#[derive(Deserialize, Debug)]
+struct UploadReply {
+ processed: usize,
+ current_usn: i32,
+}
+
+#[derive(Serialize)]
+struct FinalizeRequest {
+ local: u32,
+}
+
+#[derive(Debug, Deserialize)]
+struct FinalizeResponse {
+ data: Option,
+ err: String,
+}
+
+impl MediaSyncer<'_, P>
where
P: Fn(Progress) -> bool,
{
- fn new<'a>(mgr: &'a MediaManager, progress_cb: P, endpoint: &'a str) -> SyncContext<'a, P> {
+ pub fn new<'a>(mgr: &'a MediaManager, progress_cb: P, endpoint: &'a str) -> MediaSyncer<'a, P> {
let client = Client::builder()
.connect_timeout(time::Duration::from_secs(30))
.build()
.unwrap();
let ctx = mgr.dbctx();
- SyncContext {
+ MediaSyncer {
mgr,
ctx,
skey: None,
@@ -70,6 +153,44 @@ where
self.skey.as_ref().unwrap()
}
+ #[allow(clippy::useless_let_if_seq)]
+ pub async fn sync(&mut self, hkey: &str) -> Result<()> {
+ // make sure media DB is up to date
+ register_changes(&mut self.ctx, self.mgr.media_folder.as_path())?;
+
+ let meta = self.ctx.get_meta()?;
+ let client_usn = meta.last_sync_usn;
+
+ debug!("beginning media sync");
+ let (sync_key, server_usn) = self.sync_begin(hkey).await?;
+ self.skey = Some(sync_key);
+ debug!("server usn was {}", server_usn);
+
+ let mut actions_performed = false;
+
+ // need to fetch changes from server?
+ if client_usn != server_usn {
+ debug!("differs from local usn {}, fetching changes", client_usn);
+ self.fetch_changes(meta).await?;
+ actions_performed = true;
+ }
+
+ // need to send changes to server?
+ let changes_pending = !self.ctx.get_pending_uploads(1)?.is_empty();
+ if changes_pending {
+ self.send_changes().await?;
+ actions_performed = true;
+ }
+
+ if actions_performed {
+ self.finalize_sync().await?;
+ }
+
+ debug!("media sync complete");
+
+ Ok(())
+ }
+
async fn sync_begin(&self, hkey: &str) -> Result<(String, i32)> {
let url = format!("{}begin", self.endpoint);
@@ -263,83 +384,6 @@ where
}
}
-#[allow(clippy::useless_let_if_seq)]
-pub async fn sync_media(
- mgr: &mut MediaManager,
- hkey: &str,
- progress_cb: F,
- endpoint: &str,
-) -> Result<()>
-where
- F: Fn(Progress) -> bool,
-{
- let mut sctx = SyncContext::new(mgr, progress_cb, endpoint);
-
- // make sure media DB is up to date
- register_changes(&mut sctx.ctx, mgr.media_folder.as_path())?;
-
- let meta = sctx.ctx.get_meta()?;
- let client_usn = meta.last_sync_usn;
-
- debug!("beginning media sync");
- let (sync_key, server_usn) = sctx.sync_begin(hkey).await?;
- sctx.skey = Some(sync_key);
- debug!("server usn was {}", server_usn);
-
- let mut actions_performed = false;
-
- // need to fetch changes from server?
- if client_usn != server_usn {
- debug!("differs from local usn {}, fetching changes", client_usn);
- sctx.fetch_changes(meta).await?;
- actions_performed = true;
- }
-
- // need to send changes to server?
- let changes_pending = !sctx.ctx.get_pending_uploads(1)?.is_empty();
- if changes_pending {
- sctx.send_changes().await?;
- actions_performed = true;
- }
-
- if actions_performed {
- sctx.finalize_sync().await?;
- }
-
- debug!("media sync complete");
-
- Ok(())
-}
-
-#[derive(Debug, Deserialize)]
-struct SyncBeginResult {
- data: Option,
- err: String,
-}
-
-#[derive(Debug, Deserialize)]
-struct SyncBeginResponse {
- #[serde(rename = "sk")]
- sync_key: String,
- usn: i32,
-}
-
-#[derive(Debug, Clone, Copy)]
-enum LocalState {
- NotInDB,
- InDBNotPending,
- InDBAndPending,
-}
-
-#[derive(PartialEq, Debug)]
-enum RequiredChange {
- // no also covers the case where we'll later upload
- None,
- Download,
- Delete,
- RemovePending,
-}
-
fn determine_required_change(
local_sha1: &str,
remote_sha1: &str,
@@ -419,25 +463,6 @@ fn determine_required_changes<'a>(
Ok((to_download, to_delete, to_remove_pending))
}
-#[derive(Debug, Serialize)]
-#[serde(rename_all = "camelCase")]
-struct RecordBatchRequest {
- last_usn: i32,
-}
-
-#[derive(Debug, Deserialize)]
-struct RecordBatchResult {
- data: Option>,
- err: String,
-}
-
-#[derive(Debug, Deserialize)]
-struct ServerMediaRecord {
- fname: String,
- usn: i32,
- sha1: String,
-}
-
async fn ankiweb_json_request(
client: &Client,
url: &str,
@@ -483,12 +508,6 @@ async fn ankiweb_request(
.map_err(Into::into)
}
-#[derive(Debug, Serialize)]
-#[serde(rename_all = "camelCase")]
-struct ZipRequest<'a> {
- files: &'a [&'a String],
-}
-
fn extract_into_media_folder(media_folder: &Path, zip: Bytes) -> Result> {
let reader = io::Cursor::new(zip);
let mut zip = zip::ZipArchive::new(reader)?;
@@ -586,24 +605,6 @@ fn record_clean(ctx: &mut MediaDatabaseContext, clean: &[&String]) -> Result<()>
Ok(())
}
-#[derive(Serialize_tuple)]
-struct UploadEntry<'a> {
- fname: &'a str,
- in_zip_name: Option,
-}
-
-#[derive(Deserialize, Debug)]
-struct UploadResult {
- data: Option,
- err: String,
-}
-
-#[derive(Deserialize, Debug)]
-struct UploadReply {
- processed: usize,
- current_usn: i32,
-}
-
fn zip_files(media_folder: &Path, files: &[MediaEntry]) -> Result> {
let buf = vec![];
@@ -681,21 +682,10 @@ fn media_check_required() -> AnkiError {
}
}
-#[derive(Serialize)]
-struct FinalizeRequest {
- local: u32,
-}
-
-#[derive(Debug, Deserialize)]
-struct FinalizeResponse {
- data: Option,
- err: String,
-}
-
#[cfg(test)]
mod test {
use crate::err::Result;
- use crate::media::sync::{determine_required_change, sync_media, LocalState, RequiredChange};
+ use crate::media::sync::{determine_required_change, LocalState, MediaSyncer, RequiredChange};
use crate::media::MediaManager;
use tempfile::tempdir;
use tokio::runtime::Runtime;
@@ -715,7 +705,8 @@ mod test {
let mut mgr = MediaManager::new(&media_dir, &media_db)?;
- sync_media(&mut mgr, hkey, progress, "https://sync.ankiweb.net/msync/").await?;
+ let mut syncer = MediaSyncer::new(&mut mgr, progress, "https://sync.ankiweb.net/msync/");
+ syncer.sync(hkey).await?;
Ok(())
}