const_irc_message_parser/
command.rs

1//! Method for parsing and extracting a [`Command`].
2//!
3//! ## Purpose
4//!
5//! [`Command`] is part of the [IRC Message Protocol].
6//! It must never be empty.
7//! The [`Command`] occurs after the [`Source`](crate::Source) and before the [`Parameters`](crate::Parameters).
8//! Since both [`Tags`](crate::Tags) and [`Source`](crate::Source) are optional it is possible for [`Command`]
9//! to be the first component in an [`IrcMsg`](crate::IrcMsg).
10//! It can either be a `Named` or a `Numeric` command.
11//! A `Numeric` is a 3 digit string representing a reply or an error to a `Named` command.
12//! Some `Named` commands are only available after successful [capability negotiation].
13//! For such commands an IRC server must never send them to an IRC client if the client doesn't indicate support
14//! through the `CAP` command.
15//! Some commands do not require any [`Parameters`](crate::Parameters) but most do.
16//! The amount of [`Parameters`](crate::Parameters) and what they mean depends on the [`Command`].
17//!
18//! [IRC Message Protocol]: <https://modern.ircdocs.horse/#command>
19//! [capability negotiation]: <https://ircv3.net/specs/extensions/capability-negotiation.html>
20
21/// The command of an [`IrcMsg`](crate::IrcMsg).
22#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23pub enum Command<'msg> {
24    /// A [`Command`] in the form of a word.
25    Named(&'msg str),
26    /// A 3 digit number represented as a string.
27    Numeric(&'msg str),
28}
29
30impl<'msg> Command<'msg> {
31    /// Generates a [`Command`] from a slice of bytes and number of [`Parameters`](crate::Parameters).
32    ///
33    /// # Errors
34    ///
35    /// Will return `Err` if the input is empty, contains anything but ascii alphanumeric characters,
36    /// is an unsupported `Named`/`Numeric` command or is provided too few parameters.
37    /// Please file a bug report if you want to add support for a missing `Named`/`Numeric`
38    /// command or the parameters required are too low.
39    #[allow(clippy::too_many_lines)]
40    pub const fn parse(input: &'msg [u8], params_amount: usize) -> Result<Self, CommandError> {
41        if input.is_empty() {return Err(CommandError::EmptyInput);}
42        let mut number_count = 0;
43        let mut index = 0;
44        while index < input.len() {
45            if is_invalid_char(input[index]) {return Err(CommandError::InvalidByte(input[index]));}
46            if input[index].is_ascii_digit() {number_count += 1;}
47            index += 1;
48        }
49        if let Ok(cmd) = core::str::from_utf8(input) {
50            if cmd.len() == 3 && number_count == 3 {
51                let mut unhandled = false;
52                match input {
53                    b"006" | // RPL_MAP (unreal)
54                    b"007" | // RPL_MAPEND/RPL_ENDMAP (unreal)
55                    b"008" | // RPL_SNOMASK/RPL_SNOMASKIS (ircu)
56                    b"009" | // RPL_STATMEMTOT (ircu)
57                    b"014" | // RPL_YOURCOOKIE
58                    b"015" | // RPL_MAP (ircu)
59                    b"016" | // RPL_MAPMORE (ircu)
60                    b"017" | // RPL_MAPEND/RPL_ENDMAP (ircu)
61                    b"030" | // RPL_APASSWARN_SET (ircu)
62                    b"031" | // RPL_APASSWARN_SECRET (ircu)
63                    b"032" | // RPL_APASSWARN_CLEAR (ircu)
64                    b"042" | // RPL_YOURID/RPL_YOURUUID (IRCnet/inspircd)
65                    b"050" | // RPL_ATTEMPTINGJUNC (aircd)
66                    b"051" | // RPL_ATTEMPTINGREROUTE (aircd)
67                    b"210" | // RPL_TRACERECONNECT (RFC2812) (conflicts: RPL_STATS (aircd) RPL_STATSHELP (unreal))
68                    b"217" | // RPL_STATSQLINE (RFC1459) (conflict: RPL_STATSPLINE (ircu))
69                    b"220" | // RPL_STATSPLINE (hybrid) (conflicts: RPL_STATSBLINE (bahamut, unreal) RPL_STATSWLINE (nefarious))
70                    b"222" | // RPL_MODLIST (conflicts: RPL_SQLINE_NICK (unreal) RPL_STATSBLINE (bahamut) RPL_STATSJLINE (ircu) RPL_CODEPAGE (rusnet-ircd))
71                    b"223" | // RPL_STATSELINE (bahamut) (conflicts: RPL_STATSGLINE (unreal) RPL_CHARSET (rusnet-ircd))
72                    b"224" | // RPL_STATSFLINE (hybrid, bahamut) (conflict: RPL_STATSTLINE (unreal))
73                    b"225" | // RPL_STATSDLINE (hybrid) (conflicts: RPL_STATSCLONE (bahamut) RPL_STATSELINE (unreal) (depreciated: RPL_STATSZLINE (bahamut)))
74                    b"226" | // RPL_STATSCOUNT (bahamut) (conflicts: RPL_STATSALINE (hybrid) RPL_STATSNLINE (unreal))
75                    b"227" | // RPL_STATSGLINE (bahamut) (conflicts: RPL_STATSVLINE (unreal) RPL_STATSBLINE (rizon))
76                    b"228" | // RPL_STATSQLINE (ircu) (conflicts: RPL_STATSBANVER (unreal) RPL_STATSCOUNT (oftc-hybrid))
77                    b"229" | // RPL_STATSSPAMF (unreal)
78                    b"230" | // RPL_STATSEXCEPTTKL (unreal)
79                    b"231" | // RPL_SERVICEINFO (RFC1459) depreciated
80                    b"232" | // RPL_ENDOFSERVICES (RFC1459) depreciated (conflict: RPL_RULES (unreal))
81                    b"233" | // RPL_SERVICE (RFC1459) depreciated
82                    b"236" | // RPL_STATSVERBOSE (ircu)
83                    b"237" | // RPL_STATSENGINE (ircu)
84                    b"238" | // RPL_STATSFLINE (ircu)
85                    b"239" | // RPL_STATSIAUTH (IRCnet)
86                    b"240" | // RPL_STATSVLINE (RFC2812) (conflict: RPL_STATSXLINE (austhex))
87                    b"245" | // RPL_STATSSLINE (bahamut) (conflict: RPL_STATSTLINE (hybrid?))
88                    b"246" | // RPL_STATSPING (RFC2812) (conflicts: RPL_STATSSERVICE (hybrid) RPL_STATSTLINE (ircu) RPL_STATSULINE (bahamut))
89                    b"247" | // RPL_STATSBLINE (RFC2812) (conflicts: RPL_STATSXLINE (unreal) RPL_STATSGLINE (ircu))
90                    b"248" | // RPL_STATSULINE (ircu) (conflict: RPL_STATSDEFINE (IRCnet))
91                    b"249" | // RPL_STATSDEBUG (hybrid) (conflict: RPL_STATSULINE)
92                    b"250" | // RPL_STATSDLINE (RFC2812) (conflict: RPL_STATSCONN (ircu))
93                    b"264" | // RPL_USINGSSL (rusnet-ircd)
94                    b"267" | // RPL_START_NETSTAT (aircd)
95                    b"268" | // RPL_NETSTAT (aircd)
96                    b"269" | // RPL_END_NETSTAT (aircd)
97                    b"270" | // RPL_PRIVS (ircu) (conflict & depreciated: RPL_MAPUSERS (inspircd old))
98                    b"273" | // RPL_NOTIFY (aircd)
99                    b"274" | // RPL_ENDNOTIFY (aircd) (conflict: RPL_STATSDELTA (IRCnet))
100                    b"275" | // RPL_STATSDLINE (ircu) (conflict: RPL_USINGSSL (bahamut))
101                    b"277" | // RPL_VCHANLIST (hybrid) depreciated
102                    b"278" | // RPL_VCHANHELP (hybrid) depreciated
103                    b"280" | // RPL_GLIST (ircu)
104                    b"283" | // RPL_ALIST (conflict: RPL_ENDOFJUPELIST (ircu))
105                    b"284" | // RPL_ENDOFALIST (conflict: RPL_FEATURE (ircu))
106                    b"285" | // RPL_GLIST_HASH (conflicts: RPL_CHANINFO_HANDLE (aircd) RPL_NEWHOSTIS (quakenet))
107                    b"286" | // RPL_CHANINFO_USERS (aircd) (conflict: RPL_CHKHEAD (quakenet))
108                    b"287" | // RPL_CHANINFO_CHOPS (aircd) (conflict: RPL_CHANUSER (quakenet))
109                    b"288" | // RPL_CHANINFO_VOICES (aircd) (conflict: RPL_PATCHHEAD (quakenet))
110                    b"289" | // RPL_CHANINFO_AWAY (aircd) (conflict: RPL_PATCHCON (quakenet))
111                    b"290" | // RPL_CHANINFO_OPERS (aircd) (conflicts: RPL_DATASTR (quakenet) RPL_HELPHDR (unreal))
112                    b"291" | // RPL_CHANINFO_BANNED (aircd) (conflicts: RPL_ENDOFCHECK (quakenet) RPL_HELPOP (unreal))
113                    b"292" | // RPL_CHANINFO_BANS (aircd) (conflicts: RPL_HELPTLR (unreal) ERR_SEARCHNOMATCH (nefarious))
114                    b"293" | // RPL_CHANINFO_INVITE (aircd) (conflict: RPL_HELPHLP (unreal))
115                    b"294" | // RPL_CHANINFO_INVITES (aircd) (conflict: RPL_HELPFWD (unreal))
116                    b"295" | // RPL_CHANINFO_KICK (aircd) (conflict: RPL_HELPIGN (unreal))
117                    b"296" | // RPL_CHANINFO_KICKS (aircd)
118                    b"299" | // RPL_END_CHANINFO (aircd)
119                    b"300" | // RPL_NONE (RFC1459)
120                    b"302" | // RPL_USERHOST (RFC1459)
121                    b"303" | // RPL_ISON (RFC1459)
122                    b"308" | // RPL_NOTIFYACTION (aircd) (conflicts: RPL_WHOISADMIN (bahamut) RPL_RULESSTART (unreal)/RPL_RULESTART (inspircd))
123                    b"309" | // RPL_NICKTRACE (aircd) (conflicts: RPL_WHOISSADMIN (bahamut) RPL_ENDOFRULES (unreal)/RPL_RULESEND (inspircd) RPL_WHOISHELPER (austhex) RPL_WHOISSERVICE (oftc-hybrid))
124                    b"310" | // RPL_WHOISSVCMSG (bahamut) (conflicts: RPL_WHOISHELPOP (unreal) RPL_WHOISSERVICE (austhex))
125                    b"316" | // RPL_WHOISPRIVDEAF (nefarious) (conflict & depreciated: RPL_WHOISCHANOP (RFC1459))
126                    b"326" | // RPL_NOCHANPASS
127                    b"327" | // RPL_CHPASSUNKNOWN (conflict: RPL_WHOISHOST (rusnet-ircd))
128                    b"328" | // RPL_CHANNEL_URL (bahamut)/RPL_CHANNELURL (charybdis)
129                    b"334" | // RPL_LISTUSAGE (ircu) (conflicts: RPL_COMMANDSYNTAX (bahamut) RPL_LISTSYNTAX (unreal))
130                    b"339" | // RPL_BADCHANPASS (conflict: RPL_WHOISMARKS (nefarious))
131                    b"343" | // RPL_WHOISKILL (nefarious) (conflict: RPL_WHOISOPERNAME (snircd))
132                    b"344" | // RPL_WHOISCOUNTRY (inspircd) (conflicts: RPL_REOPLIST (IRCnet) RPL_QUIETLIST (oftc-hybrid))
133                    b"345" | // RPL_INVITED (gamesurge)/RPL_ISSUEDINVITE (ircu) (conflicts: RPL_ENDOFREOPLIST (IRCnet) RPL_ENDOFQUIETLIST (oftc-hybrid))
134                    b"355" | // RPL_NAMREPLY_ (quakenet)/RPL_DELNAMREPLY (ircu)
135                    b"357" | // RPL_MAP (austhex)
136                    b"358" | // RPL_MAPMORE (austhex)
137                    b"359" | // RPL_MAPEND/RPL_ENDMAP (austhex)
138                    b"360" | // RPL_WHOWASREAL (charybdis) depreciated
139                    b"361" | // RPL_KILLDONE (RFC1459)
140                    b"362" | // RPL_CLOSING (RFC1459)
141                    b"363" | // RPL_CLOSEEND (RFC1459)
142                    b"373" | // RPL_INFOSTART (RFC1459) depreciated
143                    b"377" | // RPL_KICKEXPIRED (aircd) (conflict & deprecated: RPL_SPAM (austhex))
144                    b"380" | // RPL_BANLINKED (aircd) (conflict: RPL_YOURHELPER (austhex))
145                    b"383" | // RPL_YOURESERVICE (RFC2812)
146                    b"384" | // RPL_MYPORTIS (RFC1459) depreciated
147                    b"385" | // RPL_NOTOPERANYMORE (austhex)
148                    b"386" | // RPL_QLIST (unreal) (conflicts: RPL_IRCOPS (ultimate) RPL_IRCOPSHEADER (nefarious) depreciated: RPL_RSACHALLENGE (hybrid))
149                    b"387" | // RPL_ENDOFQLIST (unreal) (conflicts: RPL_ENDOFIRCOPS (ultimate) RPL_IRCOPS (nefarious))
150                    b"388" | // RPL_ALIST (unreal) (conflict: RPL_ENDOFIRCOPS (nefarious))
151                    b"389" | // RPL_ENDOFALIST (unreal)
152                    b"398" | // RPL_STATSSLINE (snirc)
153                    b"399" | // RPL_USINGSLINE (snirc) (conflict: RPL_CLONES (inspircd))
154                    b"419" | // ERR_LENGTHTRUNCATED (aircd)
155                    b"425" | // ERR_NOOPERMOTD (unreal)
156                    b"429" | // ERR_TOOMANYAWAY (bahamut)
157                    b"430" | // ERR_EVENTNICKCHANGE (austhex)
158                    b"434" | // ERR_SERVICENAMEINUSE (austhex) (conflicts: ERR_NORULES (unreal) ERR_NONICKWHILEBAN (oftc-hybrid))
159                    b"435" | // ERR_SERVICECONFUSED (unreal) (conflict: ERR_BANONCHAN (bahamut)/ERR_BANNICKCHANGE (ratbox) depreciated: ERR_NICKONBAN (oftc-hybrid))
160                    b"438" | // ERR_NICKTOOFAST (ircu)/ERR_NCHANGETOOFAST (unreal) (conflict: ERR_DEAD (IRCnet))
161                    b"439" | // ERR_TARGETTOOFAST (ircu)/ERR_TARGETTOFAST/RPL_INVTOOFAST/RPL_MSGTOOFAST
162                    b"440" | // ERR_SERVICESDOWN (bahamut)/ERR_REG_UNAVAILABLE (ergo)
163                    b"447" | // ERR_NONICKCHANGE (unreal)/ERR_CANTCHANGENICK (inspircd)
164                    b"449" | // ERR_NOTIMPLEMENTED (undernet)
165                    b"452" | // ERR_IDCOLLISION
166                    b"453" | // ERR_NICKLOST
167                    b"455" | // ERR_HOSTILENAME (unreal)
168                    b"459" | // ERR_NOHIDING (unreal)
169                    b"460" | // ERR_NOTFORHALFOPS (unreal)
170                    b"466" | // ERR_YOUWILLBEBANNED (RFC1459) depreciated
171                    b"468" | // ERR_INVALIDUSERNAME (ircu) (conflicts: ERR_ONLYSERVERSCANCHANGE (bahamut) ERR_NOCODEPAGE (rusnet-ircd))
172                    b"469" | // ERR_LINKSET (unreal)
173                    b"470" | // ERR_LINKCHANNEL (unreal) (conflicts: ERR_KICKEDFROMCHAN (aircd) ERR_7BIT (rusnet-ircd))
174                    b"479" | // ERR_BADCHANNAME (hybrid) (conflicts: ERR_LINKFAIL (unreal) ERR_NOCOLOR (rusnet-ircd))
175                    b"480" | // ERR_NOULINE (austhex) (conflicts: ERR_CANNOTKNOCK (unreal) ERR_THROTTLE (ratbox)/ERR_NEEDTOWAIT (bahamut) ERR_NOWALLOP (rusnet-ircd) ERR_SSLONLYCHAN (oftc-hybrid))
176                    b"486" | // ERR_NONONREG (unreal)/ERR_ACCOUNTONLY (conflicts: ERR_RLINED (rusnet-ircd) depreciated: ERR_HTMDISABLED (unreal))
177                    b"487" | // ERR_CHANTOORECENT (IRCnet) (conflicts: ERR_MSGSERVICES (bahamut) ERR_NOTFORUSERS (unreal) ERR_NONONSSL (ChatIRCd))
178                    b"488" | // ERR_TSLESSCHAN (IRCnet) (conflicts: ERR_HTMDISABLED (unreal) ERR_NOSSL (bahamut))
179                    b"489" | // ERR_SECUREONLYCHAN (unreal)/ERR_SSLONLYCHAN (conflict: ERR_VOICENEEDED (undernet))
180                    b"490" | // ERR_ALLMUSTSSL (inspIRCd) (conflicts: ERR_NOSWEAR (unreal) ERR_MAXMSGSENT (bahamut))
181                    b"492" | // ERR_NOSERVICEHOST (RFC1459) depreciated (conflicts: ERR_NOCTCP (hybrid)/ERR_NOCTCPALLOWED (inspIRCd) ERR_CANNOTSENDTOUSER (charybdis))
182                    b"493" | // ERR_NOSHAREDCHAN (bahamut) (conflict: ERR_NOFEATURE (ircu))
183                    b"494" | // ERR_BADFEATVALUE (ircu) (conflict: ERR_OWNMODE (bahamut) ERR_INVITEREMOVED (inspIRCd))
184                    b"495" | // ERR_BADLOGTYPE (ircu) (conflict & depreciated: ERR_DELAYREJOIN (inspIRCd))
185                    b"496" | // ERR_BADLOGSYS (ircu)
186                    b"497" | // ERR_BADLOGVALUE (ircu)
187                    b"498" | // ERR_ISOPERLCHAN (ircu)
188                    b"499" | // ERR_CHANOWNPRIVNEEDED (unreal)
189                    b"500" | // ERR_TOOMANYJOINS (unreal) (conflicts: ERR_NOREHASHPARAM (rusnet-ircd) ERR_CANNOTSETMODDER (inspIRCd))
190                    b"504" | // ERR_USERNOTONSERV (conflict: ERR_LAST_ERR_MSG (bahamut))
191                    b"505" | // ERR_NOTINVITED (inspIRCd) (conflict & depreciated: ERR_LAST_ERR_MSG (oftc-hybrid))
192                    b"512" | // ERR_TOOMANYWATCH (bahamut)/ERR_NOTIFYFULL (aircd) (conflicts: ERR_NOSUCHGLINE (ircu) depreciated: ERR_NEEDPONG (oftc-hybrid))
193                    b"513" | // ERR_BADPING (ircu)/ERR_WRONGPONG (charybdis)/ERR_NEEDPONG (ultimate)
194                    b"514" | // ERR_TOOMANYDCC (bahamut) (conflicts: ERR_NOSUCHJUPE (ircu) depreciated: ERR_INVALID_ERROR (ircu))
195                    b"515" | // ERR_BADEXPIRE (ircu)
196                    b"516" | // ERR_DONTCHEAT (ircu)
197                    b"518" | // ERR_NOINVITE (unreal) (conflict: ERR_LONGMASK (ircu))
198                    b"519" | // ERR_ADMONLY (unreal) (conflict: ERR_TOOMANYUSERS (ircu))
199                    b"520" | // ERR_OPERONLY (unreal)/ERR_OPERONLYCHAN (hybrid)/ERR_CANTJOINOPERSONLY (inspIRCd) (conflicts: ERR_MASKTOOWIDE (ircu) depreciated: ERR_WHOTRUNC (austhex))
200                    b"521" | // ERR_LISTSYNTAX (bahamut) (conflict: ERR_NOSUCHGLINE (nefarious))
201                    b"522" | // ERR_WHOSYNTAX (bahamut)
202                    b"524" | // ERR_QUARANTINED (ircu) (conflicts: ERR_OPERSVERIFY (unreal) ERR_HELPNOTFOUND (hybrid))
203                    b"525" | // ERR_INVALIDKEY (ircu) (conflict & depreciated: ERR_REMOTEPFX)
204                    b"530" | // ERR_BADHOSTMASK (snircd)
205                    b"550" | // ERR_BADHOSTMASK (quakenet)
206                    b"551" | // ERR_HOSTUNAVAIL (quakenet)
207                    b"552" | // ERR_USINGSLINE (quakenet)
208                    b"553" | // ERR_STATSSLINE (quakenet)
209                    b"560" | // ERR_NOTLOWEROPLEVEL (ircu)
210                    b"561" | // ERR_NOTMANAGER (ircu)
211                    b"562" | // ERR_CHANSECURED (ircu)
212                    b"563" | // ERR_UPASSSET (ircu)
213                    b"564" | // ERR_UPASSNOTSET (ircu)
214                    b"565" | // ERR_NOMANAGER_LONG (ircu) depreciated
215                    b"566" | // ERR_NOMANAGER (ircu)
216                    b"567" | // ERR_UPASS_SAME_APASS (ircu)
217                    b"568" | // ERR_LASTERROR (ircu) (conflict: RPL_NOOMOTD (nefarious))
218                    b"573" | // ERR_CANNOTSENDRP (ergo)
219                    b"597" | // RPL_REAWAY (unreal)
220                    b"603" | // RPL_WATCHSTAT (unreal)
221                    b"606" | // RPL_WATCHLIST (unreal)
222                    b"607" | // RPL_ENDOFWATCHLIST (unreal)
223                    b"608" | // RPL_WATCHCLEAR (ultimate)/RPL_CLEARWATCH (unreal)
224                    b"610" | // RPL_MAPMORE (unreal) (conflict: RPL_ISOPER (ultimate))
225                    b"611" | // RPL_ISLOCOP (ultimate)
226                    b"612" | // RPL_ISNOTOPER (ultimate)
227                    b"613" | // RPL_ENDOFISOPER (ultimate)
228                    b"615" | // RPL_MAPMORE (ptlink) (conflict: RPL_WHOISMODES (ultimate))
229                    b"616" | // RPL_WHOISHOST (ultimate)
230                    b"617" | // RPL_WHOISSSLFP (nefarious) (conflicts: RPL_DCCSTATUS (bahamut) RPL_WHOISBOT (ultimate))
231                    b"618" | // RPL_DCCLIST (bahamut)
232                    b"619" | // RPL_ENDOFDCCLIST (bahamut) (conflict: RPL_WHOWASHOST (ultimate))
233                    b"620" | // RPL_DCCINFO (bahamut) (conflict: RPL_RULESSTART (ultimate))
234                    b"621" | // RPL_RULES (ultimate)
235                    b"622" | // RPL_ENDOFRULES (ultimate)
236                    b"623" | // RPL_MAPMORE (ultimate)
237                    b"624" | // RPL_OMOTDSTART (ultimate)
238                    b"625" | // RPL_OMOTD (ultimate)
239                    b"626" | // RPL_ENDOFOMOTD (ultimate)
240                    b"630" | // RPL_SETTINGS (ultimate)
241                    b"631" | // RPL_ENDOFSETTINGS (ultimate)
242                    b"640" | // RPL_DUMPING (unreal) depreciated
243                    b"641" | // RPL_DUMPRPL (unreal) depreciated
244                    b"642" | // RPL_EODUMP (unreal) depreciated
245                    b"687" | // RPL_YOURLANGUAGESARE (ergo)
246                    b"727" | // RPL_TESTMASKGECOS (ratbox) (conflict: RPL_ISCAPTURED (oftc-hybrid))
247                    b"728" | // RPL_QUIETLIST (charybdis) (conflict: RPL_ISUNCAPTURED (ofc-hybrid))
248                    b"744" | // ERR_TOPICLOCK (inspIRCd)
249                    b"762" | // RPL_METADATAEND (IRCv3)
250                    b"771" | // RPL_XINFO (ithildin)
251                    b"773" | // RPL_XINFOSTART (ithildin)
252                    b"774" | // RPL_XINFOEND (ithildin)
253                    b"802" | // RPL_CHECK (inspIRCd)
254                    b"975" | // RPL_LOADEDMODULE (inspIRCd) (conflict: ERR_LASTERROR (nefarious))
255                    b"981" | // ERR_TOOMANYLANGUAGES (ergo)
256                    b"982" | // ERR_NOLANGUAGE (ergo)
257                    b"999"   // ERR_NUMERIC_ERR (bahamut)/ERR_NUMERICERR/ERR_LAST_ERR_MSG (depreciated: RPL_ENDOFDCCALLOWHELP (inspIRCd))
258                    => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));},
259                    b"001" | // RPL_WELCOME
260                    b"002" | // RPL_YOURHOST/RPL_YOURHOSTIS
261                    b"003" | // RPL_CREATED/RPL_SERVERCREATED
262                    b"005" | // RPL_ISUPPORT/RPL_PROTOCTL (depreciated: RPL_BOUNCE moved to 010)
263                    b"018" | // RPL_MAPUSERS (inspircd)
264                    b"020" | // RPL_HELLO (rusnet-ircd)
265                    b"105" | // RPL_REMOTEISUPPORT (unreal)
266                    b"203" | // RPL_TRACEUNKNOWN (RFC1459)
267                    b"221" | // RPL_UMODEIS (RFC1459)
268                    b"242" | // RPL_STATSUPTIME (RFC1459)
269                    b"251" | // RPL_LUSERCLIENT (RFC1459)
270                    b"255" | // RPL_LUSERME (RFC1459)
271                    b"256" | // RPL_ADMINME (RFC1459)
272                    b"257" | // RPL_ADMINLOC1 (RFC1459)
273                    b"258" | // RPL_ADMINLOC2 (RFC1459)
274                    b"259" | // RPL_ADMINEMAIL (RFC1459)
275                    b"265" | // RPL_LOCALUSERS/RPL_CURRENT_LOCAL (bahamut)
276                    b"266" | // RPL_GLOBALUSERS/RPL_CURRENT_GLOBAL (bahamut)
277                    b"271" | // RPL_SILELIST (ircu)
278                    b"272" | // RPL_ENDOFSILELIST (ircu)
279                    b"281" | // RPL_ACCEPTLIST (conflict: RPL_ENDOFGLIST (ircu))
280                    b"282" | // RPL_ENDOFACCEPT (conflict: RPL_JUPELIST (ircu))
281                    b"304" | // RPL_TEXT (unreal)
282                    b"305" | // RPL_UNAWAY (RFC1459)
283                    b"306" | // RPL_NOWAWAY (RFC1459)
284                    b"321" | // RPL_LISTSTART (RFC1459) depreciated
285                    b"323" | // RPL_LISTEND (RFC1459)
286                    b"336" | // RPL_INVITELIST (hybrid not 346) (conflict: RPL_WHOISBOT (nefarious))
287                    b"337" | // RPL_ENDOFINVITELIST (hybrid not 347) (conflict: RPL_WHOISTEXT (older hybrid?))
288                    b"340" | // RPL_USERIP (ircu)
289                    b"354" | // RPL_WHOSPCRPL (ircu)/RPL_RWHOREPLY (bahamut)
290                    b"371" | // RPL_INFO (RFC1459)
291                    b"372" | // RPL_MOTD (RFC1459)
292                    b"374" | // RPL_ENDOFINFO (RFC1459)
293                    b"375" | // RPL_MOTDSTART (RFC1459)
294                    b"376" | // RPL_ENDOFMOTD (RFC1459)
295                    b"381" | // RPL_YOUREOPER (RFC1459)/RPL_YOUAREOPER (inspircd)
296                    b"392" | // RPL_USERSSTART (RFC1459)
297                    b"393" | // RPL_USERS (RFC1459)
298                    b"394" | // RPL_ENDOFUSERS (RFC1459)
299                    b"395" | // RPL_NOUSERS (RFC1459)
300                    b"406" | // ERR_WASNOSUCHNICK (RFC1459)
301                    b"409" | // ERR_NOORIGIN (RFC1459)
302                    b"410" | // ERR_INVALIDCAPCMD (undernet?)/ERR_INVALIDCAPSUBCOMMAND (inspircd)/ERR_UNKNOWNCAPCMD (ircu)
303                    b"411" | // ERR_NORECIPIENT (RFC1459)
304                    b"412" | // ERR_NOTEXTTOSEND (RFC1459)
305                    b"417" | // ERR_INPUTTOOLONG (ircu)
306                    b"420" | // ERR_AMBIGUOUSCOMMAND (inspircd)
307                    b"422" | // ERR_NOMOTD (RFC1459)
308                    b"424" | // ERR_FILEERROR (RFC1459)
309                    b"431" | // ERR_NONICKNAMEGIVEN (RFC1459)
310                    b"436" | // ERR_ERR_NICKCOLLISION (RFC1459)
311                    b"445" | // ERR_SUMMONDISABLED (RFC1459)
312                    b"446" | // ERR_USERSDISABLED (RFC1459)
313                    b"448" | // ERR_FORBIDDENCHANNEL (unreal)
314                    b"451" | // ERR_NOTREGISTERED (RFC1459)
315                    b"456" | // ERR_ACCEPTFULL
316                    b"462" | // ERR_ALREADYREGISTERED (RFC1459)/ERR_ALREADYREGISTRED
317                    b"463" | // ERR_NOPERMFORHOST (RFC1459)
318                    b"464" | // ERR_PASSWDMISMATCH (RFC1459)
319                    b"465" | // ERR_YOUREBANNEDCREEP (RFC1459)
320                    b"481" | // ERR_NOPRIVILEGES (RFC1459)
321                    b"483" | // ERR_CANTKILLSERVER (RFC1459)/ERR_KILLDENY (unreal)
322                    b"484" | // ERR_RESTRICTED (RFC2812) (conflicts: ERR_ISCHANSERVICE (undernet) ERR_DESYNC (bahamut) ERR_ATTACKDENY (unreal))
323                    b"485" | // ERR_UNIQOPRIVSNEEDED (RFC2812) (conflicts: ERR_KILLDENY (unreal) ERR_CANTKICKADMIN (PTlink) ERR_ISREALSERVICE (quakenet) ERR_CHANBANREASON (hybrid) depreciated: ERR_BANNEDNICK (ratbox))
324                    b"491" | // ERR_NOOPERHOST (RFC1459)
325                    b"501" | // ERR_UMODEUNKOWNFLAG (RFC1459) (conflict: ERR_UNKNOWNSNOMASK (inspIRCd))
326                    b"502" | // ERR_USERSDONTMATCH (RFC1459)
327                    b"503" | // ERR_GHOSTEDCLIENT (hybrid) depreciated (conflict & depreciated: ERR_VWORLDWARN (austhex))
328                    b"511" | // ERR_SILELISTFULL (ircu)
329                    b"523" | // ERR_WHOLIMEXCEED (bahamut)
330                    b"526" | // ERR_PFXUNROUTABLE depreciated
331                    b"653" | // RPL_UNINVITED (inspIRCd)
332                    b"670" | // RPL_STARTTLS (IRCv3)
333                    b"672" | // RPL_UNKNOWNMODES (ithildin) (conflict: RPL_WHOISREALIP (rizon)/RPL_WHOISCGI (plexus))
334                    b"673" | // RPL_CANNOTSETMODES (ithildin)
335                    b"674" | // RPL_WHOISYOURID (ChatIRCd)
336                    b"690" | // ERR_REDIRECT (inspIRCd)
337                    b"691" | // ERR_STARTTLS (IRCv3)
338                    b"700" | // RPL_COMMANDS (inspIRCd)
339                    b"701" | // RPL_COMMANDSEND (inspIRCd)
340                    b"702" | // RPL_MODLIST (ratbox) (conflict & depreciated: RPL_COMMANDS (inspIRCd))
341                    b"703" | // RPL_ENDOFMODLIST (ratbox) (conflict & depreciated: RPL_COMMANDSEND (inspIRCd))
342                    b"715" | // ERR_KNOCKDISABLED (ratbox) (conflicts: ERR_TOOMANYINVITE (hybrid) RPL_INVITETHROTTLE (rizon))
343                    b"716" | // RPL_TARGUMODEG (ratbox)/ERR_TARGUMODEG
344                    b"717" | // RPL_TARGNOTIFY (ratbox)
345                    b"720" | // RPL_OMOTDSTART (ratbox)
346                    b"721" | // RPL_OMOTD (ratbox)
347                    b"722" | // RPL_ENDOFOMOTD (ratbox)
348                    b"730" | // RPL_MONONLINE (ratbox)
349                    b"731" | // RPL_MONOFFLINE (ratbox)
350                    b"732" | // RPL_MONLIST (ratbox)
351                    b"733" | // RPL_ENDOFMONLIST (ratbox)
352                    b"740" | // RPL_RSACHALLENGE2 (ratbox)
353                    b"741" | // RPL_ENDOFRSACHALLENGE2 (ratbox)
354                    b"750" | // RPL_SCANMATCHED (ratbox)
355                    b"759" | // RPL_ETRACEEND (irc2.11)
356                    b"764" | // ERR_METADATALIMIT (IRCv3)
357                    b"765" | // ERR_TARGETINVALID (IRCv3)
358                    b"767" | // ERR_KEYINVALID (IRCv3)
359                    b"902" | // ERR_NICKLOCKED (IRCv3)
360                    b"903" | // RPL_SASLSUCCESS (IRCv3)
361                    b"904" | // ERR_SASLFAIL (IRCv3)
362                    b"905" | // ERR_SASLTOOLONG (IRCv3)
363                    b"906" | // ERR_SASLABORTED (IRCv3)
364                    b"907" | // ERR_SASLALREADY (IRCv3)
365                    b"944" | // RPL_IDLETIMESET (inspIRCd)
366                    b"948" | // ERR_INVALIDIDLETIME (inspIRCd)
367                    b"961" | // RPL_PROPLIST (inspIRCd)
368                    b"990" | // RPL_DCCALLOWSTART (inspIRCd)
369                    b"992" | // RPL_DCCALLOWEND (inspIRCd)
370                    b"998"   // ERR_UNKNOWNDCCALLOWCMD (inspIRCd) (depreciated: RPL_DCCALLOWHELP (inspIRCd))
371                    => if params_amount < 2 {return Err(CommandError::MinimumArgsRequired(2, cmd));},
372                    b"043" | // RPL_SAVENICK (IRCnet)
373                    b"200" | // RPL_TRACELINK (RFC1459)
374                    b"201" | // RPL_TRACECONNECTING (RFC1459)
375                    b"202" | // RPL_TRACEHANDSHAKE (RFC1459)
376                    b"204" | // RPL_TRACEOPERATOR (RFC1459)
377                    b"205" | // RPL_TRACEUSER (RFC1459)
378                    b"208" | // RPL_TRACENEWTYPE (RFC1459)
379                    b"209" | // RPL_TRACECLASS (RFC2812)
380                    b"212" | // RPL_STATSCOMMANDS (RFC1459)
381                    b"219" | // RPL_ENDOFSTATS (RFC1459)
382                    b"252" | // RPL_LUSEROP (RFC1459)
383                    b"253" | // RPL_LUSERUNKNOWN (RFC1459)
384                    b"254" | // RPL_LUSERCHANNELS (RFC1459)
385                    b"261" | // RPL_TRACELOG (RFC1459)
386                    b"263" | // RPL_TRYAGAIN/RPL_LOAD2HI/RPL_LOAD_THROTTLED (RFC2812)
387                    b"276" | // RPL_WHOISCERTFP (oftc-hybrid) (conflicts: RPL_STATSRLINE (ircu) depreciated: RPL_VCHANEXIST (hybrid))
388                    b"301" | // RPL_AWAY (RFC1459)
389                    b"307" | // RPL_USERIP (conflicts: RPL_WHOISREGNICK (bahamut) RPL_SUPERHOST (austhex))
390                    b"313" | // RPL_WHOISOPERATOR (RFC1459)
391                    b"315" | // RPL_ENDOFWHO (RFC1459)
392                    b"318" | // RPL_ENDOFWHOIS (RFC1459)
393                    b"319" | // RPL_WHOISCHANNELS (RFC1459)
394                    b"320" | // RPL_WHOISSPECIAL (unreal) (conflicts: RPL_WHOIS_HIDDEN (anothernet) RPL_WHOISVIRT (austhex))
395                    b"324" | // RPL_CHANNELMODEIS (RFC1459)
396                    b"325" | // RPL_UNIQOPIS (RFC2812) (conflicts: RPL_CHANNELPASSIS RPL_WHOISWEBIRC (nefarious) depreciated: RPL_CHANNELMLOCKIS/RPL_CHANNELMLOCK (sorircd))
397                    b"329" | // RPL_CREATIONTIME (bahamut)/RPL_CHANNELCREATED (inspircd)
398                    b"331" | // RPL_NOTOPIC (RFC1459)/RPL_NOTOPICSET (inspircd)
399                    b"332" | // RPL_TOPIC (RFC1459)/RPL_TOPICSET (inspircd)
400                    b"333" | // RPL_TOPICWHOTIME (ircu)/RPL_TOPICTIME (inspircd)
401                    b"335" | // RPL_WHOISBOT (unreal) (conflicts: RPL_WHOISTEXT (hybrid) RPL_WHOISACCOUNTONLY (nefarious))
402                    b"338" | // RPL_WHOISACTUALLY (ircu) (conflict: RPL_CHANPASSOK)
403                    b"341" | // RPL_INVITING (RFC1459)
404                    b"342" | // RPL_SUMMONING (RFC1459) depreciated
405                    b"346" | // RPL_INVITELIST (RFC2812 not 336)/RPL_INVEXLIST (hybrid)
406                    b"347" | // RPL_ENDOFINVITELIST (RFC2812 not 337)/RPL_ENDOFINVEXLIST (hybrid)
407                    b"348" | // RPL_EXCEPTLIST (RFC2812)/RPL_EXLIST (unreal)/RPL_EXEMPTLIST (bahamut)
408                    b"349" | // RPL_ENDOFEXCEPTLIST (RFC2812)/RPL_ENDOFEXLIST (unreal)/RPL_ENDOFEXEMPTLIST (bahamut)
409                    b"365" | // RPL_ENDOFLINKS (RFC1459)
410                    b"366" | // RPL_ENDOFNAMES (RFC1459)
411                    b"367" | // RPL_BANLIST (RFC1459)
412                    b"368" | // RPL_ENDOFBANLIST (RFC1459)
413                    b"369" | // RPL_ENDOFWHOWAS (RFC1459)
414                    b"378" | // RPL_BANEXPIRED (aircd) (conflicts: RPL_WHOISHOST (unreal) depreciated: RPL_MOTD (austhex))
415                    b"379" | // RPL_KICKLINKED (aircd) (conflicts: RPL_WHOISMODES (unreal) depreciated: RPL_WHOWASIP (inspircd))
416                    b"382" | // RPL_REHASHING (RFC1459)
417                    b"391" | // RPL_TIME (RFC1459)
418                    b"396" | // RPL_HOSTHIDDEN (unreal)/RPL_VISIBLEHOST (hybrid)/RPL_YOURDISPLAYEDHOST (inspircd)
419                    b"400" | // ERR_UNKNOWNERROR (ergo) (conflict & depreciated: ERR_FIRSTERROR (ircu))
420                    b"401" | // ERR_NOSUCHNICK (RFC1459)
421                    b"402" | // ERR_NOSUCHSERVER (RFC1459)
422                    b"403" | // ERR_NOSUCHCHANNEL (RFC1459)
423                    b"404" | // ERR_CANNOTSENDTOCHAN (RFC1459)
424                    b"405" | // ERR_TOOMANYCHANNELS (RFC1459)
425                    b"407" | // ERR_TOOMANYTARGETS (RFC1459)
426                    b"408" | // ERR_NOSUCHSERVICE (RFC2812) (conflicts: ERR_NOCOLORSONCHAN (bahamut) ERR_NOCTRLSONCHAN (hybrid) ERR_SEARCHNOMATCH (snircd))
427                    b"413" | // ERR_NOTPLEVEL (RFC1459)
428                    b"414" | // ERR_WILDTOPLEVEL (RFC1459)
429                    b"415" | // ERR_BADMASK (RFC2812) (conflict: ERR_MSGNEEDREGGEDNICK (solanum)/ERR_CANTSENDREGONLY (oftc-hybrid))
430                    b"416" | // ERR_TOOMANYMATCHES (IRCnet)/ERR_QUERYTOOLONG (ircu)
431                    b"421" | // ERR_UNKNOWNCOMMAND (RFC1459)
432                    b"423" | // ERR_NOADMININFO (RFC1459)
433                    b"432" | // ERR_ERRONEUSNICKNAME (RFC1459)
434                    b"433" | // ERR_NICKNAMEINUSE (RFC1459)
435                    b"437" | // ERR_UNAVAILRESOURCE (RFC2812) (conflict: ERR_BANNICKCHANGE (ircu))
436                    b"442" | // ERR_NOTONCHANNEL (RFC1459)
437                    b"444" | // ERR_NOLOGIN (RFC1459)
438                    b"457" | // ERR_ACCEPTEXIST
439                    b"458" | // ERR_ACCEPTNOT
440                    b"461" | // ERR_NEEDMOREPARAMS (RFC1459)
441                    b"467" | // ERR_KEYSET (RFC1459)
442                    b"471" | // ERR_CHANNELISFULL (RFC1459)
443                    b"472" | // ERR_UNKNOWNMODE (RFC1459)
444                    b"473" | // ERR_INVITEONLYCHAN (RFC1459)
445                    b"474" | // ERR_BANNEDFROMCHAN (RFC1459)
446                    b"475" | // ERR_BADCHANNELKEY (RFC1459)
447                    b"476" | // ERR_BADCHANMASK (RFC2812) (conflict: ERR_OPERONLYCHAN (plexus))
448                    b"477" | // ERR_NOCHANMODES (RFC2812)/ERR_MODELESS (conflict: ERR_NEEDREGGEDNICK (bahamut)/ERR_REGONLYCHAN (oftc-hybrid))
449                    b"478" | // ERR_BANLISTFULL (RFC2812)
450                    b"482" | // ERR_CHANOPRIVSNEEDED (RFC1459)
451                    b"517" | // ERR_DISABLED (ircu)
452                    b"531" | // ERR_CANTSENDTOUSER (inspIRCd)/ERR_HOSTUNAVAIL (snircd)
453                    b"650" | // RPL_SYNTAX (inspIRCd)
454                    b"651" | // RPL_CHANNELMSG (inspIRCd)
455                    b"652" | // RPL_WHOWASIP (inspIRCd)
456                    b"659" | // RPL_SPAMCMDFWD (unreal)
457                    b"671" | // RPL_WHOISSECURE (unreal)/RPL_WOISSSL (nefarious)
458                    b"704" | // RPL_HELPSTART (ratbox)
459                    b"705" | // RPL_HELPTXT (ratbox)
460                    b"706" | // RPL_ENDOFHELP (ratbox)
461                    b"707" | // ERR_TARGCHANGE (ratbox)
462                    b"710" | // RPL_KNOCK (ratbox)
463                    b"711" | // RPL_KNOCKDLVR (ratbox)
464                    b"712" | // ERR_TOOMANYKNOCK (ratbox)
465                    b"713" | // ERR_CHANOPEN (ratbox)
466                    b"714" | // ERR_KNOCKONCHAN (ratbox)
467                    b"718" | // RPL_UMODEGMSG (ratbox)
468                    b"723" | // ERR_NOPRIVS (ratbox)
469                    b"726" | // RPL_NOTESTLINE (ratbox)
470                    b"761" | // RPL_KEYVALUE (IRCv3)
471                    b"766" | // ERR_NOMATCHINGKEY (IRCv3)
472                    b"768" | // ERR_KEYNOTSET (IRCv3)
473                    b"769" | // ERR_KEYNOPERMISSION (IRCv3)
474                    b"801" | // RPL_STATSCOUNTRY (inspIRCd)
475                    b"901" | // RPL_LOGGEDOUT (IRCv3)
476                    b"908" | // RPL_SASLMECHS (IRCv3)
477                    b"910" | // RPL_ACCESSLIST (inspIRCd)
478                    b"911" | // RPL_ENDOFACCESSLIST (inspIRCd)
479                    b"926" | // ERR_BADCHANNEL (inspIRCd)
480                    b"937" | // ERR_ALREADYCHANFILTERED (inspIRCd) depreciated
481                    b"938" | // ERR_NOSUCHCHANFILTER (inspIRCd) depreciated
482                    b"939" | // ERR_CHANFILTERFULL (inspIRCd) depreciated
483                    b"940" | // RPL_ENDOFSPAMFILTER (inspIRCd)
484                    b"942" | // ERR_INVALIDWATCHNICK (inspIRCd)
485                    b"945" | // RPL_NICKLOCKOFF (inspIRCd)
486                    b"946" | // ERR_NICKNOTLOCKED (inspIRCd)
487                    b"947" | // RPL_NICKLOCKON (inspIRCd)
488                    b"950" | // RPL_UNSILENCED (inspIRCd)
489                    b"951" | // RPL_SILENCED (inspIRCd)
490                    b"952" | // ERR_SILENCE (inspIRCd)
491                    b"953" | // RPL_ENDOFEXEMPTIONLIST (inspIRCd)
492                    b"960" | // RPL_ENDOFPROPLIST (inspIRCd)
493                    b"972" | // ERR_CANNOTDOCOMMAND (unreal) (conflict: ERR_CANTUNLOADMODULE (inspIRCd))
494                    b"973" | // RPL_UNLOADEDMODULE (inspIRCd)
495                    b"974" | // RPL_CANNOTCHANGECHANMODE (unreal) (conflict: ERR_CANTLOADMODULE (inspIRCd))
496                    b"988" | // RPL_SERVLOCKON (inspIRCd)
497                    b"989" | // RPL_SERVLOCKOFF (inspIRCd)
498                    b"991" | // RPL_DCCALLOWLIST (inspIRCd)
499                    b"993" | // RPL_DCCALLOWTIMED (inspIRCd)
500                    b"994" | // RPL_DCCALLOWPERMANENT (inspIRCd)
501                    b"995" | // RPL_DCCALLOWREMOVED (inspIRCd)
502                    b"996" | // ERR_DCCALLOWINVALID (inspIRCd)
503                    b"997"   // RPL_DCCALLOWEXPIRED (inspIRCd)
504                    => if params_amount < 3 {return Err(CommandError::MinimumArgsRequired(3, cmd));},
505                    b"010" | // RPL_BOUNCE/RPL_REDIR (depreciated & conflict: RPL_STATMEM (ircu))
506                    b"235" | // RPL_SERVLISTEND (RFC2812)
507                    b"262" | // RPL_TRACEEND/RPL_ENDOFTRACE (RFC2812) (conflict: RPL_TRACEPING)
508                    b"312" | // RPL_WHOISSERVER (RFC1459)
509                    b"322" | // RPL_LIST (RFC1459)
510                    b"330" | // RPL_WHOISACCOUNT (ircu)/RPL_WHOISLOGGEDIN (conflict: RPL_WHOWAS_TIME)
511                    b"350" | // RPL_WHOISGATEWAY (inspircd)
512                    b"351" | // RPL_VERSION (RFC1459)
513                    b"353" | // RPL_NAMREPLY (RFC1459)
514                    b"364" | // RPL_LINKS (RFC1459)
515                    b"441" | // ERR_USERNOTINCHANNEL (RFC1459)
516                    b"443" | // ERR_USERONCHANNEL (RFC1459)
517                    b"569" | // RPL_WHOISASN (inspIRCd)
518                    b"729" | // RPL_ENDOFQUIETLIST (charybdis)
519                    b"734" | // ERR_MONLISTFULL (ratbox)
520                    b"742" | // ERR_MLOCKRESTRICTED (charybdis)
521                    b"743" | // ERR_INVALIDBAN (charybdis)
522                    b"760" | // RPL_WHOISKEYVALUE (IRCv3)
523                    b"803" | // RPL_OTHERUMODEIS (inspIRCd)
524                    b"804" | // RPL_OTHERSNOMASKIS (inspIRCd)
525                    b"900" | // RPL_LOGGEDIN (IRCv3)
526                    b"936"   // ERR_WORDFILTERED (inspIRCd) depreciated
527                    => if params_amount < 4 {return Err(CommandError::MinimumArgsRequired(4, cmd));},
528                    b"004" | // RPL_MYINFO/RPL_SERVERVERSION
529                    b"206" | // RPL_TRACESERVER (RFC1459)
530                    b"207" | // RPL_TRACESERVICE (RFC2812) (conflict: RPL_TRACECAPTURED (oftc-hybrid))
531                    b"244" | // RPL_STATSHLINE (RFC1459)
532                    b"317" | // RPL_WHOISIDLE (RFC1459)
533                    b"598" | // RPL_GONEAWAY (unreal)
534                    b"599" | // RPL_NOTAWAY (unreal)
535                    b"600" | // RPL_LOGON (unreal)
536                    b"601" | // RPL_LOGOFF (unreal)
537                    b"602" | // RPL_WATCHOFF (unreal)
538                    b"604" | // RPL_NOWON (unreal)
539                    b"605" | // RPL_NOWOFF (unreal)
540                    b"609" | // RPL_NOWISAWAY (unreal)
541                    b"696" | // ERR_INVALIDMODEPARAM (inspIRCd)
542                    b"697" | // ERR_LISTMODEALREADYSET (inspIRCd)
543                    b"698" | // ERR_LISTMODENOTSET (inspIRCd)
544                    b"724" | // RPL_TESTMASK (ratbox)
545                    b"725" | // RPL_TESTLINE (ratbox)
546                    b"941" | // RPL_SPAMFILTER (inspIRCd)
547                    b"954"   // RPL_EXEMPTIONLIST (inspIRCd)
548                    => if params_amount < 5 {return Err(CommandError::MinimumArgsRequired(5, cmd));},
549                    b"218" | // RPL_STATSYLINE (RFC1459)
550                    b"241" | // RPL_STATSLLINE (RFC1459)
551                    b"243" | // RPL_STATSOLINE (RFC1459)
552                    b"311" | // RPL_WHOISUSER (RFC1459)
553                    b"314"   // RPL_WHOWASUSER (RFC1459)
554                    => if params_amount < 6 {return Err(CommandError::MinimumArgsRequired(6, cmd));},
555                    b"213" | // RPL_STATSCLINE (RFC1459)
556                    b"214" | // RPL_STATSNLINE (RFC1459)/RPL_STATSOLDNLINE (ircu, unreal)
557                    b"215" | // RPL_STATSILINE (RFC1459)
558                    b"216" | // RPL_STATSKLINE (RFC1459)
559                    b"234" | // RPL_SERVLIST (RFC2812)
560                    b"709" | // RPL_ETRACE (ratbox)
561                    b"751"   // RPL_SCANUMODES (ratbox)
562                    => if params_amount < 7 {return Err(CommandError::MinimumArgsRequired(7, cmd));},
563                    b"211" | // RPL_STATSLINKINFO (RFC1459)
564                    b"352" | // RPL_WHOREPLY (RFC1459)
565                    b"708"   // RPL_ETRACEFULL (ratbox)
566                    => if params_amount < 8 {return Err(CommandError::MinimumArgsRequired(8, cmd));},
567                    _ => unhandled = true,
568                }
569                if unhandled {return Err(CommandError::UnhandledNumeric(cmd));}
570                return Ok(Self::Numeric(cmd));
571            } else if number_count > 0 {return Err(CommandError::NumberInNamedCommand(cmd));}
572            match &command_to_uppercase_bytes(input) {
573                b"INFO00000000" => return Ok(Self::Named("INFO")),
574                b"LUSERS000000" => return Ok(Self::Named("LUSERS")),
575                b"REHASH000000" => return Ok(Self::Named("REHASH")),
576                b"RESTART00000" => return Ok(Self::Named("RESTART")),
577                b"LINKS0000000" => return Ok(Self::Named("LINKS")),
578                b"QUIT00000000" => return Ok(Self::Named("QUIT")),
579                b"MOTD00000000" => return Ok(Self::Named("MOTD")),
580                b"VERSION00000" => return Ok(Self::Named("VERSION")),
581                b"ADMIN0000000" => return Ok(Self::Named("ADMIN")),
582                b"TIME00000000" => return Ok(Self::Named("TIME")),
583                b"HELP00000000" => return Ok(Self::Named("HELP")),
584                b"AWAY00000000" => return Ok(Self::Named("AWAY")),
585                b"LIST00000000" => return Ok(Self::Named("LIST")),
586                b"ACK000000000" => return Ok(Self::Named("ACK")),
587                b"ACCEPT000000" => return Ok(Self::Named("ACCEPT")),
588                b"SILENCE00000" => return Ok(Self::Named("SILENCE")),
589                b"DIE000000000" => return Ok(Self::Named("DIE")),
590                b"TRACE0000000" => return Ok(Self::Named("TRACE")),
591                b"ETRACE000000" => return Ok(Self::Named("ETRACE")),
592                b"SERVLIST0000" => return Ok(Self::Named("SERVLIST")),
593                b"USERS0000000" => return Ok(Self::Named("USERS")),
594                b"MAP000000000" => return Ok(Self::Named("MAP")),
595                b"PASS00000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
596                                   else {return Ok(Self::Named("PASS"));},
597                b"NICK00000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
598                                   else {return Ok(Self::Named("NICK"));},
599                b"PING00000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
600                                   else {return Ok(Self::Named("PING"));},
601                b"ERROR0000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
602                                   else {return Ok(Self::Named("ERROR"));},
603                b"NAMES0000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
604                                   else {return Ok(Self::Named("NAMES"));},
605                b"WHO000000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
606                                   else {return Ok(Self::Named("WHO"));},
607                b"WALLOPS00000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
608                                   else {return Ok(Self::Named("WALLOPS"));},
609                b"AUTHENTICATE" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
610                                   else {return Ok(Self::Named("AUTHENTICATE"));},
611                b"ACCOUNT00000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
612                                   else {return Ok(Self::Named("ACCOUNT"));},
613                b"CAP000000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
614                                   else {return Ok(Self::Named("CAP"));},
615                b"MODE00000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
616                                   else {return Ok(Self::Named("MODE"));},
617                b"PONG00000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
618                                   else {return Ok(Self::Named("PONG"));},
619                b"JOIN00000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
620                                   else {return Ok(Self::Named("JOIN"));},
621                b"PART00000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
622                                   else {return Ok(Self::Named("PART"));},
623                b"TOPIC0000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
624                                   else {return Ok(Self::Named("TOPIC"));},
625                b"STATS0000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
626                                   else {return Ok(Self::Named("STATS"));},
627                b"WHOIS0000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
628                                   else {return Ok(Self::Named("WHOIS"));},
629                b"WHOWAS000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
630                                   else {return Ok(Self::Named("WHOWAS"));},
631                b"CONNECT00000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
632                                   else {return Ok(Self::Named("CONNECT"));},
633                b"USERHOST0000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
634                                   else {return Ok(Self::Named("USERHOST"));},
635                b"TAGMSG000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
636                                   else {return Ok(Self::Named("TAGMSG"));},
637                b"BATCH0000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
638                                   else {return Ok(Self::Named("BATCH"));},
639                b"SETNAME00000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
640                                   else {return Ok(Self::Named("SETNAME"));},
641                b"MONITOR00000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
642                                   else {return Ok(Self::Named("MONITOR"));},
643                b"ISON00000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
644                                   else {return Ok(Self::Named("ISON"));},
645                b"KNOCK0000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
646                                   else {return Ok(Self::Named("KNOCK"));},
647                b"SUMMON000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
648                                   else {return Ok(Self::Named("SUMMON"));},
649                b"USERIP000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
650                                   else {return Ok(Self::Named("USERIP"));},
651                b"WATCH0000000" => if params_amount < 1 {return Err(CommandError::MinimumArgsRequired(1, cmd));}
652                                   else {return Ok(Self::Named("WATCH"));},
653                b"OPER00000000" => if params_amount < 2 {return Err(CommandError::MinimumArgsRequired(2, cmd));}
654                                   else {return Ok(Self::Named("OPER"));},
655                b"INVITE000000" => if params_amount < 2 {return Err(CommandError::MinimumArgsRequired(2, cmd));}
656                                   else {return Ok(Self::Named("INVITE"));},
657                b"PRIVMSG00000" => if params_amount < 2 {return Err(CommandError::MinimumArgsRequired(2, cmd));}
658                                   else {return Ok(Self::Named("PRIVMSG"));},
659                b"NOTICE000000" => if params_amount < 2 {return Err(CommandError::MinimumArgsRequired(2, cmd));}
660                                   else {return Ok(Self::Named("NOTICE"));},
661                b"KILL00000000" => if params_amount < 2 {return Err(CommandError::MinimumArgsRequired(2, cmd));}
662                                   else {return Ok(Self::Named("KILL"));},
663                b"SQUIT0000000" => if params_amount < 2 {return Err(CommandError::MinimumArgsRequired(2, cmd));}
664                                   else {return Ok(Self::Named("SQUIT"));},
665                b"KICK00000000" => if params_amount < 2 {return Err(CommandError::MinimumArgsRequired(2, cmd));}
666                                   else {return Ok(Self::Named("KICK"));},
667                b"CHGHOST00000" => if params_amount < 2 {return Err(CommandError::MinimumArgsRequired(2, cmd));}
668                                   else {return Ok(Self::Named("CHGHOST"));},
669                b"ENCAP0000000" => if params_amount < 2 {return Err(CommandError::MinimumArgsRequired(2, cmd));}
670                                   else {return Ok(Self::Named("ENCAP"));},
671                b"SQUERY000000" => if params_amount < 2 {return Err(CommandError::MinimumArgsRequired(2, cmd));}
672                                   else {return Ok(Self::Named("SQUERY"));},
673                b"METADATA0000" => if params_amount < 2 {return Err(CommandError::MinimumArgsRequired(2, cmd));}
674                                   else {return Ok(Self::Named("METADATA"));},
675                b"FAIL00000000" => if params_amount < 3 {return Err(CommandError::MinimumArgsRequired(3, cmd));}
676                                   else {return Ok(Self::Named("FAIL"));},
677                b"WARN00000000" => if params_amount < 3 {return Err(CommandError::MinimumArgsRequired(3, cmd));}
678                                   else {return Ok(Self::Named("WARN"));},
679                b"NOTE00000000" => if params_amount < 3 {return Err(CommandError::MinimumArgsRequired(3, cmd));}
680                                   else {return Ok(Self::Named("NOTE"));},
681                b"CPRIVMSG0000" => if params_amount < 3 {return Err(CommandError::MinimumArgsRequired(3, cmd));}
682                                   else {return Ok(Self::Named("CPRIVMSG"));},
683                b"CNOTICE00000" => if params_amount < 3 {return Err(CommandError::MinimumArgsRequired(3, cmd));}
684                                   else {return Ok(Self::Named("CNOTICE"));},
685                b"SERVER000000" => if params_amount < 3 {return Err(CommandError::MinimumArgsRequired(3, cmd));}
686                                   else {return Ok(Self::Named("SERVER"));},
687                b"USER00000000" => if params_amount < 4 {return Err(CommandError::MinimumArgsRequired(4, cmd));}
688                                   else {return Ok(Self::Named("USER"));},
689                b"WEBIRC000000" => if params_amount < 4 {return Err(CommandError::MinimumArgsRequired(4, cmd));}
690                                   else {return Ok(Self::Named("WEBIRC"));},
691                b"SERVICE00000" => if params_amount < 6 {return Err(CommandError::MinimumArgsRequired(6, cmd));}
692                                   else {return Ok(Self::Named("SERVICE"));},
693                _ => return Err(CommandError::UnhandledNamed(cmd)),
694            }
695        }
696        unreachable!();
697    }
698}
699
700impl<'msg> core::fmt::Display for Command<'msg> {
701    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
702        match self {Self::Named(inner) | Self::Numeric(inner) => write!(f, "{inner}")}
703    }
704}
705
706/// The possible types of errors when parsing [`Command`].
707#[derive(Clone, Copy, Debug, Eq, PartialEq)]
708pub enum CommandError<'msg> {
709    /// The byte slice input is empty.
710    EmptyInput,
711    /// Use of an invalid byte when parsing [`Command`].
712    InvalidByte(u8),
713    /// A [`Command`] cannot be a mixture of numbers and letters.
714    NumberInNamedCommand(&'msg str),
715    /// The minimum required number of arguments for the specific [`Command`].
716    MinimumArgsRequired(u8, &'msg str),
717    /// A `Numeric` [`Command`] not currently supported by this parser.
718    UnhandledNumeric(&'msg str),
719    /// A `Named` [`Command`] not currently supported by this parser.
720    UnhandledNamed(&'msg str),
721}
722
723const fn is_invalid_char(input: u8) -> bool {
724    !input.is_ascii_alphanumeric()
725}
726
727const fn command_to_uppercase_bytes(input: &[u8]) -> [u8; 12] {
728    let mut output = [b'0'; 12];
729    let mut index = 0;
730    while index < input.len() {
731        if input[index].is_ascii_lowercase() {output[index] = input[index].to_ascii_uppercase();}
732        else {output[index] = input[index];}
733        index += 1;
734    }
735    output
736}
737
738#[cfg(test)]
739mod const_tests {
740    use crate::is_identical;
741    use super::{Command, command_to_uppercase_bytes};
742    #[test]
743    const fn parsing_command() {
744        assert!(Command::parse(b"302", 1).is_ok());
745        assert!(Command::parse(b"907", 2).is_ok());
746        assert!(Command::parse(b"908", 3).is_ok());
747        assert!(Command::parse(b"900", 4).is_ok());
748        assert!(Command::parse(b"696", 5).is_ok());
749        assert!(Command::parse(b"314", 6).is_ok());
750        assert!(Command::parse(b"709", 7).is_ok());
751        assert!(Command::parse(b"352", 8).is_ok());
752        assert!(Command::parse(b"3027", 1).is_err());
753        assert!(Command::parse(b"000", 1).is_err());
754        assert!(Command::parse(b"info", 0).is_ok());
755        assert!(Command::parse(b"LuSeRS", 0).is_ok());
756        assert!(Command::parse(b"REHASH", 0).is_ok());
757        assert!(Command::parse(b"RESTART", 0).is_ok());
758        assert!(Command::parse(b"LINKs", 0).is_ok());
759        assert!(Command::parse(b"QUIT", 0).is_ok());
760        assert!(Command::parse(b"MOTD", 0).is_ok());
761        assert!(Command::parse(b"VERSION", 0).is_ok());
762        assert!(Command::parse(b"ADMIN", 0).is_ok());
763        assert!(Command::parse(b"TIME", 0).is_ok());
764        assert!(Command::parse(b"HELP", 0).is_ok());
765        assert!(Command::parse(b"AWAY", 0).is_ok());
766        assert!(Command::parse(b"LIST", 0).is_ok());
767        assert!(Command::parse(b"ACK", 0).is_ok());
768        assert!(Command::parse(b"ACCEPT", 0).is_ok());
769        assert!(Command::parse(b"SILENCE", 0).is_ok());
770        assert!(Command::parse(b"DIE", 0).is_ok());
771        assert!(Command::parse(b"TRACE", 0).is_ok());
772        assert!(Command::parse(b"ETRACE", 0).is_ok());
773        assert!(Command::parse(b"SERVLIST", 0).is_ok());
774        assert!(Command::parse(b"USERS", 0).is_ok());
775        assert!(Command::parse(b"MAP", 0).is_ok());
776        assert!(Command::parse(b"PASS", 1).is_ok());
777        assert!(Command::parse(b"PASS", 0).is_err());
778        assert!(Command::parse(b"NICK", 1).is_ok());
779        assert!(Command::parse(b"NICK", 0).is_err());
780        assert!(Command::parse(b"PING", 1).is_ok());
781        assert!(Command::parse(b"PING", 0).is_err());
782        assert!(Command::parse(b"ERROR", 1).is_ok());
783        assert!(Command::parse(b"ERROR", 0).is_err());
784        assert!(Command::parse(b"NAMES", 1).is_ok());
785        assert!(Command::parse(b"NAMES", 0).is_err());
786        assert!(Command::parse(b"WHO", 1).is_ok());
787        assert!(Command::parse(b"WHO", 0).is_err());
788        assert!(Command::parse(b"WALLOPS", 1).is_ok());
789        assert!(Command::parse(b"WALLOPS", 0).is_err());
790        assert!(Command::parse(b"AUTHENTICATE", 1).is_ok());
791        assert!(Command::parse(b"AUTHENTICATE", 0).is_err());
792        assert!(Command::parse(b"ACCOUNT", 1).is_ok());
793        assert!(Command::parse(b"ACCOUNT", 0).is_err());
794        assert!(Command::parse(b"CAP", 1).is_ok());
795        assert!(Command::parse(b"CAP", 0).is_err());
796        assert!(Command::parse(b"MODE", 1).is_ok());
797        assert!(Command::parse(b"MODE", 0).is_err());
798        assert!(Command::parse(b"PONG", 1).is_ok());
799        assert!(Command::parse(b"PONG", 0).is_err());
800        assert!(Command::parse(b"JOIN", 1).is_ok());
801        assert!(Command::parse(b"JOIN", 0).is_err());
802        assert!(Command::parse(b"PART", 1).is_ok());
803        assert!(Command::parse(b"PART", 0).is_err());
804        assert!(Command::parse(b"TOPIC", 1).is_ok());
805        assert!(Command::parse(b"TOPIC", 0).is_err());
806        assert!(Command::parse(b"STATS", 1).is_ok());
807        assert!(Command::parse(b"STATS", 0).is_err());
808        assert!(Command::parse(b"WHOIS", 1).is_ok());
809        assert!(Command::parse(b"WHOIS", 0).is_err());
810        assert!(Command::parse(b"WHOWAS", 1).is_ok());
811        assert!(Command::parse(b"WHOWAS", 0).is_err());
812        assert!(Command::parse(b"CONNECT", 1).is_ok());
813        assert!(Command::parse(b"CONNECT", 0).is_err());
814        assert!(Command::parse(b"USERHOST", 1).is_ok());
815        assert!(Command::parse(b"USERHOST", 0).is_err());
816        assert!(Command::parse(b"TAGMSG", 1).is_ok());
817        assert!(Command::parse(b"TAGMSG", 0).is_err());
818        assert!(Command::parse(b"BATCH", 1).is_ok());
819        assert!(Command::parse(b"BATCH", 0).is_err());
820        assert!(Command::parse(b"SETNAME", 1).is_ok());
821        assert!(Command::parse(b"SETNAME", 0).is_err());
822        assert!(Command::parse(b"MONITOR", 1).is_ok());
823        assert!(Command::parse(b"MONITOR", 0).is_err());
824        assert!(Command::parse(b"ISON", 1).is_ok());
825        assert!(Command::parse(b"ISON", 0).is_err());
826        assert!(Command::parse(b"KNOCK", 1).is_ok());
827        assert!(Command::parse(b"KNOCK", 0).is_err());
828        assert!(Command::parse(b"SUMMON", 1).is_ok());
829        assert!(Command::parse(b"SUMMON", 0).is_err());
830        assert!(Command::parse(b"USERIP", 1).is_ok());
831        assert!(Command::parse(b"USERIP", 0).is_err());
832        assert!(Command::parse(b"WATCH", 1).is_ok());
833        assert!(Command::parse(b"WATCH", 0).is_err());
834        assert!(Command::parse(b"OPER", 2).is_ok());
835        assert!(Command::parse(b"OPER", 0).is_err());
836        assert!(Command::parse(b"INVITE", 2).is_ok());
837        assert!(Command::parse(b"INVITE", 0).is_err());
838        assert!(Command::parse(b"PRIVMSG", 2).is_ok());
839        assert!(Command::parse(b"PRIVMSG", 0).is_err());
840        assert!(Command::parse(b"NOTICE", 2).is_ok());
841        assert!(Command::parse(b"NOTICE", 0).is_err());
842        assert!(Command::parse(b"KILL", 2).is_ok());
843        assert!(Command::parse(b"KILL", 0).is_err());
844        assert!(Command::parse(b"SQUIT", 2).is_ok());
845        assert!(Command::parse(b"SQUIT", 0).is_err());
846        assert!(Command::parse(b"KICK", 2).is_ok());
847        assert!(Command::parse(b"KICK", 0).is_err());
848        assert!(Command::parse(b"CHGHOST", 2).is_ok());
849        assert!(Command::parse(b"CHGHOST", 0).is_err());
850        assert!(Command::parse(b"ENCAP", 2).is_ok());
851        assert!(Command::parse(b"ENCAP", 0).is_err());
852        assert!(Command::parse(b"SQUERY", 2).is_ok());
853        assert!(Command::parse(b"SQUERY", 0).is_err());
854        assert!(Command::parse(b"METADATA", 2).is_ok());
855        assert!(Command::parse(b"METADATA", 0).is_err());
856        assert!(Command::parse(b"FAIL", 3).is_ok());
857        assert!(Command::parse(b"FAIL", 0).is_err());
858        assert!(Command::parse(b"WARN", 3).is_ok());
859        assert!(Command::parse(b"WARN", 0).is_err());
860        assert!(Command::parse(b"NOTE", 3).is_ok());
861        assert!(Command::parse(b"NOTE", 0).is_err());
862        assert!(Command::parse(b"CPRIVMSG", 3).is_ok());
863        assert!(Command::parse(b"CPRIVMSG", 0).is_err());
864        assert!(Command::parse(b"CNOTICE", 3).is_ok());
865        assert!(Command::parse(b"CNOTICE", 0).is_err());
866        assert!(Command::parse(b"SERVER", 3).is_ok());
867        assert!(Command::parse(b"SERVER", 0).is_err());
868        assert!(Command::parse(b"USER", 4).is_ok());
869        assert!(Command::parse(b"USER", 0).is_err());
870        assert!(Command::parse(b"WEBIRC", 4).is_ok());
871        assert!(Command::parse(b"WEBIRC", 0).is_err());
872        assert!(Command::parse(b"SERVICE", 6).is_ok());
873        assert!(Command::parse(b"SERVICE", 0).is_err());
874        assert!(Command::parse(b"EXCELLENT", 0).is_err());
875    }
876    #[test]
877    const fn uppercasing() {
878        let input = b"INFO";
879        let output = command_to_uppercase_bytes(input);
880        assert!(output.len() == 12);
881        assert!(is_identical(&output, &[b'I', b'N', b'F', b'O', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0']));
882    }
883}