imessage_database/tables/
handle.rs1use rusqlite::{CachedStatement, Connection, Error, Result, Row};
6use std::collections::{BTreeSet, HashMap};
7
8use crate::{
9 error::table::TableError,
10 tables::table::{Cacheable, Deduplicate, Diagnostic, HANDLE, ME, Table},
11 util::output::{done_processing, processing},
12};
13
14#[derive(Debug)]
16pub struct Handle {
17 pub rowid: i32,
19 pub id: String,
21 pub person_centric_id: Option<String>,
23}
24
25impl Table for Handle {
26 fn from_row(row: &Row) -> Result<Handle> {
27 Ok(Handle {
28 rowid: row.get("rowid")?,
29 id: row.get("id")?,
30 person_centric_id: row.get("person_centric_id").unwrap_or(None),
31 })
32 }
33
34 fn get(db: &Connection) -> Result<CachedStatement, TableError> {
35 Ok(db.prepare_cached(&format!("SELECT * from {HANDLE}"))?)
36 }
37
38 fn extract(handle: Result<Result<Self, Error>, Error>) -> Result<Self, TableError> {
39 match handle {
40 Ok(Ok(handle)) => Ok(handle),
41 Err(why) | Ok(Err(why)) => Err(TableError::QueryError(why)),
42 }
43 }
44}
45
46impl Cacheable for Handle {
47 type K = i32;
48 type V = String;
49 fn cache(db: &Connection) -> Result<HashMap<Self::K, Self::V>, TableError> {
64 let mut map = HashMap::new();
66 map.insert(0, ME.to_string());
68
69 let mut statement = Handle::get(db)?;
71
72 let handles = statement.query_map([], |row| Ok(Handle::from_row(row)))?;
74
75 for handle in handles {
77 let contact = Handle::extract(handle)?;
78 map.insert(contact.rowid, contact.id);
79 }
80
81 let dupe_contacts = Handle::get_person_id_map(db)?;
83 for contact in dupe_contacts {
84 let (id, new) = contact;
85 map.insert(id, new);
86 }
87
88 Ok(map)
90 }
91}
92
93impl Deduplicate for Handle {
94 type T = String;
95
96 fn dedupe(duplicated_data: &HashMap<i32, Self::T>) -> HashMap<i32, i32> {
116 let mut deduplicated_participants: HashMap<i32, i32> = HashMap::new();
117 let mut participant_to_unique_participant_id: HashMap<Self::T, i32> = HashMap::new();
118
119 let mut unique_participant_identifier = 0;
121
122 let mut sorted_dupes: Vec<(&i32, &Self::T)> = duplicated_data.iter().collect();
124 sorted_dupes.sort_by(|(a, _), (b, _)| a.cmp(b));
125
126 for (participant_id, participant) in sorted_dupes {
127 if let Some(id) = participant_to_unique_participant_id.get(participant) {
128 deduplicated_participants.insert(participant_id.to_owned(), id.to_owned());
129 } else {
130 participant_to_unique_participant_id
131 .insert(participant.to_owned(), unique_participant_identifier);
132 deduplicated_participants
133 .insert(participant_id.to_owned(), unique_participant_identifier);
134 unique_participant_identifier += 1;
135 }
136 }
137 deduplicated_participants
138 }
139}
140
141impl Diagnostic for Handle {
142 fn run_diagnostic(db: &Connection) -> Result<(), TableError> {
164 let query = concat!(
165 "SELECT COUNT(DISTINCT person_centric_id) ",
166 "FROM handle ",
167 "WHERE person_centric_id NOT NULL"
168 );
169
170 if let Ok(mut rows) = db.prepare(query) {
171 processing();
172
173 let count_dupes: Option<i32> = rows.query_row([], |r| r.get(0))?;
174
175 done_processing();
176
177 if let Some(dupes) = count_dupes {
178 if dupes > 0 {
179 println!("Handle diagnostic data:");
180 println!(" Contacts with more than one ID: {dupes}");
181 }
182 }
183 }
184
185 Ok(())
186 }
187}
188
189impl Handle {
190 fn get_person_id_map(db: &Connection) -> Result<HashMap<i32, String>, TableError> {
196 let mut person_to_id: HashMap<String, BTreeSet<String>> = HashMap::new();
197 let mut row_to_id: HashMap<i32, String> = HashMap::new();
198 let mut row_data: Vec<(String, i32, String)> = vec![];
199
200 let query = concat!(
202 "SELECT DISTINCT A.person_centric_id, A.rowid, A.id ",
203 "FROM handle A ",
204 "INNER JOIN handle B ON B.id = A.id ",
205 "WHERE A.person_centric_id NOT NULL ",
206 "ORDER BY A.person_centric_id",
207 );
208 let statement = db.prepare(query);
209
210 if let Ok(mut statement) = statement {
211 let contacts = statement.query_map([], |row| {
213 let person_centric_id: String = row.get(0)?;
214 let rowid: i32 = row.get(1)?;
215 let id: String = row.get(2)?;
216 Ok((person_centric_id, rowid, id))
217 })?;
218
219 for contact in contacts {
220 row_data.push(contact?);
221 }
222
223 for contact in &row_data {
225 let (person_centric_id, _, id) = contact;
226 if let Some(set) = person_to_id.get_mut(person_centric_id) {
227 set.insert(id.to_owned());
228 } else {
229 let mut set = BTreeSet::new();
230 set.insert(id.to_owned());
231 person_to_id.insert(person_centric_id.to_owned(), set);
232 }
233 }
234
235 for contact in &row_data {
237 let (person_centric_id, rowid, _) = contact;
238 let data_to_insert = match person_to_id.get_mut(person_centric_id) {
239 Some(person) => person.iter().cloned().collect::<Vec<String>>().join(" "),
240 None => panic!("Attempted to resolve contact with no person_centric_id!"),
241 };
242 row_to_id.insert(rowid.to_owned(), data_to_insert);
243 }
244 }
245
246 Ok(row_to_id)
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use crate::tables::{handle::Handle, table::Deduplicate};
253 use std::collections::{HashMap, HashSet};
254
255 #[test]
256 fn test_can_dedupe() {
257 let mut input: HashMap<i32, String> = HashMap::new();
258 input.insert(1, String::from("A")); input.insert(2, String::from("A")); input.insert(3, String::from("A")); input.insert(4, String::from("B")); input.insert(5, String::from("B")); input.insert(6, String::from("C")); let output = Handle::dedupe(&input);
266 let expected_deduped_ids: HashSet<i32> = output.values().copied().collect();
267 assert_eq!(expected_deduped_ids.len(), 3);
268 }
269
270 #[test]
271 fn test_same_values() {
273 let mut input_1: HashMap<i32, String> = HashMap::new();
274 input_1.insert(1, String::from("A"));
275 input_1.insert(2, String::from("A"));
276 input_1.insert(3, String::from("A"));
277 input_1.insert(4, String::from("B"));
278 input_1.insert(5, String::from("B"));
279 input_1.insert(6, String::from("C"));
280
281 let mut input_2: HashMap<i32, String> = HashMap::new();
282 input_2.insert(1, String::from("A"));
283 input_2.insert(2, String::from("A"));
284 input_2.insert(3, String::from("A"));
285 input_2.insert(4, String::from("B"));
286 input_2.insert(5, String::from("B"));
287 input_2.insert(6, String::from("C"));
288
289 let mut input_3: HashMap<i32, String> = HashMap::new();
290 input_3.insert(1, String::from("A"));
291 input_3.insert(2, String::from("A"));
292 input_3.insert(3, String::from("A"));
293 input_3.insert(4, String::from("B"));
294 input_3.insert(5, String::from("B"));
295 input_3.insert(6, String::from("C"));
296
297 let mut output_1 = Handle::dedupe(&input_1)
298 .into_iter()
299 .collect::<Vec<(i32, i32)>>();
300 let mut output_2 = Handle::dedupe(&input_2)
301 .into_iter()
302 .collect::<Vec<(i32, i32)>>();
303 let mut output_3 = Handle::dedupe(&input_3)
304 .into_iter()
305 .collect::<Vec<(i32, i32)>>();
306
307 output_1.sort_unstable();
308 output_2.sort_unstable();
309 output_3.sort_unstable();
310
311 assert_eq!(output_1, output_2);
312 assert_eq!(output_1, output_3);
313 assert_eq!(output_2, output_3);
314 }
315}