1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/*!
 This module represents common (but not all) columns in the `chat` table.
*/

use std::collections::HashMap;

use rusqlite::{Connection, Error, Result, Row, Statement};

use crate::{
    error::table::TableError,
    tables::table::{Cacheable, Table, CHAT},
};

/// Represents a single row in the `chat` table.
#[derive(Debug)]
pub struct Chat {
    pub rowid: i32,
    pub chat_identifier: String,
    pub service_name: String,
    pub display_name: Option<String>,
}

impl Table for Chat {
    fn from_row(row: &Row) -> Result<Chat> {
        Ok(Chat {
            rowid: row.get("rowid")?,
            chat_identifier: row.get("chat_identifier")?,
            service_name: row.get("service_name")?,
            display_name: row.get("display_name").unwrap_or(None),
        })
    }

    fn get(db: &Connection) -> Result<Statement, TableError> {
        db.prepare(&format!("SELECT * from {CHAT}"))
            .map_err(TableError::Chat)
    }

    fn extract(chat: Result<Result<Self, Error>, Error>) -> Result<Self, TableError> {
        match chat {
            Ok(chat) => match chat {
                Ok(ch) => Ok(ch),
                // TODO: When does this occur?
                Err(why) => Err(TableError::Chat(why)),
            },
            // TODO: When does this occur?
            Err(why) => Err(TableError::Chat(why)),
        }
    }
}

impl Cacheable for Chat {
    type K = i32;
    type V = Chat;
    /// Generate a hashmap containing each chatroom's ID pointing to the chatroom's metadata.
    ///
    /// These chatroom ID's contain duplicates and must be deduped later once we have all of
    /// the participants parsed out. On its own this data is not useful.
    ///
    /// # Example:
    ///
    /// ```
    /// use imessage_database::util::dirs::default_db_path;
    /// use imessage_database::tables::table::{Cacheable, get_connection};
    /// use imessage_database::tables::chat::Chat;
    ///
    /// let db_path = default_db_path();
    /// let conn = get_connection(&db_path).unwrap();
    /// let chatrooms = Chat::cache(&conn);
    /// ```
    fn cache(db: &Connection) -> Result<HashMap<Self::K, Self::V>, TableError> {
        let mut map = HashMap::new();

        let mut statement = Chat::get(db)?;

        let chats = statement
            .query_map([], |row| Ok(Chat::from_row(row)))
            .unwrap();

        for chat in chats {
            let result = Chat::extract(chat)?;
            map.insert(result.rowid, result);
        }
        Ok(map)
    }
}

impl Chat {
    pub fn name(&self) -> &str {
        match &self.display_name() {
            Some(name) => name,
            None => &self.chat_identifier,
        }
    }

    pub fn display_name(&self) -> Option<&str> {
        match &self.display_name {
            Some(name) => {
                if !name.is_empty() {
                    return Some(name.as_str());
                }
                None
            }
            None => None,
        }
    }
}