Anki/rslib/src/sync/collection/sanity.rs
Damien Elmes ded805b504
Switch Rust import style (#2330)
* Prepare to switch Rust import style

* Run nightly format

Closes #2320

* Clean up a few imports

* Enable comment wrapping

* Wrap comments
2023-01-18 21:39:55 +10:00

126 lines
3.6 KiB
Rust

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use serde::Deserialize;
use serde::Serialize;
use serde_tuple::Serialize_tuple;
use tracing::debug;
use tracing::info;
use crate::error::SyncErrorKind;
use crate::prelude::*;
use crate::serde::default_on_invalid;
use crate::sync::collection::normal::NormalSyncProgress;
use crate::sync::collection::normal::NormalSyncer;
use crate::sync::collection::protocol::SyncProtocol;
use crate::sync::request::IntoSyncRequest;
#[derive(Serialize, Deserialize, Debug)]
pub struct SanityCheckResponse {
pub status: SanityCheckStatus,
#[serde(rename = "c", default, deserialize_with = "default_on_invalid")]
pub client: Option<SanityCheckCounts>,
#[serde(rename = "s", default, deserialize_with = "default_on_invalid")]
pub server: Option<SanityCheckCounts>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum SanityCheckStatus {
Ok,
Bad,
}
#[derive(Serialize_tuple, Deserialize, Debug, PartialEq, Eq)]
pub struct SanityCheckCounts {
pub counts: SanityCheckDueCounts,
pub cards: u32,
pub notes: u32,
pub revlog: u32,
pub graves: u32,
#[serde(rename = "models")]
pub notetypes: u32,
pub decks: u32,
pub deck_config: u32,
}
#[derive(Serialize_tuple, Deserialize, Debug, Default, PartialEq, Eq)]
pub struct SanityCheckDueCounts {
pub new: u32,
pub learn: u32,
pub review: u32,
}
impl<F> NormalSyncer<'_, F>
where
F: FnMut(NormalSyncProgress, bool),
{
/// Caller should force full sync after rolling back.
pub(in crate::sync) async fn sanity_check(&mut self) -> Result<()> {
let local_counts = self.col.storage.sanity_check_info()?;
debug!("gathered local counts; waiting for server reply");
let SanityCheckResponse {
status,
client,
server,
} = self
.server
.sanity_check(
SanityCheckRequest {
client: local_counts,
}
.try_into_sync_request()?,
)
.await?
.json()?;
debug!("got server reply");
if status != SanityCheckStatus::Ok {
Err(AnkiError::sync_error(
"",
SyncErrorKind::SanityCheckFailed { client, server },
))
} else {
Ok(())
}
}
}
pub fn server_sanity_check(
SanityCheckRequest { mut client }: SanityCheckRequest,
col: &mut Collection,
) -> Result<SanityCheckResponse> {
let mut server = match col.storage.sanity_check_info() {
Ok(info) => info,
Err(err) => {
info!(?client, ?err, "sanity check failed");
return Ok(SanityCheckResponse {
status: SanityCheckStatus::Bad,
client: Some(client),
server: None,
});
}
};
client.counts = Default::default();
// clients on schema 17 and below may send duplicate
// deletion markers, so we can't compare graves until
// the minimum syncing version is schema 18.
client.graves = 0;
server.graves = 0;
Ok(SanityCheckResponse {
status: if client == server {
SanityCheckStatus::Ok
} else {
info!(?client, ?server, "sanity check failed");
SanityCheckStatus::Bad
},
client: Some(client),
server: Some(server),
})
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SanityCheckRequest {
pub client: SanityCheckCounts,
}