1 parent
0421994fe6
commit
425ea7449b
138 files changed
+5705
-1012
No files matched your search
+21
-63
@@ -1,3 +1,4 @@
|
||||
use crate::queries::sqlite::queries::db_contacts::CONTACT_SELECT_COLS;
|
||||
use rusqlite::{Connection, params};
|
||||
use serde::Serialize;
|
||||
use uuid::Uuid;
|
||||
@@ -58,8 +59,6 @@ fn row_to_contact(row: &rusqlite::Row<'_>) -> rusqlite::Result<Contact> {
|
||||
})
|
||||
}
|
||||
|
||||
const CONTACT_SELECT_COLS: &str = "id, email, name, company, notes, phone, job_title, address, website, birthday, is_favorite, last_contacted, contact_count, source, created_at, updated_at, emails_json, phones_json, addresses_json, EXISTS(SELECT 1 FROM contact_photos p WHERE p.contact_id = contacts.id) AS has_photo";
|
||||
|
||||
fn escape_like(term: &str) -> String {
|
||||
term.replace('\\', "\\\\")
|
||||
.replace('%', "\\%")
|
||||
@@ -68,16 +67,7 @@ fn escape_like(term: &str) -> String {
|
||||
|
||||
pub fn upsert_contact(conn: &Connection, contact: &Contact) -> Result<(), String> {
|
||||
conn.execute(
|
||||
"INSERT INTO contacts
|
||||
(id, email, name, company, notes, phone, job_title, address, website, birthday, is_favorite, last_contacted,
|
||||
contact_count, source, created_at, updated_at, emails_json, phones_json, addresses_json)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
email=excluded.email, name=excluded.name, company=excluded.company, notes=excluded.notes,
|
||||
phone=excluded.phone, job_title=excluded.job_title, address=excluded.address, website=excluded.website, birthday=excluded.birthday,
|
||||
is_favorite=excluded.is_favorite, last_contacted=excluded.last_contacted, contact_count=excluded.contact_count,
|
||||
source=excluded.source, updated_at=excluded.updated_at, emails_json=excluded.emails_json,
|
||||
phones_json=excluded.phones_json, addresses_json=excluded.addresses_json",
|
||||
crate::queries::sqlite::queries::db_contacts::Q_001,
|
||||
params![
|
||||
contact.id,
|
||||
contact.email,
|
||||
@@ -105,7 +95,7 @@ pub fn upsert_contact(conn: &Connection, contact: &Contact) -> Result<(), String
|
||||
}
|
||||
|
||||
pub fn get_contact(conn: &Connection, id: &str) -> Result<Option<Contact>, String> {
|
||||
let sql = format!("SELECT {CONTACT_SELECT_COLS} FROM contacts WHERE id = ?1");
|
||||
let sql = crate::rustbox_query_db_contacts_001!();
|
||||
let result = conn.query_row(&sql, params![id], row_to_contact);
|
||||
|
||||
match result {
|
||||
@@ -116,11 +106,7 @@ pub fn get_contact(conn: &Connection, id: &str) -> Result<Option<Contact>, Strin
|
||||
}
|
||||
|
||||
pub fn get_contact_by_email(conn: &Connection, email: &str) -> Result<Option<Contact>, String> {
|
||||
let sql = format!(
|
||||
"SELECT {CONTACT_SELECT_COLS} FROM contacts \
|
||||
WHERE lower(email) = lower(?1) \
|
||||
OR EXISTS (SELECT 1 FROM json_each(contacts.emails_json) WHERE lower(value) = lower(?1))"
|
||||
);
|
||||
let sql = crate::rustbox_query_db_contacts_002!();
|
||||
let result = conn.query_row(&sql, params![email], row_to_contact);
|
||||
|
||||
match result {
|
||||
@@ -140,22 +126,13 @@ pub fn list_contacts(
|
||||
Some(q) if !q.is_empty() => {
|
||||
let escaped = escape_like(q);
|
||||
let pattern = format!("%{escaped}%");
|
||||
let sql = format!(
|
||||
"SELECT {CONTACT_SELECT_COLS} FROM contacts
|
||||
WHERE name LIKE ?1 ESCAPE '\\' OR email LIKE ?1 ESCAPE '\\' OR emails_json LIKE ?1 ESCAPE '\\'
|
||||
ORDER BY name ASC
|
||||
LIMIT ?2 OFFSET ?3"
|
||||
);
|
||||
let sql = crate::rustbox_query_db_contacts_003!();
|
||||
|
||||
drop(pattern);
|
||||
(sql, Some(escaped))
|
||||
}
|
||||
_ => {
|
||||
let sql = format!(
|
||||
"SELECT {CONTACT_SELECT_COLS} FROM contacts
|
||||
ORDER BY name ASC
|
||||
LIMIT ?1 OFFSET ?2"
|
||||
);
|
||||
let sql = crate::rustbox_query_db_contacts_004!();
|
||||
(sql, None)
|
||||
}
|
||||
};
|
||||
@@ -193,7 +170,10 @@ pub fn list_contacts(
|
||||
|
||||
pub fn delete_contact(conn: &Connection, id: &str) -> Result<bool, String> {
|
||||
let deleted = conn
|
||||
.execute("DELETE FROM contacts WHERE id = ?1", params![id])
|
||||
.execute(
|
||||
crate::queries::sqlite::queries::db_contacts::Q_002,
|
||||
params![id],
|
||||
)
|
||||
.map_err(|e| format!("Failed to delete contact: {e}"))?;
|
||||
Ok(deleted > 0)
|
||||
}
|
||||
@@ -202,12 +182,7 @@ pub fn search_contacts(conn: &Connection, query: &str, limit: u32) -> Result<Vec
|
||||
let escaped = escape_like(query);
|
||||
let pattern = format!("%{escaped}%");
|
||||
|
||||
let sql = format!(
|
||||
"SELECT {CONTACT_SELECT_COLS} FROM contacts
|
||||
WHERE name LIKE ?1 ESCAPE '\\' OR email LIKE ?1 ESCAPE '\\' OR emails_json LIKE ?1 ESCAPE '\\'
|
||||
ORDER BY name ASC
|
||||
LIMIT ?2"
|
||||
);
|
||||
let sql = crate::rustbox_query_db_contacts_005!();
|
||||
|
||||
let mut stmt = conn
|
||||
.prepare(&sql)
|
||||
@@ -231,18 +206,7 @@ pub fn search_known_addresses(
|
||||
let escaped = escape_like(query);
|
||||
let pattern = format!("%{escaped}%");
|
||||
|
||||
let sql = r#"
|
||||
SELECT email, name
|
||||
FROM known_addresses
|
||||
WHERE (email LIKE ?1 ESCAPE '\' OR name LIKE ?1 ESCAPE '\')
|
||||
AND email NOT IN (SELECT email FROM contacts)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM contacts c, json_each(c.emails_json) alt
|
||||
WHERE lower(alt.value) = lower(known_addresses.email)
|
||||
)
|
||||
ORDER BY email ASC
|
||||
LIMIT ?2
|
||||
"#;
|
||||
let sql = crate::queries::sqlite::queries::db_contacts::Q_003;
|
||||
|
||||
let mut stmt = conn
|
||||
.prepare(sql)
|
||||
@@ -273,8 +237,7 @@ pub fn populate_known_addresses(
|
||||
) -> Result<(), String> {
|
||||
if !from_address.is_empty() {
|
||||
conn.execute(
|
||||
"INSERT INTO known_addresses (email, name) VALUES (?1, ?2)
|
||||
ON CONFLICT(email) DO UPDATE SET name = excluded.name WHERE excluded.name != ''",
|
||||
crate::queries::sqlite::queries::db_contacts::Q_004,
|
||||
params![from_address, from_name],
|
||||
)
|
||||
.map_err(|e| format!("Failed to insert known address (from): {e}"))?;
|
||||
@@ -300,8 +263,7 @@ fn insert_addresses_from_json(conn: &Connection, json: &str) -> Result<(), Strin
|
||||
}
|
||||
let name = entry.get("name").and_then(|v| v.as_str()).unwrap_or("");
|
||||
conn.execute(
|
||||
"INSERT INTO known_addresses (email, name) VALUES (?1, ?2)
|
||||
ON CONFLICT(email) DO UPDATE SET name = excluded.name WHERE excluded.name != ''",
|
||||
crate::queries::sqlite::queries::db_contacts::Q_005,
|
||||
params![email, name],
|
||||
)
|
||||
.map_err(|e| format!("Failed to insert known address (json): {e}"))?;
|
||||
@@ -313,11 +275,7 @@ fn insert_addresses_from_json(conn: &Connection, json: &str) -> Result<(), Strin
|
||||
#[allow(dead_code)]
|
||||
pub fn increment_contact_count(conn: &Connection, email: &str) -> Result<(), String> {
|
||||
conn.execute(
|
||||
"UPDATE contacts
|
||||
SET contact_count = contact_count + 1,
|
||||
last_contacted = datetime('now'),
|
||||
updated_at = datetime('now')
|
||||
WHERE email = ?1",
|
||||
crate::queries::sqlite::queries::db_contacts::Q_006,
|
||||
params![email],
|
||||
)
|
||||
.map_err(|e| format!("Failed to increment contact count: {e}"))?;
|
||||
@@ -328,8 +286,7 @@ pub fn increment_contact_count(conn: &Connection, email: &str) -> Result<(), Str
|
||||
pub fn auto_add_contact(conn: &Connection, email: &str, name: &str) -> Result<(), String> {
|
||||
let id = Uuid::new_v4().to_string();
|
||||
conn.execute(
|
||||
"INSERT OR IGNORE INTO contacts (id, email, name, source)
|
||||
VALUES (?1, ?2, ?3, 'auto')",
|
||||
crate::queries::sqlite::queries::db_contacts::Q_007,
|
||||
params![id, email, name],
|
||||
)
|
||||
.map_err(|e| format!("Failed to auto-add contact: {e}"))?;
|
||||
@@ -343,9 +300,10 @@ pub fn set_contact_photo(
|
||||
data: &[u8],
|
||||
) -> Result<(), String> {
|
||||
conn.execute(
|
||||
"INSERT INTO contact_photos (contact_id, content_type, data, updated_at) VALUES (?1, ?2, ?3, datetime('now')) ON CONFLICT(contact_id) DO UPDATE SET content_type=excluded.content_type, data=excluded.data, updated_at=datetime('now')",
|
||||
crate::queries::sqlite::queries::db_contacts::Q_008,
|
||||
params![contact_id, content_type, data],
|
||||
).map_err(|e| format!("Failed to save contact photo: {e}"))?;
|
||||
)
|
||||
.map_err(|e| format!("Failed to save contact photo: {e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -354,7 +312,7 @@ pub fn get_contact_photo(
|
||||
contact_id: &str,
|
||||
) -> Result<Option<(String, Vec<u8>)>, String> {
|
||||
match conn.query_row(
|
||||
"SELECT content_type, data FROM contact_photos WHERE contact_id = ?1",
|
||||
crate::queries::sqlite::queries::db_contacts::Q_009,
|
||||
params![contact_id],
|
||||
|row| Ok((row.get(0)?, row.get(1)?)),
|
||||
) {
|
||||
@@ -366,7 +324,7 @@ pub fn get_contact_photo(
|
||||
|
||||
pub fn delete_contact_photo(conn: &Connection, contact_id: &str) -> Result<bool, String> {
|
||||
conn.execute(
|
||||
"DELETE FROM contact_photos WHERE contact_id = ?1",
|
||||
crate::queries::sqlite::queries::db_contacts::Q_010,
|
||||
params![contact_id],
|
||||
)
|
||||
.map(|n| n > 0)
|
||||
|
||||
Reference in new issue
Block a user