use std::fmt::{Display, Formatter, Result};
use crabstep::{PropertyIterator, deserializer::iter::Property};
use crate::{
message_types::text_effects::TextEffect,
tables::messages::message::Message,
util::typedstream::{as_float, as_nsstring},
};
#[derive(Debug, PartialEq, Clone)]
pub enum BubbleComponent {
Text(Vec<TextAttributes>),
Attachment(AttachmentMeta),
App,
Retracted,
}
#[derive(Debug, PartialEq, Eq)]
pub enum Service<'a> {
#[allow(non_camel_case_types)]
iMessage,
SMS,
RCS,
Satellite,
Other(&'a str),
Unknown,
}
impl<'a> Service<'a> {
#[must_use]
pub fn from_name(service: Option<&'a str>) -> Self {
if let Some(service_name) = service {
return match service_name.trim() {
"iMessage" => Service::iMessage,
"iMessageLite" => Service::Satellite,
"SMS" => Service::SMS,
"rcs" | "RCS" => Service::RCS,
service_name => Service::Other(service_name),
};
}
Service::Unknown
}
}
impl Display for Service<'_> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result {
match self {
Service::iMessage => write!(fmt, "iMessage"),
Service::SMS => write!(fmt, "SMS"),
Service::RCS => write!(fmt, "RCS"),
Service::Satellite => write!(fmt, "Satellite"),
Service::Other(other) => write!(fmt, "{other}"),
Service::Unknown => write!(fmt, "Unknown"),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct TextAttributes {
pub start: usize,
pub end: usize,
pub effects: Vec<TextEffect>,
}
impl TextAttributes {
#[must_use]
pub fn new(start: usize, end: usize, effects: Vec<TextEffect>) -> Self {
Self {
start,
end,
effects,
}
}
}
#[derive(Debug, PartialEq, Default, Clone)]
pub struct AttachmentMeta {
pub guid: Option<String>,
pub transcription: Option<String>,
pub height: Option<f64>,
pub width: Option<f64>,
pub name: Option<String>,
}
impl AttachmentMeta {
#[must_use]
pub(crate) fn from_components<'a>(
first_key: &str,
components: &'a mut PropertyIterator<'a, 'a>,
) -> Self {
let mut meta = Self::default();
if let Some(mut prop) = components.next() {
meta.set_from_key_value(first_key, &mut prop);
}
while let Some(mut key) = components.next() {
if let Some(key_name) = as_nsstring(&mut key)
&& let Some(mut value) = components.next()
{
meta.set_from_key_value(key_name, &mut value);
}
}
meta
}
fn set_from_key_value<'a>(&'a mut self, key: &'a str, value: &'a mut Property<'a, 'a>) {
match key {
"__kIMFileTransferGUIDAttributeName" => {
self.guid = as_nsstring(value).map(String::from);
}
"IMAudioTranscription" => self.transcription = as_nsstring(value).map(String::from),
"__kIMInlineMediaHeightAttributeName" => self.height = as_float(value),
"__kIMInlineMediaWidthAttributeName" => self.width = as_float(value),
"__kIMFilenameAttributeName" => self.name = as_nsstring(value).map(String::from),
_ => {}
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum GroupAction<'a> {
ParticipantAdded(i32),
ParticipantRemoved(i32),
NameChange(&'a str),
ParticipantLeft,
GroupIconChanged,
GroupIconRemoved,
ChatBackgroundChanged,
ChatBackgroundRemoved,
PhoneNumberChanged(i32),
}
impl<'a> GroupAction<'a> {
#[must_use]
pub(crate) fn from_message(message: &'a Message) -> Option<Self> {
match (
message.item_type,
message.group_action_type,
message.other_handle,
&message.group_title,
) {
(1, 0, Some(who), _) if message.handle_id == Some(who) => {
Some(Self::PhoneNumberChanged(who))
}
(1, 0, Some(who), _) => Some(Self::ParticipantAdded(who)),
(1, 1, Some(who), _) => Some(Self::ParticipantRemoved(who)),
(2, _, _, Some(name)) => Some(Self::NameChange(name)),
(3, 0, _, _) => Some(Self::ParticipantLeft),
(3, 1, _, _) => Some(Self::GroupIconChanged),
(3, 2, _, _) => Some(Self::GroupIconRemoved),
(3, 4, _, _) => Some(Self::ChatBackgroundChanged),
(3, 6, _, _) => Some(Self::ChatBackgroundRemoved),
_ => None,
}
}
}