use rusqlite::Connection;
use crate::{
error::table::TableError,
tables::{
attachment::ATTACHMENT_COLUMNS,
diagnostic::{column_names, table_exists},
messages::columns::MESSAGE_COLUMNS,
table::{ATTACHMENT, MESSAGE, RECENTLY_DELETED},
},
};
#[derive(Clone, Debug)]
pub struct Capabilities {
message_columns: Vec<&'static str>,
attachment_columns: Vec<&'static str>,
pub filter_actions: bool,
pub recoverable_messages: bool,
pub replies: bool,
pub associated_message_guids: bool,
}
impl Capabilities {
pub fn determine(db: &Connection) -> Result<Self, TableError> {
let message_names = column_names(db, MESSAGE)?;
let attachment_names = column_names(db, ATTACHMENT)?;
Ok(Self {
message_columns: MESSAGE_COLUMNS
.into_iter()
.filter(|column| message_names.contains(*column))
.collect(),
attachment_columns: ATTACHMENT_COLUMNS
.into_iter()
.filter(|column| attachment_names.contains(*column))
.collect(),
filter_actions: message_names.contains("filter_action")
&& message_names.contains("filter_sub_action"),
recoverable_messages: table_exists(db, RECENTLY_DELETED)?,
replies: message_names.contains("thread_originator_guid"),
associated_message_guids: message_names.contains("associated_message_guid"),
})
}
#[must_use]
pub fn without_derived_features(&self) -> Self {
Self {
recoverable_messages: false,
replies: false,
filter_actions: false,
..self.clone()
}
}
pub fn message_columns(&self) -> &[&'static str] {
&self.message_columns
}
pub fn attachment_columns(&self) -> &[&'static str] {
&self.attachment_columns
}
}
#[cfg(test)]
mod tests {
use super::Capabilities;
use crate::test_support::schema_db;
#[test]
fn detects_present_features() {
let db = schema_db(true, true, true);
let capabilities = Capabilities::determine(&db).unwrap();
assert!(capabilities.filter_actions);
assert!(capabilities.recoverable_messages);
assert!(capabilities.replies);
assert!(capabilities.associated_message_guids);
assert_eq!(capabilities.message_columns().len(), 26);
}
#[test]
fn detects_absent_features() {
let db = schema_db(false, false, false);
let capabilities = Capabilities::determine(&db).unwrap();
assert!(!capabilities.filter_actions);
assert!(!capabilities.recoverable_messages);
assert!(!capabilities.replies);
assert!(
!capabilities
.message_columns()
.contains(&"thread_originator_guid")
);
}
#[test]
fn pre_tapback_schema_probes_no_associated_guids() {
let db = schema_db(false, false, false);
db.execute_batch("ALTER TABLE message DROP COLUMN associated_message_guid")
.unwrap();
assert!(
!Capabilities::determine(&db)
.unwrap()
.associated_message_guids
);
}
#[test]
fn attachment_columns_track_the_schema() {
let db = schema_db(false, false, false);
db.execute_batch(
"CREATE TABLE attachment (
ROWID INTEGER PRIMARY KEY,
guid TEXT,
filename TEXT,
uti TEXT,
mime_type TEXT,
transfer_name TEXT,
total_bytes INTEGER,
is_sticker INTEGER,
hide_attachment INTEGER
);",
)
.unwrap();
let capabilities = Capabilities::determine(&db).unwrap();
assert_eq!(capabilities.attachment_columns().len(), 9);
assert!(
!capabilities
.attachment_columns()
.contains(&"emoji_image_short_description")
);
}
}