irc_command_micro/
command.rs

1use crate::error::{MessageError, MessageErrorDetails::*};
2
3#[derive(Debug, PartialEq, Clone)]
4#[allow(non_camel_case_types)]
5pub enum IRCCommand {
6    NONE,
7    NAMES,
8    PRIVMSG,
9    NOTICE,
10    NICK,
11    USER,
12    QUIT,
13    MOTD,
14    LUSERS,
15    WHOIS,
16    TOPIC,
17    RPL_WELCOME,
18    RPL_YOURHOST,
19    RPL_CREATED,
20    RPL_MYINFO,
21    RPL_NAMREPLY,
22    RPL_ENDOFNAMES,
23    RPL_LUSERCLIENT,
24    RPL_LUSEROP,
25    RPL_LUSERUNKNOWN,
26    RPL_LUSERCHANNELS,
27    RPL_LUSERME,
28    RPL_WHOISUSER,
29    RPL_WHOISSERVER,
30    RPL_ENDOFWHOIS,
31    PING,
32    PONG,
33    ERR_NONICKNAMEGIVEN,
34    ERR_NICKNAMEINUSE,
35    ERR_ALREADYREGISTRED,
36    ERR_NEEDMOREPARAMS,
37    ERR_NOTREGISTERED,
38    ERR_UNKNOWNCOMMAND,
39    ERR_NORECIPIENT,
40    ERR_NOTEXTTOSEND,
41    ERR_NOSUCHNICK,
42    ERR_NOMOTD,
43}
44
45impl IRCCommand {
46    /// Returns a reference to the command text of this [`IRCCommand`].
47    pub fn command_text(&self) -> &str {
48        match *self {
49            IRCCommand::NONE => "NONE",
50            IRCCommand::NAMES => "NAMES",
51            IRCCommand::PRIVMSG => "PRIVMSG",
52            IRCCommand::NICK => "NICK",
53            IRCCommand::RPL_NAMREPLY => "353",
54            IRCCommand::RPL_ENDOFNAMES => "366",
55            IRCCommand::RPL_WELCOME => "001",
56            IRCCommand::PING => "PING",
57            IRCCommand::PONG => "PONG",
58            IRCCommand::USER => "USER",
59            IRCCommand::QUIT => "QUIT",
60            IRCCommand::NOTICE => "NOTICE",
61            IRCCommand::MOTD => "MOTD",
62            IRCCommand::LUSERS => "LUSERS",
63            IRCCommand::WHOIS => "WHOIS",
64            IRCCommand::RPL_YOURHOST => "002",
65            IRCCommand::RPL_CREATED => "003",
66            IRCCommand::RPL_MYINFO => "004",
67            IRCCommand::RPL_LUSERCLIENT => "251",
68            IRCCommand::RPL_LUSEROP => "252",
69            IRCCommand::RPL_LUSERUNKNOWN => "253",
70            IRCCommand::RPL_LUSERCHANNELS => "254",
71            IRCCommand::RPL_LUSERME => "255",
72            IRCCommand::RPL_WHOISUSER => "311",
73            IRCCommand::RPL_WHOISSERVER => "312",
74            IRCCommand::RPL_ENDOFWHOIS => "318",
75            IRCCommand::ERR_NONICKNAMEGIVEN => "431",
76            IRCCommand::ERR_NICKNAMEINUSE => "433",
77            IRCCommand::ERR_ALREADYREGISTRED => "462",
78            IRCCommand::ERR_NEEDMOREPARAMS => "461",
79            IRCCommand::ERR_NOTREGISTERED => "451",
80            IRCCommand::ERR_UNKNOWNCOMMAND => "421",
81            IRCCommand::ERR_NORECIPIENT => "411",
82            IRCCommand::ERR_NOTEXTTOSEND => "412",
83            IRCCommand::ERR_NOSUCHNICK => "401",
84            IRCCommand::ERR_NOMOTD => "422",
85            IRCCommand::TOPIC => "TOPIC",
86        }
87    }
88
89    pub fn text_command(text: &str) -> IRCCommand {
90        match text {
91            "NAMES" => return IRCCommand::NAMES,
92            "NICK" => return IRCCommand::NICK,
93            "PRIVMSG" => return IRCCommand::PRIVMSG,
94            "001" => return IRCCommand::RPL_WELCOME,
95            "353" => return IRCCommand::RPL_NAMREPLY,
96            "366" => return IRCCommand::RPL_ENDOFNAMES,
97            "PING" => return IRCCommand::PING,
98            "PONG" => return IRCCommand::PONG,
99            "USER" => return IRCCommand::USER,
100            "QUIT" => return IRCCommand::QUIT,
101            "NOTICE" => return IRCCommand::NOTICE,
102            "MOTD" => return IRCCommand::MOTD,
103            "LUSERS" => return IRCCommand::LUSERS,
104            "WHOIS" => return IRCCommand::WHOIS,
105            "TOPIC" => return IRCCommand::TOPIC,
106            "002" => return IRCCommand::RPL_YOURHOST,
107            "003" => return IRCCommand::RPL_CREATED,
108            "004" => return IRCCommand::RPL_MYINFO,
109            "251" => return IRCCommand::RPL_LUSERCLIENT,
110            "252" => return IRCCommand::RPL_LUSEROP,
111            "253" => return IRCCommand::RPL_LUSERUNKNOWN,
112            "254" => return IRCCommand::RPL_LUSERCHANNELS,
113            "255" => return IRCCommand::RPL_LUSERME,
114            "311" => return IRCCommand::RPL_WHOISUSER,
115            "312" => return IRCCommand::RPL_WHOISSERVER,
116            "318" => return IRCCommand::RPL_ENDOFWHOIS,
117            "431" => return IRCCommand::ERR_NONICKNAMEGIVEN,
118            "433" => return IRCCommand::ERR_NICKNAMEINUSE,
119            "462" => return IRCCommand::ERR_ALREADYREGISTRED,
120            "461" => return IRCCommand::ERR_NEEDMOREPARAMS,
121            "451" => return IRCCommand::ERR_NOTREGISTERED,
122            "421" => return IRCCommand::ERR_UNKNOWNCOMMAND,
123            "411" => return IRCCommand::ERR_NORECIPIENT,
124            "412" => return IRCCommand::ERR_NOTEXTTOSEND,
125            "401" => return IRCCommand::ERR_NOSUCHNICK,
126            "422" => return IRCCommand::ERR_NOMOTD,
127            _ => return IRCCommand::NONE,
128        }
129    }
130
131    pub fn params_before_colon(&self) -> usize {
132        match *self {
133            IRCCommand::PRIVMSG => 1,
134            IRCCommand::USER => 3,
135            IRCCommand::TOPIC => 1,
136            _ => 0,
137        }
138    }
139
140    /// From a vector of u8 vectors, each entry representing the space (32) seperated values following the IRCCommand, will return a similar list of parameters. If a : (58) is encountered at the start of an entry all remaining values are concatenated with a space (32)
141    ///
142    /// # Errors
143    ///
144    /// This function will return an error if .
145    pub fn parse_params(&self, raw_input: Vec<Vec<u8>>) -> Result<Vec<Vec<u8>>, MessageError> {
146        let mut return_vec: Vec<Vec<u8>> = Vec::new();
147        match *self {
148            IRCCommand::NONE => Ok(Vec::new()),
149            IRCCommand::NAMES => Ok(Vec::new()),
150            IRCCommand::PRIVMSG => self.number_and_presence(&raw_input, 2),
151            IRCCommand::NICK => self.number_and_presence(&raw_input, 1),
152            IRCCommand::RPL_NAMREPLY => self.number_and_presence(&raw_input, 2),
153            IRCCommand::RPL_ENDOFNAMES => self.number_and_presence(&raw_input, 2),
154            IRCCommand::RPL_WELCOME => self.number_and_presence(&raw_input, 2),
155            IRCCommand::PING => self.number_and_presence(&raw_input, 1),
156            IRCCommand::PONG => self.number_and_presence(&raw_input, 1),
157            IRCCommand::USER => self.number_and_presence(&raw_input, 4),
158            IRCCommand::QUIT => {
159                IRCCommand::parse_irc_params(&raw_input, &mut return_vec);
160                Ok(return_vec)
161            }
162            IRCCommand::NOTICE => self.number_and_presence(&raw_input, 2),
163            IRCCommand::MOTD => {
164                IRCCommand::parse_irc_params(&raw_input, &mut return_vec);
165                Ok(return_vec)
166            }
167            IRCCommand::LUSERS => {
168                IRCCommand::parse_irc_params(&raw_input, &mut return_vec);
169                Ok(return_vec)
170            }
171            IRCCommand::WHOIS => self.number_and_presence(&raw_input, 1),
172            IRCCommand::RPL_YOURHOST => self.number_and_presence(&raw_input, 2),
173            IRCCommand::RPL_CREATED => self.number_and_presence(&raw_input, 2),
174            IRCCommand::RPL_MYINFO => self.number_and_presence(&raw_input, 2),
175            IRCCommand::RPL_LUSERCLIENT => self.number_and_presence(&raw_input, 2),
176            IRCCommand::RPL_LUSEROP => self.number_and_presence(&raw_input, 3),
177            IRCCommand::RPL_LUSERUNKNOWN => self.number_and_presence(&raw_input, 3),
178            IRCCommand::RPL_LUSERCHANNELS => self.number_and_presence(&raw_input, 3),
179            IRCCommand::RPL_LUSERME => self.number_and_presence(&raw_input, 1),
180            IRCCommand::RPL_WHOISUSER => self.number_and_presence(&raw_input, 6),
181            IRCCommand::RPL_WHOISSERVER => self.number_and_presence(&raw_input, 4),
182            IRCCommand::RPL_ENDOFWHOIS => self.number_and_presence(&raw_input, 3),
183            IRCCommand::ERR_NONICKNAMEGIVEN => self.number_and_presence(&raw_input, 1),
184            IRCCommand::ERR_NICKNAMEINUSE => self.number_and_presence(&raw_input, 2),
185            IRCCommand::ERR_ALREADYREGISTRED => self.number_and_presence(&raw_input, 2),
186            IRCCommand::ERR_NEEDMOREPARAMS => self.number_and_presence(&raw_input, 3),
187            IRCCommand::ERR_NOTREGISTERED => self.number_and_presence(&raw_input, 1),
188            IRCCommand::ERR_UNKNOWNCOMMAND => self.number_and_presence(&raw_input, 3),
189            IRCCommand::ERR_NORECIPIENT => self.number_and_presence(&raw_input, 2),
190            IRCCommand::ERR_NOTEXTTOSEND => self.number_and_presence(&raw_input, 2),
191            IRCCommand::ERR_NOSUCHNICK => self.number_and_presence(&raw_input, 3),
192            IRCCommand::ERR_NOMOTD => self.number_and_presence(&raw_input, 2),
193            IRCCommand::TOPIC => self.parse_topic(&raw_input),
194        }
195    }
196
197    fn parse_topic(&self, params: &Vec<Vec<u8>>) -> Result<Vec<Vec<u8>>, MessageError> {
198        if params.len() < 1 {
199            return Err(MessageError {
200                command: Some(self.clone()),
201                detail: NotEnoughParams,
202            });
203        }
204        let mut return_vec: Vec<Vec<u8>> = Vec::new();
205        IRCCommand::parse_irc_params(&params, &mut return_vec);
206        Ok(return_vec)
207    }
208
209    fn number_and_presence(
210        &self,
211        params: &Vec<Vec<u8>>,
212        number: usize,
213    ) -> Result<Vec<Vec<u8>>, MessageError> {
214        let mut return_vec: Vec<Vec<u8>> = Vec::new();
215        if !IRCCommand::validate_param_presence(&params) {
216            return Err(MessageError {
217                detail: BlankParams,
218                command: Some(self.clone()),
219            });
220        }
221        if params.len() < number {
222            return Err(MessageError {
223                detail: NotEnoughParams,
224                command: Some(self.clone()),
225            });
226        }
227        IRCCommand::parse_irc_params(&params, &mut return_vec);
228        Ok(return_vec)
229    }
230
231    fn validate_param_presence(params: &Vec<Vec<u8>>) -> bool {
232        for p in params {
233            if p.len() < 1 {
234                return false;
235            }
236        }
237        true
238    }
239
240    fn parse_irc_params(raw_input: &Vec<Vec<u8>>, return_vec: &mut Vec<Vec<u8>>) {
241        for (i, r) in raw_input.into_iter().enumerate() {
242            if r[0] != 58 {
243                return_vec.push(r.to_vec());
244            } else {
245                let rest = raw_input.clone().split_off(i);
246                let mut message: Vec<u8> = Vec::new();
247                for (j, s) in rest.into_iter().enumerate() {
248                    if j == 0 {
249                        message = [message, s[1..].to_vec()].concat();
250                    } else {
251                        message = [message, [[32].to_vec(), s].concat()].concat();
252                    }
253                }
254                return_vec.push(message);
255                return;
256            }
257        }
258    }
259}
260
261#[test]
262fn test_no_params() {
263    let command_params = vec!["".as_bytes().to_vec()];
264    let res = IRCCommand::NAMES.parse_params(command_params);
265    assert!(res.is_ok());
266    assert_eq!(res.ok().unwrap().len(), 0);
267    let command_params = vec!["abc".as_bytes().to_vec()];
268    let res = IRCCommand::NAMES.parse_params(command_params);
269    assert!(res.is_ok());
270    assert_eq!(res.ok().unwrap().len(), 0);
271    let command_params = vec!["abc :Hello".as_bytes().to_vec()];
272    let res = IRCCommand::NAMES.parse_params(command_params);
273    assert!(res.is_ok());
274    assert_eq!(res.ok().unwrap().len(), 0);
275}
276
277#[test]
278fn test_one_param() {
279    let command_params = vec!["".as_bytes().to_vec()];
280    let res = IRCCommand::NICK.parse_params(command_params);
281    assert!(res.is_err());
282    assert_eq!(res.err().unwrap().detail, BlankParams);
283    let command_params = vec!["Anon".as_bytes().to_vec()];
284    let res = IRCCommand::NICK.parse_params(command_params);
285    assert!(res.is_ok());
286    assert_eq!(res.ok().unwrap()[0][0], 65);
287    let command_params = vec!["Anon".as_bytes().to_vec(), "B".as_bytes().to_vec()];
288    let res = IRCCommand::NICK.parse_params(command_params);
289    assert!(res.is_ok());
290    assert_eq!(res.ok().unwrap().len(), 2);
291}
292
293#[test]
294fn test_message_param() {
295    let command_params = vec!["".as_bytes().to_vec()];
296    let res = IRCCommand::PRIVMSG.parse_params(command_params);
297    assert!(res.is_err());
298    assert_eq!(res.err().unwrap().detail, BlankParams);
299    let command_params = vec!["Anon".as_bytes().to_vec()];
300    let res = IRCCommand::PRIVMSG.parse_params(command_params);
301    assert!(res.is_err());
302    assert_eq!(res.err().unwrap().detail, NotEnoughParams);
303    let command_params = vec![
304        "Anon".as_bytes().to_vec(),
305        ":Hello".as_bytes().to_vec(),
306        "anon".as_bytes().to_vec(),
307    ];
308    let res = IRCCommand::PRIVMSG.parse_params(command_params);
309    assert!(res.is_ok());
310    assert_eq!(res.as_ref().ok().unwrap().len(), 2);
311    assert_eq!(res.as_ref().ok().unwrap()[1][0], 72);
312}
313
314#[test]
315fn test_welcome() {
316    let raw_input = vec![
317        "Anon".as_bytes().to_vec(),
318        ":Welcome".as_bytes().to_vec(),
319        "to".as_bytes().to_vec(),
320        "the".as_bytes().to_vec(),
321        "server".as_bytes().to_vec(),
322    ];
323    let res = IRCCommand::RPL_WELCOME.parse_params(raw_input);
324    println!("{:?}", res);
325    assert!(res.is_ok());
326    assert_eq!(res.as_ref().unwrap().len(), 2);
327    assert_eq!(res.as_ref().unwrap()[1].len(), 21);
328    let raw_input = vec!["Anon".as_bytes().to_vec()];
329    let res = IRCCommand::RPL_WELCOME.parse_params(raw_input);
330    println!("{:?}", res);
331    assert!(res.is_err());
332}