v0.1.0
RustBox CI / rustbox (push) Canceled after 0s

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-30 17:22:17 +01:00
1 parent 0421994fe6
commit 425ea7449b
138 files changed
+5705 -1012

No files matched your search

+21 -63
View File
@@ -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)