imessage_database/tables/
chat.rs

1/*!
2 This module represents common (but not all) columns in the `chat` table.
3*/
4
5use std::collections::HashMap;
6
7use rusqlite::{Connection, Error, Result, Row, Statement};
8
9use crate::{
10    error::table::TableError,
11    tables::{
12        messages::models::Service,
13        table::{Cacheable, Table, CHAT},
14    },
15};
16
17/// Represents a single row in the `chat` table.
18#[derive(Debug)]
19pub struct Chat {
20    pub rowid: i32,
21    pub chat_identifier: String,
22    /// The service the chat used, i.e. iMessage, SMS, IRC, etc.
23    pub service_name: Option<String>,
24    /// Optional custom name created created for the chat
25    pub display_name: Option<String>,
26}
27
28impl Table for Chat {
29    fn from_row(row: &Row) -> Result<Chat> {
30        Ok(Chat {
31            rowid: row.get("rowid")?,
32            chat_identifier: row.get("chat_identifier")?,
33            service_name: row.get("service_name")?,
34            display_name: row.get("display_name").unwrap_or(None),
35        })
36    }
37
38    fn get(db: &Connection) -> Result<Statement, TableError> {
39        db.prepare(&format!("SELECT * from {CHAT}"))
40            .map_err(TableError::Chat)
41    }
42
43    fn extract(chat: Result<Result<Self, Error>, Error>) -> Result<Self, TableError> {
44        match chat {
45            Ok(Ok(chat)) => Ok(chat),
46            Err(why) | Ok(Err(why)) => Err(TableError::Chat(why)),
47        }
48    }
49}
50
51impl Cacheable for Chat {
52    type K = i32;
53    type V = Chat;
54    /// Generate a hashmap containing each chatroom's ID pointing to the chatroom's metadata.
55    ///
56    /// These chatroom ID's contain duplicates and must be deduped later once we have all of
57    /// the participants parsed out. On its own this data is not useful.
58    ///
59    /// # Example:
60    ///
61    /// ```
62    /// use imessage_database::util::dirs::default_db_path;
63    /// use imessage_database::tables::table::{Cacheable, get_connection};
64    /// use imessage_database::tables::chat::Chat;
65    ///
66    /// let db_path = default_db_path();
67    /// let conn = get_connection(&db_path).unwrap();
68    /// let chatrooms = Chat::cache(&conn);
69    /// ```
70    fn cache(db: &Connection) -> Result<HashMap<Self::K, Self::V>, TableError> {
71        let mut map = HashMap::new();
72
73        let mut statement = Chat::get(db)?;
74
75        let chats = statement
76            .query_map([], |row| Ok(Chat::from_row(row)))
77            .map_err(TableError::Chat)?;
78
79        for chat in chats {
80            let result = Chat::extract(chat)?;
81            map.insert(result.rowid, result);
82        }
83        Ok(map)
84    }
85}
86
87impl Chat {
88    /// Generate a name for a chat, falling back to the default if a custom one is not set
89    pub fn name(&self) -> &str {
90        match self.display_name() {
91            Some(name) => name,
92            None => &self.chat_identifier,
93        }
94    }
95
96    /// Get the current display name for the chat, if it exists.
97    pub fn display_name(&self) -> Option<&str> {
98        match &self.display_name {
99            Some(name) => {
100                if !name.is_empty() {
101                    return Some(name.as_str());
102                }
103                None
104            }
105            None => None,
106        }
107    }
108
109    /// Get the service used by the chat, i.e. iMessage, SMS, IRC, etc.
110    pub fn service(&self) -> Service {
111        Service::from(self.service_name.as_deref())
112    }
113}