refactor: drop the never-read EmailChanges.destroyed field

It was populated at the client boundary but read nowhere in production -
the doc comment claimed "retained for cursor accounting", yet the cursor
is just new_state. Deletions are dropped at the boundary instead of being
plumbed through and then ignored one layer in.

The upstream crate still returns destroyed; we simply stop carrying it.
Re-add it only when something acts on deletions (e.g. pruning a dedup
key) - keeping an unread Vec costs a copy on every changes page for
nothing.
This commit is contained in:
anonsaber 2026-09-30 12:20:03 +08:00
parent bc68c6b241
commit e130cb1dfb
3 changed files with 4 additions and 6 deletions

View File

@ -55,14 +55,14 @@ pub struct EmailContent {
pub is_long: bool, pub is_long: bool,
} }
/// A channel-neutral page of JMAP Email/changes. Deleted IDs are retained for /// A channel-neutral page of JMAP Email/changes. Only `created` and `updated`
/// cursor accounting but are not notified because they have no metadata. /// drive notification: deletions have no metadata to notify about and are
/// therefore dropped at the boundary rather than plumbed through.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct EmailChanges { pub struct EmailChanges {
pub new_state: String, pub new_state: String,
pub created: Vec<String>, pub created: Vec<String>,
pub updated: Vec<String>, pub updated: Vec<String>,
pub destroyed: Vec<String>,
pub has_more: bool, pub has_more: bool,
} }
@ -320,7 +320,6 @@ mod tests {
new_state: "new".into(), new_state: "new".into(),
created: vec!["email-1".into()], created: vec!["email-1".into()],
updated: vec![], updated: vec![],
destroyed: vec![],
has_more: false, has_more: false,
}) })
} }

View File

@ -172,7 +172,6 @@ impl JmapBackend for JmapClientBackend {
new_state: response.new_state().to_owned(), new_state: response.new_state().to_owned(),
created: response.created().to_owned(), created: response.created().to_owned(),
updated: response.updated().to_owned(), updated: response.updated().to_owned(),
destroyed: response.destroyed().to_owned(),
has_more: response.has_more_changes(), has_more: response.has_more_changes(),
}) })
} }

View File

@ -341,7 +341,7 @@ impl<B: JmapBackend> WorkerHandler for MetadataWorker<B> {
// Re-baseline instead of failing the invocation. jmap-client // Re-baseline instead of failing the invocation. jmap-client
// 0.4.2 exposes no reset signal: `ChangesResponse` only // 0.4.2 exposes no reset signal: `ChangesResponse` only
// carries `oldState`/`newState`/`hasMoreChanges`/`created`/ // carries `oldState`/`newState`/`hasMoreChanges`/`created`/
// `updated`/`destroyed`, so a `/changes` failure is // `updated`, so a `/changes` failure is
// indistinguishable from "the server no longer replays from // indistinguishable from "the server no longer replays from
// this `sinceState`". Returning `Err(())` leaves the cursor // this `sinceState`". Returning `Err(())` leaves the cursor
// untouched, so every later invocation retries the same dead // untouched, so every later invocation retries the same dead