106 lines
3.0 KiB
Rust
106 lines
3.0 KiB
Rust
/*
|
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*/
|
|
|
|
use rusqlite::{Connection, OptionalExtension, params};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct SourceKey {
|
|
pub kind: String,
|
|
pub session_url: String,
|
|
pub account_id: String,
|
|
}
|
|
|
|
pub fn find_source(conn: &Connection, key: &SourceKey) -> Result<Option<i64>, rusqlite::Error> {
|
|
conn.query_row(
|
|
"SELECT id FROM sources WHERE kind = ?1 AND session_url = ?2 AND account_id = ?3",
|
|
params![key.kind, key.session_url, key.account_id],
|
|
|row| row.get(0),
|
|
)
|
|
.optional()
|
|
}
|
|
|
|
pub fn conflicting_source(
|
|
conn: &Connection,
|
|
kind: &str,
|
|
session_url: &str,
|
|
account_id: &str,
|
|
) -> Result<Option<(String, String)>, rusqlite::Error> {
|
|
conn.query_row(
|
|
"SELECT session_url, account_id FROM sources
|
|
WHERE kind = ?1 AND NOT (session_url = ?2 AND account_id = ?3) LIMIT 1",
|
|
params![kind, session_url, account_id],
|
|
|row| Ok((row.get(0)?, row.get(1)?)),
|
|
)
|
|
.optional()
|
|
}
|
|
|
|
pub fn upsert_source(
|
|
conn: &Connection,
|
|
key: &SourceKey,
|
|
account_name: Option<&str>,
|
|
username: &str,
|
|
) -> Result<i64, rusqlite::Error> {
|
|
conn.execute(
|
|
"INSERT INTO sources (kind, session_url, account_id, account_name, username)
|
|
VALUES (?1, ?2, ?3, ?4, ?5)
|
|
ON CONFLICT (kind, session_url, account_id)
|
|
DO UPDATE SET account_name = excluded.account_name, username = excluded.username",
|
|
params![
|
|
key.kind,
|
|
key.session_url,
|
|
key.account_id,
|
|
account_name,
|
|
username
|
|
],
|
|
)?;
|
|
find_source(conn, key).map(|opt| opt.unwrap_or_else(|| conn.last_insert_rowid()))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::db::init;
|
|
|
|
fn mem() -> Connection {
|
|
let c = Connection::open_in_memory().unwrap();
|
|
init::apply_schema(&c).unwrap();
|
|
c
|
|
}
|
|
|
|
fn key(url: &str, acc: &str) -> SourceKey {
|
|
SourceKey {
|
|
kind: "jmap".to_owned(),
|
|
session_url: url.to_owned(),
|
|
account_id: acc.to_owned(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn upsert_is_idempotent_and_returns_stable_id() {
|
|
let c = mem();
|
|
let k = key("https://a/jmap", "w");
|
|
let id1 = upsert_source(&c, &k, Some("alice"), "alice").unwrap();
|
|
let id2 = upsert_source(&c, &k, Some("alice2"), "alice").unwrap();
|
|
assert_eq!(id1, id2);
|
|
assert!(find_source(&c, &k).unwrap().is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn conflicting_source_detects_a_different_account() {
|
|
let c = mem();
|
|
upsert_source(&c, &key("https://a/jmap", "w"), None, "alice").unwrap();
|
|
assert!(
|
|
conflicting_source(&c, "jmap", "https://a/jmap", "w")
|
|
.unwrap()
|
|
.is_none()
|
|
);
|
|
let conflict = conflicting_source(&c, "jmap", "https://b/jmap", "v")
|
|
.unwrap()
|
|
.unwrap();
|
|
assert_eq!(conflict, ("https://a/jmap".to_owned(), "w".to_owned()));
|
|
}
|
|
}
|