pub struct ChatToHandle { /* private fields */ }Expand description
Row from the chat_handle_join table.
Implementations§
Source§impl ChatToHandle
impl ChatToHandle
Sourcepub fn get(db: &Connection) -> Result<CachedStatement<'_>, TableError>
pub fn get(db: &Connection) -> Result<CachedStatement<'_>, TableError>
Prepare the SELECT * statement for the chat_handle_join table.
Sourcepub fn participants_by_chat(
db: &Connection,
) -> Result<HashMap<i32, BTreeSet<i32>>, TableError>
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
impl ChatToHandle
Sourcepub fn run_diagnostic(
db: &Connection,
) -> Result<ChatHandleDiagnostic, TableError>
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
impl ChatToHandle
Sourcepub fn get_chat_lookup_map(
conn: &Connection,
) -> Result<HashMap<i32, i32>, TableError>
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);Sourcepub 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>
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 fromchat_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 IDchat_lookup_map: canonical chat ID keyed by each related chat ID, fromChatToHandle::get_chat_lookup_maphandle_groups: group ID keyed by each handle row ID, fromHandleGroups::finalize; an unmapped handle keeps its own identitychats: 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
impl ChatToHandle
Sourcepub fn get_last_activity(
db: &Connection,
) -> Result<HashMap<i32, i64>, TableError>
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);Sourcepub 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>
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:
- Use the most recently active row if named.
- Otherwise, use the named row with the lowest ID.
- For an entirely unnamed group, use the most recently active row with participant rows.
- 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, fromChatToHandle::dedupechats: chat rows keyed by row ID; treat a missing row as unnamedlast_activity: map from each chat row ID to its latest message date, fromChatToHandle::get_last_activityparticipants_by_chat: raw participant handle IDs keyed by chat row ID, fromChatToHandle::participants_by_chat- Returns: representative chat row ID for each input chat row ID
Trait Implementations§
Source§impl Debug for ChatToHandle
impl Debug for ChatToHandle
Source§impl Table for ChatToHandle
impl Table for ChatToHandle
Source§fn from_row(row: &Row<'_>) -> Result<ChatToHandle>
fn from_row(row: &Row<'_>) -> Result<ChatToHandle>
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,
fn rows<'stmt, P: Params>(
stmt: &'stmt mut Statement<'_>,
params: P,
) -> Result<impl Iterator<Item = Result<Self, TableError>> + 'stmt, TableError>where
Self: 'stmt,
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>
fn row<P: Params>( stmt: &mut Statement<'_>, params: P, ) -> Result<Self, TableError>
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).