1use std::fmt::{self};
2
3use crate::command::IRCCommand;
4use crate::error::{MessageError, MessageErrorDetails};
5
6#[derive(Debug)]
7pub struct Message {
8 pub source: Option<String>,
9 pub command: IRCCommand,
10 pub params: Vec<String>,
11}
12impl fmt::Display for Message {
13 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14 write!(f, "{}", String::from_utf8_lossy(self.serialize().as_ref()))
15 }
16}
17
18static CR: u8 = 13;
19static LF: u8 = 10;
20
21impl Message {
22 pub fn serialize(&self) -> Vec<u8> {
26 let mut ret_val: Vec<u8> = vec![];
27 if self.source.is_some() {
28 ret_val = [
29 ":".as_bytes().to_vec(),
30 self.source.as_ref().unwrap().as_bytes().to_vec(),
31 " ".as_bytes().to_vec(),
32 ]
33 .concat();
34 }
35
36 let mut params = self.params.join(" ");
37 if self.needs_colon() {
38 if self.params.len() > self.command.params_before_colon() {
39 let mut modified_params = self.params.clone();
40 modified_params[self.command.params_before_colon()] =
41 format!(":{}", modified_params[self.command.params_before_colon()]);
42 params = modified_params.join(" ");
43 }
44 }
45
46 ret_val = [
47 ret_val,
48 self.command.command_text().as_bytes().to_vec(),
49 " ".as_bytes().to_vec(),
50 params.into(),
51 ]
52 .concat();
53
54 ret_val.truncate(510);
55
56 ret_val = [ret_val, vec![CR], vec![LF]].concat();
57
58 ret_val
59 }
60 pub fn parse(
66 mut raw_input: Vec<u8>,
67 source_client: Option<String>,
68 ) -> Result<Self, MessageError> {
69 if raw_input.len() < 1 {
70 return Err(MessageError {
71 command: None,
72 detail: MessageErrorDetails::NoData,
73 });
74 }
75 if raw_input[raw_input.len() - 1] == LF {
76 raw_input.pop();
77 }
78 if raw_input[raw_input.len() - 1] == CR {
79 raw_input.pop();
80 }
81
82 raw_input = String::from_utf8_lossy(&raw_input)
83 .trim_end()
84 .trim_start()
85 .as_bytes()
86 .to_vec();
87
88 let split_message: Vec<Vec<u8>> = raw_input.into_iter().fold(Vec::new(), |mut acc, x| {
89 if x == 32 || acc.is_empty() {
90 acc.push(Vec::new());
91 }
92 if x != 32 {
93 acc.last_mut().unwrap().push(x);
94 }
95 acc
96 });
97
98 let mut source = source_client.clone();
99
100 let mut command_index = 0;
101 if split_message[0][0] == 58 {
102 command_index = 1;
103 let parsed_source = match Message::parse_prefix(split_message[0].clone()) {
104 Ok(x) => x,
105 Err(y) => return Err(y),
106 };
107 source = source_client.or_else(|| Some(parsed_source));
108 }
109
110 if split_message.len() < command_index + 1 {
111 return Err(MessageError {
112 command: None,
113 detail: MessageErrorDetails::NoCommand,
114 });
115 }
116
117 let irc_command = match Message::parse_command(split_message[command_index].clone()) {
118 Ok(x) => x,
119 Err(y) => return Err(y),
120 };
121
122 let params = match irc_command.parse_params(split_message[command_index + 1..].to_vec()) {
123 Ok(x) => x,
124 Err(y) => return Err(y),
125 };
126
127 Ok(Message {
128 source: source,
129 command: irc_command,
130 params: params
131 .iter()
132 .map(|p| String::from_utf8_lossy(p).into_owned())
133 .collect(),
134 })
135 }
136
137 fn parse_command(command_input: Vec<u8>) -> Result<IRCCommand, MessageError> {
138 let raw_command = match String::from_utf8(command_input) {
139 Ok(x) => x,
140 Err(_) => {
141 return Err(MessageError {
142 command: None,
143 detail: MessageErrorDetails::FailedCommandParse,
144 })
145 }
146 };
147
148 match IRCCommand::text_command(raw_command.as_str()) {
149 IRCCommand::NONE => {
150 return Err(MessageError {
151 command: None,
152 detail: MessageErrorDetails::InvalidCommand,
153 })
154 }
155 x => Ok(x),
156 }
157 }
158
159 fn parse_prefix(prefix_input: Vec<u8>) -> Result<String, MessageError> {
160 if prefix_input.len() < 2 {
161 return Err(MessageError {
162 command: None,
163 detail: MessageErrorDetails::NoClient,
164 });
165 }
166
167 if prefix_input[0] == 58 {
168 match String::from_utf8(prefix_input.clone().split_off(1)) {
169 Ok(x) => return Ok(x),
170 Err(_) => {
171 return Err(MessageError {
172 command: None,
173 detail: MessageErrorDetails::FailedPrefixParse,
174 });
175 }
176 }
177 } else {
178 return Err(MessageError {
179 command: None,
180 detail: MessageErrorDetails::NoPrefix,
181 });
182 }
183 }
184
185 fn needs_colon(&self) -> bool {
186 match self.command {
187 IRCCommand::USER => true,
188 IRCCommand::PRIVMSG => true,
189 IRCCommand::TOPIC => true,
190 _ => false,
191 }
192 }
193}
194
195#[test]
196fn test_valid_command() {
197 let message = "NAMES".as_bytes().to_vec();
198 let command = Message::parse_command(message);
199 assert_eq!(command, Ok(IRCCommand::NAMES));
200}
201
202#[test]
203fn test_invalid_command() {
204 let message = "NAMES2".as_bytes().to_vec();
205 let command = Message::parse_command(message);
206 assert!(command.is_err());
207}
208
209#[test]
210fn test_blank_command() {
211 let message = "".as_bytes().to_vec();
212 let command = Message::parse_command(message);
213 assert!(command.is_err());
214}
215
216#[test]
217fn test_valid_source() {
218 let message = ":TestClient".as_bytes().to_vec();
219 let source = Message::parse_prefix(message);
220 let source_string = match source {
221 Ok(x) => x,
222 Err(_) => "None".to_string(),
223 };
224 assert_eq!(source_string, "TestClient");
225}
226#[test]
227fn test_invalid_source() {
228 let message = ":".as_bytes().to_vec();
229 let source = Message::parse_prefix(message);
230 let source_string = match source {
231 Ok(x) => x,
232 Err(y) => y.detail.error_text().to_string(),
233 };
234 assert_eq!(source_string, "No client identifier");
235}
236#[test]
237fn test_empty_source() {
238 let message = Vec::new();
239 let source = Message::parse_prefix(message);
240 let source_string = match source {
241 Ok(x) => x,
242 Err(y) => y.detail.error_text().to_string(),
243 };
244 assert_eq!(source_string, "No client identifier");
245}
246
247#[test]
248fn test_format_source() {
249 let message = "TestClient".as_bytes().to_vec();
250 let source = Message::parse_prefix(message);
251 let source_string = match source {
252 Ok(x) => x,
253 Err(y) => y.detail.error_text().to_string(),
254 };
255 assert_eq!(source_string, "Missing : character");
256}
257
258#[test]
259fn test_serialize() {
260 let message = Message {
261 source: Some("Anon".to_string()),
262 command: IRCCommand::NICK,
263 params: vec!["nonA".to_string()],
264 };
265 let output = message.serialize();
266 println!("{:?}", output);
267 assert_eq!(output.len(), 17);
268 assert_eq!(output[output.len() - 3], 65);
269
270 let message = Message {
271 source: None,
272 command: IRCCommand::NICK,
273 params: vec!["nonA".to_string()],
274 };
275 let output = message.serialize();
276 println!("{:?}", output);
277 assert_eq!(output.len(), 11);
278 assert_eq!(output[output.len() - 3], 65);
279}
280
281#[test]
282fn test_user() {
283 let message = Message {
284 source: None,
285 command: IRCCommand::USER,
286 params: vec![
287 "Anon".to_string(),
288 "0".to_string(),
289 "*".to_string(),
290 "Anon".to_string(),
291 ],
292 };
293 println!("{:?}", message);
294 let output = message.serialize();
295 println!("{:?}", output);
296 assert_eq!(output.len(), 21);
297 assert_eq!(output[14], 58);
298}
299
300#[test]
301fn test_whitespace_suffix() {
302 let data = " :Anon NICK Anon2 ".as_bytes().to_vec();
303 println!("{:?}", data);
304 let message = Message::parse(data, None).unwrap();
305 println!("{:?}", message);
306 assert_eq!(message.serialize().len(), 18)
307}
308
309#[test]
310fn test_privmsg() {
311 let message = Message {
312 source: None,
313 command: IRCCommand::PRIVMSG,
314 params: vec!["Dave".to_string(), "Hello Dave".to_string()],
315 };
316 println!("{:?}", message.serialize());
317 println!("{}", String::from_utf8_lossy(&message.serialize()));
318 assert_eq!(message.serialize().len(), 26);
319}
320
321#[test]
322fn test_topic() {
323 let message = Message {
324 source: None,
325 command: IRCCommand::TOPIC,
326 params: vec!["#Chat".to_string(), "Hello Dave".to_string()],
327 };
328 println!("{:?}", message.serialize());
329 println!("{}", String::from_utf8_lossy(&message.serialize()));
330 assert_eq!(message.serialize().len(), 25);
331
332 let message = Message {
333 source: None,
334 command: IRCCommand::TOPIC,
335 params: vec!["#Chat".to_string(), "".to_string()],
336 };
337 println!("{:?}", message.serialize());
338 println!("{}", String::from_utf8_lossy(&message.serialize()));
339 assert_eq!(message.serialize().len(), 15);
340
341 let message = Message {
342 source: None,
343 command: IRCCommand::TOPIC,
344 params: vec!["#Chat".to_string()],
345 };
346 println!("{:?}", message.serialize());
347 println!("{}", String::from_utf8_lossy(&message.serialize()));
348 assert_eq!(message.serialize().len(), 13);
349}