Initial commit
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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()));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user