alfred_core/
message.rs

1use std::{fmt, str::FromStr, collections::{HashMap, hash_map::RandomState}};
2use std::collections::LinkedList;
3use itertools::Itertools;
4use serde_derive::Deserialize;
5use crate::error::MessageCompressionError;
6
7const MESSAGE_SEPARATOR : char = 0x0 as char;
8const VEC_SEPARATOR : char = 0xFF as char;
9
10#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Default)]
11pub enum MessageType {
12    #[default]
13    Unknown,
14    Text,
15    Audio,
16    Photo,
17    ModuleInfo
18}
19
20impl FromStr for MessageType {
21    type Err = String;
22
23    fn from_str(s: &str) -> Result<Self, Self::Err> {
24        Ok(match s {
25            "Unknown" => Self::Unknown,
26            "Text" => Self::Text,
27            "Audio" => Self::Audio,
28            "Photo" => Self::Photo,
29            "ModuleInfo" => Self::ModuleInfo,
30            _ => Err(format!("{s} is not a valid MessageType."))?
31        })
32    }
33}
34
35impl fmt::Display for MessageType {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        write!(f, "{}", match self {
38            Self::Unknown => "Unknown",
39            Self::Text => "Text",
40            Self::Audio => "Audio",
41            Self::Photo => "Photo",
42            Self::ModuleInfo => "ModuleInfo"
43        })
44    }
45}
46
47#[derive(Debug, PartialEq, Eq, Default)]
48pub struct Message {
49    pub text: String,
50    pub starting_module: String,
51    pub request_topic: String,
52    pub response_topics: LinkedList<String>,
53    pub sender: String,
54    pub message_type: MessageType,
55    pub params: HashMap<String, String, RandomState>,
56}
57
58impl Clone for Message {
59    fn clone(&self) -> Self {
60        Self {
61            text: self.text.clone(),
62            starting_module: self.starting_module.clone(),
63            request_topic: self.request_topic.clone(),
64            response_topics: self.response_topics.clone(),
65            sender: self.sender.clone(),
66            message_type: self.message_type.clone(),
67            params: self.params.clone(),
68        }
69    }
70}
71
72impl Message {
73
74    pub fn empty() -> Self {
75        Self::default()
76    }
77
78    pub fn compress(&self) -> String {
79        let params = self.params.iter()
80            .map(|(k, v)| format!("{k}{MESSAGE_SEPARATOR}{v}"));
81
82        [
83            self.text.clone(),
84            self.starting_module.clone(),
85            self.request_topic.clone(),
86            Itertools::intersperse(
87                self.response_topics.iter()
88                    .cloned(), VEC_SEPARATOR.to_string()
89            ).collect(),
90            self.sender.clone(),
91            self.message_type.to_string()
92        ]
93            .into_iter()
94            .chain(params)
95            .collect::<Vec<String>>()
96            .join(MESSAGE_SEPARATOR.to_string().as_str())
97    }
98
99    /// decompress
100    /// # Examples
101    /// ```rust
102    /// use std::collections::{HashMap, LinkedList, VecDeque};
103    /// use std::io::Lines;
104    /// use alfred_core::message::{Message, MessageType};
105    ///
106    /// const MESSAGE_SEPARATOR : char = 0x0 as char;
107    ///
108    /// let decompressed = Message::decompress(format!("text{MESSAGE_SEPARATOR}module{MESSAGE_SEPARATOR}user.request{MESSAGE_SEPARATOR}module.response{MESSAGE_SEPARATOR}0123{MESSAGE_SEPARATOR}Text{MESSAGE_SEPARATOR}par1{MESSAGE_SEPARATOR}val1{MESSAGE_SEPARATOR}par2{MESSAGE_SEPARATOR}val2{MESSAGE_SEPARATOR}").as_str());
109    /// assert!(decompressed.is_ok());
110    /// let mut params = HashMap::new();
111    /// params.insert(String::from("par1"), String::from("val1"));
112    /// params.insert(String::from("par2"), String::from("val2"));
113    /// let message: Message = Message {
114    ///     text: String::from("text"),
115    ///     starting_module: String::from("module"),
116    ///     request_topic: String::from("user.request"),
117    ///     response_topics: LinkedList::from([String::from("module.response")]),
118    ///     sender: String::from("0123"),
119    ///     message_type: MessageType::Text,
120    ///     params
121    /// };
122    /// assert_eq!(message, decompressed.unwrap());
123    /// ```
124    pub fn decompress(comp_str: &str) -> Result<Self, MessageCompressionError> {
125        let binding = comp_str.to_string();
126        let ser_msg = binding.split(MESSAGE_SEPARATOR).collect::<Vec<&str>>();
127
128        let get_field = |index :usize, default: String| {
129            ser_msg
130                .get(index).copied()
131                .map_or(default, ToString::to_string)
132        };
133
134        let get_vec = |index :usize, field_name: &str| {
135            Ok(ser_msg
136                .get(index)
137                .ok_or_else(|| MessageCompressionError::FieldNotFound(field_name.to_string()))?
138                .split(VEC_SEPARATOR)
139                .map(ToString::to_string)
140                .collect::<LinkedList<String>>())
141        };
142
143        let get_params = |start: usize| {
144            let mut param_name = String::new();
145            let mut params: HashMap<String, String> = HashMap::new();
146            for (i, param) in ser_msg.iter().enumerate().skip(start) {
147                if i % 2 == 0 {
148                    param_name = (*param).to_string();
149                } else {
150                    params.insert(param_name.clone(), (*param).to_string());
151                }
152            }
153            params
154        };
155
156        Ok(Self{
157            text: get_field(0, String::new()),
158            starting_module: get_field(1, String::new()),
159            request_topic: get_field(2, String::new()),
160            response_topics: get_vec(3,"response_topics")?,
161            sender: get_field(4, String::new()),
162            message_type: get_field(5, MessageType::default().to_string()).parse::<MessageType>()
163                .or(Err(MessageCompressionError::DecompressionError()))?,
164            params: get_params(6)
165        })
166    }
167
168    pub fn reply(&self, text: String, message_type: MessageType) -> Result<(String, Self), crate::error::Error> {
169        let mut response_topics = self.response_topics.clone();
170        let topic = response_topics.pop_front().ok_or(crate::error::Error::ReplyError)?;
171        let response = Self {
172            text,
173            starting_module: self.starting_module.clone(),
174            request_topic: self.request_topic.clone(),
175            response_topics,
176            sender: self.sender.clone(),
177            message_type,
178            params: HashMap::default(),
179        };
180        Ok((topic, response))
181    }
182
183}
184
185impl fmt::Display for Message {
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187        write!(f, "{}", self.compress())
188    }
189}