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,
}
/// A channel-neutral page of JMAP Email/changes. Deleted IDs are retained for
/// cursor accounting but are not notified because they have no metadata.
/// A channel-neutral page of JMAP Email/changes. Only `created` and `updated`
/// 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)]
pub struct EmailChanges {
pub new_state: String,
pub created: Vec<String>,
pub updated: Vec<String>,
pub destroyed: Vec<String>,
pub has_more: bool,
}
@ -320,7 +320,6 @@ mod tests {
new_state: "new".into(),
created: vec!["email-1".into()],
updated: vec![],
destroyed: vec![],
has_more: false,
})
}

View File

@ -172,7 +172,6 @@ impl JmapBackend for JmapClientBackend {
new_state: response.new_state().to_owned(),
created: response.created().to_owned(),
updated: response.updated().to_owned(),
destroyed: response.destroyed().to_owned(),
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
// 0.4.2 exposes no reset signal: `ChangesResponse` only
// 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
// this `sinceState`". Returning `Err(())` leaves the cursor
// untouched, so every later invocation retries the same dead