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}