use super::{ Contact, ContactCustomField, ContactEmail, ContactEmailEntry, ContactEmailId, ContactId, ContactPhone, ContactPhoneId, CustomFieldId, HashSet, NewContact, NewContactCustomField, NewContactEmail, NewContactPhone, NewSocialHandle, Result, SocialHandle, SocialHandleId, UpdateContact, UserId, async_trait, }; /// Repository for contact CRUD operations and sub-collection management. /// /// Contacts have sub-collections (emails, phones, social handles) stored in /// separate tables to enable querying by email address for future integrations. #[async_trait] pub trait ContactRepository: Send + Sync { /// Lists all contacts for a user. async fn list_all(&self, user_id: UserId) -> Result>; /// Retrieves a contact by ID, returning `None` if not found. async fn get_by_id(&self, id: ContactId, user_id: UserId) -> Result>; /// Creates a new contact. async fn create(&self, user_id: UserId, contact: NewContact) -> Result; /// Restores a contact verbatim from a backup, preserving its original ID, /// `created_at`, and `updated_at`. Idempotent (`INSERT OR IGNORE`). /// Sub-collections (emails, phones, social handles, custom fields) are /// restored separately by the caller. async fn restore(&self, user_id: UserId, contact: &Contact) -> Result<()>; /// Updates an existing contact, returning `None` if not found. async fn update( &self, id: ContactId, user_id: UserId, contact: UpdateContact, ) -> Result>; /// Deletes a contact (CASCADE removes sub-entities), returning `true` if deleted. async fn delete(&self, id: ContactId, user_id: UserId) -> Result; /// Records the external source/id for a contact (e.g. after a vCard import), /// used to dedup on re-import. async fn set_external_ref( &self, id: ContactId, user_id: UserId, source: &str, external_id: &str, ) -> Result<()>; /// Deletes multiple contacts by ID, returning the number deleted. async fn delete_many(&self, ids: &[ContactId], user_id: UserId) -> Result; /// Adds a tag to multiple contacts (skips contacts that already have the tag). async fn tag_many(&self, ids: &[ContactId], user_id: UserId, tag: &str) -> Result; /// Lists contacts matching a tag. async fn list_by_tag(&self, user_id: UserId, tag: &str) -> Result>; /// Lists contacts matching a search query and/or tag filter. /// Searches across display_name, nickname, company, title, notes, and email addresses. async fn list_filtered( &self, user_id: UserId, search: Option<&str>, tag: Option<&str>, include_implicit: bool, ) -> Result>; /// Flat (name, email) directory for compose autocomplete, one JOIN, no /// per-contact sub-collection hydration. One row per contact email address. async fn list_email_directory( &self, user_id: UserId, include_implicit: bool, ) -> Result>; /// Finds a contact by email address. async fn find_by_email(&self, user_id: UserId, email: &str) -> Result>; /// Batch check which email addresses belong to known contacts. /// Returns the set of addresses (lowercased) that match at least one contact. async fn find_emails_in_contacts( &self, user_id: UserId, addresses: &[&str], ) -> Result>; /// Promotes an implicit contact to explicit by setting is_implicit = 0. async fn promote_contact(&self, id: ContactId, user_id: UserId) -> Result>; /// Finds a contact by external source and ID (for dedup during import). async fn find_by_external_id( &self, source: &str, ext_id: &str, user_id: UserId, ) -> Result>; /// Adds an email address to a contact. async fn add_email( &self, contact_id: ContactId, user_id: UserId, email: NewContactEmail, ) -> Result; /// Removes an email address from a contact. async fn remove_email(&self, email_id: ContactEmailId, user_id: UserId) -> Result; /// Adds a phone number to a contact. async fn add_phone( &self, contact_id: ContactId, user_id: UserId, phone: NewContactPhone, ) -> Result; /// Removes a phone number from a contact. async fn remove_phone(&self, phone_id: ContactPhoneId, user_id: UserId) -> Result; /// Adds a social handle to a contact. async fn add_social_handle( &self, contact_id: ContactId, user_id: UserId, handle: NewSocialHandle, ) -> Result; /// Removes a social handle from a contact. async fn remove_social_handle( &self, handle_id: SocialHandleId, user_id: UserId, ) -> Result; /// Adds a custom field to a contact. async fn add_custom_field( &self, contact_id: ContactId, user_id: UserId, field: NewContactCustomField, ) -> Result; /// Removes a custom field from a contact. async fn remove_custom_field(&self, field_id: CustomFieldId, user_id: UserId) -> Result; /// Updates a contact email row (address/label/is_primary). Returns the updated row, or `None` if not found. async fn update_email( &self, email_id: ContactEmailId, user_id: UserId, email: NewContactEmail, ) -> Result>; /// Updates a contact phone row (number/label/is_primary). Returns the updated row, or `None` if not found. async fn update_phone( &self, phone_id: ContactPhoneId, user_id: UserId, phone: NewContactPhone, ) -> Result>; /// Updates a social handle row (platform/handle/url). Returns the updated row, or `None` if not found. async fn update_social_handle( &self, handle_id: SocialHandleId, user_id: UserId, handle: NewSocialHandle, ) -> Result>; /// Updates a custom field row (label/value/url). Returns the updated row, or `None` if not found. async fn update_custom_field( &self, field_id: CustomFieldId, user_id: UserId, field: NewContactCustomField, ) -> Result>; }