This commit is contained in:
Maurus Decimus
2026-05-30 08:09:57 +02:00
parent 0dfbaf8096
commit b173805212
35 changed files with 2160 additions and 95 deletions
+1
View File
@@ -15,4 +15,5 @@ pub mod init;
pub mod maildir_ids;
pub mod managesieve_ids;
pub mod sources;
pub mod sync_state_jmap;
pub mod takeout_ids;
+7
View File
@@ -28,6 +28,13 @@ CREATE TABLE IF NOT EXISTS sync_id_jmap (
CREATE INDEX IF NOT EXISTS sync_id_jmap_local_idx
ON sync_id_jmap (type_name, local_id);
CREATE TABLE IF NOT EXISTS sync_state_jmap (
source_id INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
type_name TEXT NOT NULL,
state TEXT NOT NULL,
PRIMARY KEY (source_id, type_name)
);
CREATE TABLE IF NOT EXISTS sync_id_imap (
source_id INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
type_name TEXT NOT NULL CHECK (type_name IN ('mailbox','email')),
+87
View File
@@ -0,0 +1,87 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
use rusqlite::{Connection, OptionalExtension, params};
use crate::types::ObjectType;
pub fn get(
conn: &Connection,
source_id: i64,
ty: ObjectType,
) -> Result<Option<String>, rusqlite::Error> {
conn.query_row(
"SELECT state FROM sync_state_jmap WHERE source_id = ?1 AND type_name = ?2",
params![source_id, ty.jmap_name()],
|row| row.get(0),
)
.optional()
}
pub fn upsert(
conn: &Connection,
source_id: i64,
ty: ObjectType,
state: &str,
) -> Result<(), rusqlite::Error> {
conn.execute(
"INSERT INTO sync_state_jmap (source_id, type_name, state)
VALUES (?1, ?2, ?3)
ON CONFLICT (source_id, type_name)
DO UPDATE SET state = excluded.state",
params![source_id, ty.jmap_name(), state],
)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::db::init;
use crate::db::sources::{SourceKey, upsert_source};
fn setup() -> (Connection, i64) {
let c = Connection::open_in_memory().unwrap();
init::apply_schema(&c).unwrap();
let sid = upsert_source(
&c,
&SourceKey {
kind: "jmap".to_owned(),
session_url: "https://host".to_owned(),
account_id: "alice".to_owned(),
},
None,
"alice",
)
.unwrap();
(c, sid)
}
#[test]
fn upsert_then_get_returns_latest_per_type() {
let (c, sid) = setup();
assert!(get(&c, sid, ObjectType::Email).unwrap().is_none());
upsert(&c, sid, ObjectType::Email, "s1").unwrap();
upsert(&c, sid, ObjectType::Mailbox, "m1").unwrap();
assert_eq!(
get(&c, sid, ObjectType::Email).unwrap().as_deref(),
Some("s1")
);
assert_eq!(
get(&c, sid, ObjectType::Mailbox).unwrap().as_deref(),
Some("m1")
);
upsert(&c, sid, ObjectType::Email, "s2").unwrap();
assert_eq!(
get(&c, sid, ObjectType::Email).unwrap().as_deref(),
Some("s2")
);
assert_eq!(
get(&c, sid, ObjectType::Mailbox).unwrap().as_deref(),
Some("m1")
);
}
}