Skip to main content

ChatToHandle

Struct ChatToHandle 

Source
pub struct ChatToHandle { /* private fields */ }
Expand description

Row from the chat_handle_join table.

Implementations§

Source§

impl ChatToHandle

Source

pub fn get(db: &Connection) -> Result<CachedStatement<'_>, TableError>

Prepare the SELECT * statement for the chat_handle_join table.

Source

pub fn participants_by_chat( db: &Connection, ) -> Result<HashMap<i32, BTreeSet<i32>>, TableError>

Map participant handle IDs by chat row ID.

§Example:
use imessage_database::util::dirs::default_db_path;
use imessage_database::tables::table::get_connection;
use imessage_database::tables::chat_handle::ChatToHandle;

let db_path = default_db_path().unwrap();
let conn = get_connection(&db_path).unwrap();
let chatrooms = ChatToHandle::participants_by_chat(&conn);
Source§

impl ChatToHandle

Source

pub fn run_diagnostic( db: &Connection, ) -> Result<ChatHandleDiagnostic, TableError>

Compute diagnostic data for the chat_handle_join table.

Counts chats referenced by messages but missing from this join table.

§Example:
use imessage_database::util::dirs::default_db_path;
use imessage_database::tables::table::get_connection;
use imessage_database::tables::chat_handle::ChatToHandle;

let db_path = default_db_path().unwrap();
let conn = get_connection(&db_path).unwrap();
ChatToHandle::run_diagnostic(&conn);
Source§

impl ChatToHandle

Source

pub fn get_chat_lookup_map( conn: &Connection, ) -> Result<HashMap<i32, i32>, TableError>

Load the chat_lookup canonical chat map when the table exists.

The map links chat IDs split across services to a canonical chat ID for deduplication.

§Example:
use imessage_database::util::dirs::default_db_path;
use imessage_database::tables::table::get_connection;
use imessage_database::tables::chat_handle::ChatToHandle;

let db_path = default_db_path().unwrap();
let conn = get_connection(&db_path).unwrap();
ChatToHandle::get_chat_lookup_map(&conn);
Source

pub fn dedupe( duplicated_data: &HashMap<i32, BTreeSet<i32>>, chat_lookup_map: &HashMap<i32, i32>, handle_groups: &HashMap<i32, i32>, chats: &HashMap<i32, Chat>, ) -> HashMap<i32, i32>

Assign stable deduplicated IDs to chats.

Use handle groups rather than handle row IDs to compare participants. Merge chats connected through chat_lookup, even with different names. For rows with matching participant groups:

  • Merge named rows only with matching display names.
  • Attach unnamed rows to the lowest-row-ID named row with the same participant groups.
  • For an unnamed row linked to a named row through chat_lookup, use that conversation when attaching other unnamed rows with matching participant groups. Preserve existing assignments to other named conversations from chat_lookup.
  • Without a matching named row, merge the unnamed rows.

Assign an ID to every row referenced in chat_lookup_map, including rows without handle rows, to route their messages to the conversation. Assign a separate ID to each component made entirely of such rows.

  • duplicated_data: raw participant handle IDs keyed by chat row ID
  • chat_lookup_map: canonical chat ID keyed by each related chat ID, from ChatToHandle::get_chat_lookup_map
  • handle_groups: group ID keyed by each handle row ID, from HandleGroups::finalize; an unmapped handle keeps its own identity
  • chats: chat rows keyed by row ID; treat a missing row as unnamed

Assuming no new chat-handle relationships have been written to the database, deduplicated data is deterministic across runs.

§Example:
use std::collections::HashMap;

use imessage_database::util::dirs::default_db_path;
use imessage_database::tables::table::get_connection;
use imessage_database::tables::chat::Chat;
use imessage_database::tables::chat_handle::ChatToHandle;
use imessage_database::tables::handle::HandleGroups;

let db_path = default_db_path().unwrap();
let conn = get_connection(&db_path).unwrap();
let chatrooms = ChatToHandle::participants_by_chat(&conn).unwrap();
let handle_groups = HandleGroups::from_db(&conn).unwrap().finalize();
let chats = Chat::by_rowid(&conn).unwrap();
let deduped_chatrooms = ChatToHandle::dedupe(&chatrooms, &HashMap::new(), &handle_groups, &chats);
Source§

impl ChatToHandle

Source

pub fn get_last_activity( db: &Connection, ) -> Result<HashMap<i32, i64>, TableError>

Return the latest message date for each chat row ID.

We query chat_message_join.message_date when that column exists. Otherwise, we join chat_message_join to message and read message.date. This fallback is slower, but we can use it with every supported schema. We omit chats without messages from the result.

§Example:
use imessage_database::util::dirs::default_db_path;
use imessage_database::tables::table::get_connection;
use imessage_database::tables::chat_handle::ChatToHandle;

let db_path = default_db_path().unwrap();
let conn = get_connection(&db_path).unwrap();
let last_activity = ChatToHandle::get_last_activity(&conn);
Source

pub fn representatives( deduplicated_chats: &HashMap<i32, i32>, chats: &HashMap<i32, Chat>, last_activity: &HashMap<i32, i64>, participants_by_chat: &HashMap<i32, BTreeSet<i32>>, ) -> HashMap<i32, i32>

Choose one chat row to represent each deduplicated conversation.

Account for different display_name values within a group:

  1. Use the most recently active row if named.
  2. Otherwise, use the named row with the lowest ID.
  3. For an entirely unnamed group, use the most recently active row with participant rows.
  4. For an unnamed group without participant rows, use the most recently active row.

Prefer rows with a name or participants for building filenames; use an unnamed lookup-only row only for a group without either. When viewing a message in Messages.app, you see the name stored on its chat row.

  • deduplicated_chats: map from each chat row ID to its deduplicated ID, from ChatToHandle::dedupe
  • chats: chat rows keyed by row ID; treat a missing row as unnamed
  • last_activity: map from each chat row ID to its latest message date, from ChatToHandle::get_last_activity
  • participants_by_chat: raw participant handle IDs keyed by chat row ID, from ChatToHandle::participants_by_chat
  • Returns: representative chat row ID for each input chat row ID

Trait Implementations§

Source§

impl Debug for ChatToHandle

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Table for ChatToHandle

Source§

fn from_row(row: &Row<'_>) -> Result<ChatToHandle>

Deserialize a single row into Self. Returns rusqlite::Result for direct use inside rusqlite::query_map / query_row callbacks. For high-level iteration, prefer Table::rows or Table::row.
Source§

fn rows<'stmt, P: Params>( stmt: &'stmt mut Statement<'_>, params: P, ) -> Result<impl Iterator<Item = Result<Self, TableError>> + 'stmt, TableError>
where Self: 'stmt,

Iterate over the results of stmt, deserializing each row through from_row. Return TableError for both fetch and deserialization failures. Pass either a rusqlite::Statement or a rusqlite::CachedStatement (via deref coercion).
Source§

fn row<P: Params>( stmt: &mut Statement<'_>, params: P, ) -> Result<Self, TableError>

Fetch exactly one row from stmt. Returns TableError::QueryError if the row is missing or fails to deserialize. Accepts both rusqlite::Statement and rusqlite::CachedStatement (the latter via deref coercion).

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.