Proof of concept mechanical port of ircnet/ircd to Rust as part of a bit about C being insecure for network services
0

Configure Feed

Select the types of activity you want to include in your feed.

ircd.rs / leveva / src / numeric.rs
47 kB 1149 lines
1//! IRC reply and error numerics (RFC 1459 / IRCnet 2.11). 2//! 3//! Every server-to-client numeric reply is identified by a three-digit code: by 4//! convention `001`–`399` are replies (`RPL_*`) and `400`–`599` are errors 5//! (`ERR_*`). On the wire a numeric is sent as `:server NNN target ...`, where 6//! `NNN` is the zero-padded three-digit code — exactly what [`Numeric`]'s 7//! [`Display`](core::fmt::Display) produces. 8//! 9//! The variant set is the complete table from `common/numeric_def.h` in the C 10//! oracle. Variant names are the canonical IRC symbol with the `RPL_`/`ERR_` 11//! prefix kept (so that `RPL_STATSKLINE` = 216 and `ERR_STATSKLINE` = 499 stay 12//! distinct) and the remainder title-cased: `RPL_NOSUCHNICK` becomes 13//! [`Numeric::RplWelcome`]-style `RplNosuchnick`. The original symbol is always 14//! recoverable via [`Numeric::name`]. 15//! 16//! ``` 17//! use leveva::Numeric; 18//! assert_eq!(Numeric::RplWelcome.code(), 1); 19//! assert_eq!(Numeric::RplWelcome.to_string(), "001"); 20//! assert_eq!(Numeric::ErrNicknameinuse.name(), "ERR_NICKNAMEINUSE"); 21//! assert!(Numeric::ErrNosuchnick.is_error()); 22//! assert_eq!(Numeric::try_from(433), Ok(Numeric::ErrNicknameinuse)); 23//! ``` 24 25/// An IRC reply or error numeric. 26/// 27/// The discriminant of each variant is its three-digit numeric code, so 28/// `numeric as u16` (or [`Numeric::code`]) yields the wire value directly. 29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 30#[repr(u16)] 31#[non_exhaustive] 32pub enum Numeric { 33 /// `RPL_WELCOME` 34 RplWelcome = 1, 35 /// `RPL_YOURHOST` 36 RplYourhost = 2, 37 /// `RPL_CREATED` 38 RplCreated = 3, 39 /// `RPL_MYINFO` 40 RplMyinfo = 4, 41 /// `RPL_ISUPPORT` 42 RplIsupport = 5, 43 /// `RPL_BOUNCE` 44 RplBounce = 10, 45 /// `RPL_MAP` 46 RplMap = 15, 47 /// `RPL_MAPEND` 48 RplMapend = 17, 49 /// `RPL_MAPSTART` 50 RplMapstart = 18, 51 /// `RPL_HELLO` 52 RplHello = 20, 53 /// `RPL_YOURID` 54 RplYourid = 42, 55 /// `RPL_SAVENICK` 56 RplSavenick = 43, 57 /// `RPL_TRACELINK` 58 RplTracelink = 200, 59 /// `RPL_TRACECONNECTING` 60 RplTraceconnecting = 201, 61 /// `RPL_TRACEHANDSHAKE` 62 RplTracehandshake = 202, 63 /// `RPL_TRACEUNKNOWN` 64 RplTraceunknown = 203, 65 /// `RPL_TRACEOPERATOR` 66 RplTraceoperator = 204, 67 /// `RPL_TRACEUSER` 68 RplTraceuser = 205, 69 /// `RPL_TRACESERVER` 70 RplTraceserver = 206, 71 /// `RPL_TRACESERVICE` 72 RplTraceservice = 207, 73 /// `RPL_TRACENEWTYPE` 74 RplTracenewtype = 208, 75 /// `RPL_TRACECLASS` 76 RplTraceclass = 209, 77 /// `RPL_STATSLINKINFO` 78 RplStatslinkinfo = 211, 79 /// `RPL_STATSCOMMANDS` 80 RplStatscommands = 212, 81 /// `RPL_STATSCLINE` 82 RplStatscline = 213, 83 /// `RPL_STATSNLINE` 84 RplStatsnline = 214, 85 /// `RPL_STATSILINE` 86 RplStatsiline = 215, 87 /// `RPL_STATSKLINE` 88 RplStatskline = 216, 89 /// `RPL_STATSQLINE` 90 RplStatsqline = 217, 91 /// `RPL_STATSYLINE` 92 RplStatsyline = 218, 93 /// `RPL_ENDOFSTATS` 94 RplEndofstats = 219, 95 /// `RPL_UMODEIS` 96 RplUmodeis = 221, 97 /// `RPL_SERVICEINFO` 98 RplServiceinfo = 231, 99 /// `RPL_ENDOFSERVICES` 100 RplEndofservices = 232, 101 /// `RPL_SERVICE` 102 RplService = 233, 103 /// `RPL_SERVLIST` 104 RplServlist = 234, 105 /// `RPL_SERVLISTEND` 106 RplServlistend = 235, 107 /// `RPL_STATSIAUTH` 108 RplStatsiauth = 239, 109 /// `RPL_STATSVLINE` 110 RplStatsvline = 240, 111 /// `RPL_STATSLLINE` 112 RplStatslline = 241, 113 /// `RPL_STATSUPTIME` 114 RplStatsuptime = 242, 115 /// `RPL_STATSOLINE` 116 RplStatsoline = 243, 117 /// `RPL_STATSHLINE` 118 RplStatshline = 244, 119 /// `RPL_STATSSLINE` 120 RplStatssline = 245, 121 /// `RPL_STATSPING` 122 RplStatsping = 246, 123 /// `RPL_STATSBLINE` 124 RplStatsbline = 247, 125 /// `RPL_STATSDEFINE` 126 RplStatsdefine = 248, 127 /// `RPL_STATSDEBUG` 128 RplStatsdebug = 249, 129 /// `RPL_STATSDLINE` 130 RplStatsdline = 250, 131 /// `RPL_LUSERCLIENT` 132 RplLuserclient = 251, 133 /// `RPL_LUSEROP` 134 RplLuserop = 252, 135 /// `RPL_LUSERUNKNOWN` 136 RplLuserunknown = 253, 137 /// `RPL_LUSERCHANNELS` 138 RplLuserchannels = 254, 139 /// `RPL_LUSERME` 140 RplLuserme = 255, 141 /// `RPL_ADMINME` 142 RplAdminme = 256, 143 /// `RPL_ADMINLOC1` 144 RplAdminloc1 = 257, 145 /// `RPL_ADMINLOC2` 146 RplAdminloc2 = 258, 147 /// `RPL_ADMINEMAIL` 148 RplAdminemail = 259, 149 /// `RPL_TRACEEND` 150 RplTraceend = 262, 151 /// `RPL_TRYAGAIN` 152 RplTryagain = 263, 153 /// `RPL_LOCALUSERS` 154 RplLocalusers = 265, 155 /// `RPL_GLOBALUSERS` 156 RplGlobalusers = 266, 157 /// `RPL_WHOISCERTFP` (from oftc-hybrid / charybdis): a WHOIS line carrying the 158 /// target's client-certificate fingerprint (CertFP, P11 slice 78). 159 RplWhoiscertfp = 276, 160 /// `RPL_NONE` 161 RplNone = 300, 162 /// `RPL_AWAY` 163 RplAway = 301, 164 /// `RPL_USERHOST` 165 RplUserhost = 302, 166 /// `RPL_ISON` 167 RplIson = 303, 168 /// `RPL_TEXT` 169 RplText = 304, 170 /// `RPL_UNAWAY` 171 RplUnaway = 305, 172 /// `RPL_NOWAWAY` 173 RplNowaway = 306, 174 /// `RPL_WHOISUSER` 175 RplWhoisuser = 311, 176 /// `RPL_WHOISSERVER` 177 RplWhoisserver = 312, 178 /// `RPL_WHOISOPERATOR` 179 RplWhoisoperator = 313, 180 /// `RPL_WHOWASUSER` 181 RplWhowasuser = 314, 182 /// `RPL_ENDOFWHO` 183 RplEndofwho = 315, 184 /// `RPL_WHOISCHANOP` — redundant and not needed but reserved 185 RplWhoischanop = 316, 186 /// `RPL_WHOISIDLE` 187 RplWhoisidle = 317, 188 /// `RPL_ENDOFWHOIS` 189 RplEndofwhois = 318, 190 /// `RPL_WHOISCHANNELS` 191 RplWhoischannels = 319, 192 /// `RPL_WHOISEXTRA` — catch-all extra WHOIS information reply -- mh 2020-01-11 193 RplWhoisextra = 320, 194 /// `RPL_WHOISACCOUNT` — the target is logged in to a services account (leveva-native, no 195 /// C oracle; the de-facto charybdis/ratbox code). `<nick> <account> :is logged in as`. 196 RplWhoisaccount = 330, 197 /// `RPL_WHOISBOT` — IRCv3 Bot Mode: the target is a bot (leveva-native, no C oracle). 198 RplWhoisbot = 335, 199 /// `RPL_LISTSTART` 200 RplListstart = 321, 201 /// `RPL_LIST` 202 RplList = 322, 203 /// `RPL_LISTEND` 204 RplListend = 323, 205 /// `RPL_ACCEPTLIST` — caller-id (`+g`): one entry of the client's `ACCEPT` list, 206 /// `<nick>` (leveva-native; the charybdis/Hybrid convention, slice 216). 207 RplAcceptlist = 281, 208 /// `RPL_ENDOFACCEPT` — caller-id: terminates an `ACCEPT` list reply, 209 /// `:End of /ACCEPT list.` (slice 216). 210 RplEndofaccept = 282, 211 /// `RPL_CHANNELMODEIS` 212 RplChannelmodeis = 324, 213 /// `RPL_UNIQOPIS` 214 RplUniqopis = 325, 215 /// `RPL_NOTOPIC` 216 RplNotopic = 331, 217 /// `RPL_TOPIC` 218 RplTopic = 332, 219 /// `RPL_TOPIC_WHO_TIME` 220 RplTopicWhoTime = 333, 221 /// `RPL_INVITING` 222 RplInviting = 341, 223 /// `RPL_SUMMONING` 224 RplSummoning = 342, 225 /// `RPL_REOPLIST` 226 RplReoplist = 344, 227 /// `RPL_ENDOFREOPLIST` 228 RplEndofreoplist = 345, 229 /// `RPL_INVITELIST` 230 RplInvitelist = 346, 231 /// `RPL_ENDOFINVITELIST` 232 RplEndofinvitelist = 347, 233 /// `RPL_EXCEPTLIST` 234 RplExceptlist = 348, 235 /// `RPL_ENDOFEXCEPTLIST` 236 RplEndofexceptlist = 349, 237 /// `RPL_VERSION` 238 RplVersion = 351, 239 /// `RPL_WHOREPLY` 240 RplWhoreply = 352, 241 /// `RPL_NAMREPLY` 242 RplNamreply = 353, 243 /// `RPL_WHOSPCRPL` 244 RplWhospcrpl = 354, 245 /// `RPL_KILLDONE` 246 RplKilldone = 361, 247 /// `RPL_CLOSING` 248 RplClosing = 362, 249 /// `RPL_CLOSEEND` 250 RplCloseend = 363, 251 /// `RPL_LINKS` 252 RplLinks = 364, 253 /// `RPL_ENDOFLINKS` 254 RplEndoflinks = 365, 255 /// `RPL_ENDOFNAMES` 256 RplEndofnames = 366, 257 /// `RPL_BANLIST` 258 RplBanlist = 367, 259 /// `RPL_ENDOFBANLIST` 260 RplEndofbanlist = 368, 261 /// `RPL_ENDOFWHOWAS` 262 RplEndofwhowas = 369, 263 /// `RPL_INFO` 264 RplInfo = 371, 265 /// `RPL_MOTD` 266 RplMotd = 372, 267 /// `RPL_INFOSTART` 268 RplInfostart = 373, 269 /// `RPL_ENDOFINFO` 270 RplEndofinfo = 374, 271 /// `RPL_MOTDSTART` 272 RplMotdstart = 375, 273 /// `RPL_ENDOFMOTD` 274 RplEndofmotd = 376, 275 /// `RPL_WHOISMODES` — WHOIS reports the target's user-mode string. leveva-native: the 276 /// IRCnet 2.11 oracle has no such numeric; `379` is the de-facto code used by 277 /// charybdis/ratbox/InspIRCd. Privacy-gated to the target themselves or an operator. 278 RplWhoismodes = 379, 279 /// `RPL_YOUREOPER` 280 RplYoureoper = 381, 281 /// `RPL_REHASHING` 282 RplRehashing = 382, 283 /// `RPL_YOURESERVICE` 284 RplYoureservice = 383, 285 /// `RPL_MYPORTIS` 286 RplMyportis = 384, 287 /// `RPL_NOTOPERANYMORE` 288 RplNotoperanymore = 385, 289 /// `RPL_TIME` 290 RplTime = 391, 291 /// `RPL_USERSSTART` 292 RplUsersstart = 392, 293 /// `RPL_USERS` 294 RplUsers = 393, 295 /// `RPL_ENDOFUSERS` 296 RplEndofusers = 394, 297 /// `RPL_NOUSERS` 298 RplNousers = 395, 299 /// `RPL_HOSTHIDDEN` — confirms a client's host is now cloaked (`+x`). 300 RplHostHidden = 396, 301 /// `ERR_NOSUCHNICK` 302 ErrNosuchnick = 401, 303 /// `ERR_NOSUCHSERVER` 304 ErrNosuchserver = 402, 305 /// `ERR_NOSUCHCHANNEL` 306 ErrNosuchchannel = 403, 307 /// `ERR_CANNOTSENDTOCHAN` 308 ErrCannotsendtochan = 404, 309 /// `ERR_TOOMANYCHANNELS` 310 ErrToomanychannels = 405, 311 /// `ERR_WASNOSUCHNICK` 312 ErrWasnosuchnick = 406, 313 /// `ERR_TOOMANYTARGETS` 314 ErrToomanytargets = 407, 315 /// `ERR_NOSUCHSERVICE` 316 ErrNosuchservice = 408, 317 /// `ERR_NOORIGIN` 318 ErrNoorigin = 409, 319 /// `ERR_INVALIDCAPCMD` (IRCv3 capability negotiation) 320 ErrInvalidcapcmd = 410, 321 /// `ERR_NORECIPIENT` 322 ErrNorecipient = 411, 323 /// `ERR_NOTEXTTOSEND` 324 ErrNotexttosend = 412, 325 /// `ERR_NOTOPLEVEL` 326 ErrNotoplevel = 413, 327 /// `ERR_WILDTOPLEVEL` 328 ErrWildtoplevel = 414, 329 /// `ERR_BADMASK` 330 ErrBadmask = 415, 331 /// `ERR_TOOMANYMATCHES` 332 ErrToomanymatches = 416, 333 /// `ERR_INPUTTOOLONG` (IRCv3 message-tags: tag data over the 8191-byte limit) 334 ErrInputtoolong = 417, 335 /// `ERR_UNKNOWNCOMMAND` 336 ErrUnknowncommand = 421, 337 /// `ERR_NOMOTD` 338 ErrNomotd = 422, 339 /// `ERR_NOADMININFO` 340 ErrNoadmininfo = 423, 341 /// `ERR_FILEERROR` 342 ErrFileerror = 424, 343 /// `ERR_NONICKNAMEGIVEN` 344 ErrNonicknamegiven = 431, 345 /// `ERR_ERRONEOUSNICKNAME` 346 ErrErroneousnickname = 432, 347 /// `ERR_NICKNAMEINUSE` 348 ErrNicknameinuse = 433, 349 /// `ERR_SERVICENAMEINUSE` 350 ErrServicenameinuse = 434, 351 /// `ERR_SERVICECONFUSED` 352 ErrServiceconfused = 435, 353 /// `ERR_NICKCOLLISION` 354 ErrNickcollision = 436, 355 /// `ERR_UNAVAILRESOURCE` 356 ErrUnavailresource = 437, 357 /// `ERR_USERNOTINCHANNEL` 358 ErrUsernotinchannel = 441, 359 /// `ERR_NOTONCHANNEL` 360 ErrNotonchannel = 442, 361 /// `ERR_USERONCHANNEL` 362 ErrUseronchannel = 443, 363 /// `ERR_NOLOGIN` 364 ErrNologin = 444, 365 /// `ERR_SUMMONDISABLED` 366 ErrSummondisabled = 445, 367 /// `ERR_USERSDISABLED` 368 ErrUsersdisabled = 446, 369 /// `ERR_NOTREGISTERED` 370 ErrNotregistered = 451, 371 /// `ERR_ACCEPTFULL` — caller-id: the `ACCEPT` list is at its limit, 372 /// `:Accept list is full` (leveva-native; the charybdis convention, slice 216). 373 ErrAcceptfull = 456, 374 /// `ERR_ACCEPTEXIST` — caller-id: the nick is already on the `ACCEPT` list, 375 /// `<nick> :is already on your accept list.` (slice 216). 376 ErrAcceptexist = 457, 377 /// `ERR_ACCEPTNOT` — caller-id: the nick is not on the `ACCEPT` list (a remove that missed), 378 /// `<nick> :is not on your accept list.` (slice 216). 379 ErrAcceptnot = 458, 380 /// `ERR_NEEDMOREPARAMS` 381 ErrNeedmoreparams = 461, 382 /// `ERR_ALREADYREGISTRED` 383 ErrAlreadyregistred = 462, 384 /// `ERR_NOPERMFORHOST` 385 ErrNopermforhost = 463, 386 /// `ERR_PASSWDMISMATCH` 387 ErrPasswdmismatch = 464, 388 /// `ERR_YOUREBANNEDCREEP` 389 ErrYourebannedcreep = 465, 390 /// `ERR_YOUWILLBEBANNED` 391 ErrYouwillbebanned = 466, 392 /// `ERR_KEYSET` 393 ErrKeyset = 467, 394 /// `ERR_CHANNELISFULL` 395 ErrChannelisfull = 471, 396 /// `ERR_UNKNOWNMODE` 397 ErrUnknownmode = 472, 398 /// `ERR_INVITEONLYCHAN` 399 ErrInviteonlychan = 473, 400 /// `ERR_BANNEDFROMCHAN` 401 ErrBannedfromchan = 474, 402 /// `ERR_BADCHANNELKEY` 403 ErrBadchannelkey = 475, 404 /// `ERR_BADCHANMASK` 405 ErrBadchanmask = 476, 406 /// `ERR_NOCHANMODES` 407 ErrNochanmodes = 477, 408 /// `ERR_BANLISTFULL` 409 ErrBanlistfull = 478, 410 /// `ERR_NOPRIVILEGES` 411 ErrNoprivileges = 481, 412 /// `ERR_CHANOPRIVSNEEDED` 413 ErrChanoprivsneeded = 482, 414 /// `ERR_CANTKILLSERVER` 415 ErrCantkillserver = 483, 416 /// `ERR_RESTRICTED` 417 ErrRestricted = 484, 418 /// `ERR_UNIQOPRIVSNEEDED` 419 ErrUniqoprivsneeded = 485, 420 /// `ERR_NOOPERHOST` 421 ErrNooperhost = 491, 422 /// `ERR_NOSERVICEHOST` 423 ErrNoservicehost = 492, 424 /// `ERR_STATSKLINE` 425 ErrStatskline = 499, 426 /// `ERR_UMODEUNKNOWNFLAG` 427 ErrUmodeunknownflag = 501, 428 /// `ERR_USERSDONTMATCH` 429 ErrUsersdontmatch = 502, 430 /// `ERR_CANTJOINOPERSONLY` — JOIN of an oper-only (`+O`) channel by a non-oper 431 /// (leveva-native; the InspIRCd/charybdis convention). 432 ErrCantjoinopersonly = 520, 433 /// `RPL_ETRACEFULL` 434 RplEtracefull = 708, 435 /// `RPL_KNOCK` — delivered to a channel's operators when a non-member `KNOCK`s on a 436 /// restricted (`+i`/`+k`/`+l`) channel: `<channel> <nick>!<user>@<host> :<reason>` 437 /// (leveva-native; the charybdis/ratbox convention, slice 198). 438 RplKnock = 710, 439 /// `RPL_KNOCKDLVR` — acknowledges to the knocker that the `KNOCK` was delivered: 440 /// `<channel> :Your KNOCK has been delivered` (slice 198). 441 RplKnockdlvr = 711, 442 /// `ERR_TOOMANYKNOCK` — `KNOCK`ing a channel still within its per-channel flood cooldown: 443 /// `<channel> :Too many KNOCKs (<channel>)` (leveva-native; the charybdis convention, slice 444 /// 200). See [`crate::knock_throttle`]. 445 ErrToomanyknock = 712, 446 /// `ERR_CHANOPEN` — `KNOCK`ing on a channel that imposes no join restriction (you could 447 /// just `JOIN`): `<channel> :Channel is open` (slice 198). 448 ErrChanopen = 713, 449 /// `ERR_KNOCKONCHAN` — `KNOCK`ing on a channel you are already on: 450 /// `<channel> :You are already on that channel` (slice 198). 451 ErrKnockonchan = 714, 452 /// `ERR_TARGUMODEG` — caller-id: the PRIVMSG target is in `+g` and you are not on its accept 453 /// list, `<nick> :is in +g mode (server-side ignore.)` (leveva-native; the charybdis 454 /// convention, slice 216). 455 ErrTargumodeg = 716, 456 /// `RPL_TARGNOTIFY` — caller-id: confirms to the sender that the `+g` target was told you 457 /// messaged them, `<nick> :has been informed that you messaged them.` (slice 216). 458 RplTargnotify = 717, 459 /// `RPL_UMODEGMSG` — caller-id: tells a `+g` user that someone (not on their accept list) 460 /// tried to message them, `<nick> <user>@<host> :is messaging you, and you have umode +g.` 461 /// (slice 216). 462 RplUmodegmsg = 718, 463 /// `RPL_MONONLINE` — a monitored target is online (IRCv3 `monitor`). 464 RplMononline = 730, 465 /// `RPL_MONOFFLINE` — a monitored target is offline (IRCv3 `monitor`). 466 RplMonoffline = 731, 467 /// `RPL_MONLIST` — a chunk of the client's monitor list (IRCv3 `monitor`). 468 RplMonlist = 732, 469 /// `RPL_ENDOFMONLIST` — terminates a `MONITOR L` reply (IRCv3 `monitor`). 470 RplEndofmonlist = 733, 471 /// `ERR_MONLISTFULL` — the monitor list is full; the add failed (IRCv3 `monitor`). 472 ErrMonlistfull = 734, 473 /// `RPL_ETRACEEND` 474 RplEtraceend = 759, 475 /// `RPL_KEYVALUE` — IRCv3 metadata: a key's value on a target (slice 163). 476 /// `<client> <target> <key> <visibility> :<value>`. 477 RplKeyvalue = 761, 478 /// `RPL_KEYNOTSET` — IRCv3 metadata: a key has no value on a target (slice 163). 479 /// `<client> <target> <key> :key not set`. 480 RplKeynotset = 766, 481 /// `RPL_METADATASUBOK` — IRCv3 metadata: keys successfully subscribed to (slice 163). 482 /// `<client> <key1> [<key2> ...]`. 483 RplMetadatasubok = 770, 484 /// `RPL_METADATAUNSUBOK` — IRCv3 metadata: keys successfully unsubscribed from (slice 163). 485 /// `<client> <key1> [<key2> ...]`. 486 RplMetadataunsubok = 771, 487 /// `RPL_METADATASUBS` — IRCv3 metadata: the client's current key subscriptions (slice 163). 488 /// `<client> <key1> [<key2> ...]`. 489 RplMetadatasubs = 772, 490 /// `RPL_LOGGEDIN` — SASL: the client is now logged in to a services account (IRCv3 SASL). 491 /// `<nick> <nick>!<user>@<host> <account> :You are now logged in as <account>`. 492 RplLoggedin = 900, 493 /// `RPL_LOGGEDOUT` — SASL: the client is now logged out (IRCv3 SASL). 494 RplLoggedout = 901, 495 /// `ERR_NICKLOCKED` — SASL: the account requires a specific (assigned) nick (IRCv3 SASL). 496 ErrNicklocked = 902, 497 /// `RPL_SASLSUCCESS` — SASL authentication succeeded (IRCv3 SASL). 498 RplSaslsuccess = 903, 499 /// `ERR_SASLFAIL` — SASL authentication failed (IRCv3 SASL). 500 ErrSaslfail = 904, 501 /// `ERR_SASLTOOLONG` — a SASL `AUTHENTICATE` payload exceeded the size limit (IRCv3 SASL). 502 ErrSasltoolong = 905, 503 /// `ERR_SASLABORTED` — the client aborted SASL with `AUTHENTICATE *` (IRCv3 SASL). 504 ErrSaslaborted = 906, 505 /// `ERR_SASLALREADY` — the client has already authenticated via SASL (IRCv3 SASL). 506 ErrSaslalready = 907, 507 /// `RPL_SASLMECHS` — the list of available SASL mechanisms (IRCv3 SASL). 508 /// `<nick> <mechanisms> :are available SASL mechanisms`. 509 RplSaslmechs = 908, 510} 511 512impl Numeric { 513 /// `RPL_WHOISTLS` — client is connected with SSL/TLS -- mh 2020-04-27. 514 /// 515 /// An alias for [`Numeric::RplWhoisextra`]: the C oracle defines 516 /// `#define RPL_WHOISTLS RPL_WHOISEXTRA`, so both names share code 517 /// 320. 518 pub const RPL_WHOISTLS: Numeric = Numeric::RplWhoisextra; 519 520 /// The three-digit numeric code (this variant's discriminant). 521 #[inline] 522 pub const fn code(self) -> u16 { 523 self as u16 524 } 525 526 /// `true` if this is an error numeric. 527 /// 528 /// By IRC convention codes in the range `400`–`599` are errors (`ERR_*`); 529 /// everything else is an informational reply (`RPL_*`). 530 #[inline] 531 pub const fn is_error(self) -> bool { 532 let c = self as u16; 533 c >= 400 && c < 600 534 } 535 536 /// The canonical IRC symbol for this numeric, e.g. `"RPL_WELCOME"`. 537 pub const fn name(self) -> &'static str { 538 match self { 539 Numeric::RplWelcome => "RPL_WELCOME", 540 Numeric::RplYourhost => "RPL_YOURHOST", 541 Numeric::RplCreated => "RPL_CREATED", 542 Numeric::RplMyinfo => "RPL_MYINFO", 543 Numeric::RplIsupport => "RPL_ISUPPORT", 544 Numeric::RplBounce => "RPL_BOUNCE", 545 Numeric::RplMap => "RPL_MAP", 546 Numeric::RplMapend => "RPL_MAPEND", 547 Numeric::RplMapstart => "RPL_MAPSTART", 548 Numeric::RplHello => "RPL_HELLO", 549 Numeric::RplYourid => "RPL_YOURID", 550 Numeric::RplSavenick => "RPL_SAVENICK", 551 Numeric::RplTracelink => "RPL_TRACELINK", 552 Numeric::RplTraceconnecting => "RPL_TRACECONNECTING", 553 Numeric::RplTracehandshake => "RPL_TRACEHANDSHAKE", 554 Numeric::RplTraceunknown => "RPL_TRACEUNKNOWN", 555 Numeric::RplTraceoperator => "RPL_TRACEOPERATOR", 556 Numeric::RplTraceuser => "RPL_TRACEUSER", 557 Numeric::RplTraceserver => "RPL_TRACESERVER", 558 Numeric::RplTraceservice => "RPL_TRACESERVICE", 559 Numeric::RplTracenewtype => "RPL_TRACENEWTYPE", 560 Numeric::RplTraceclass => "RPL_TRACECLASS", 561 Numeric::RplStatslinkinfo => "RPL_STATSLINKINFO", 562 Numeric::RplStatscommands => "RPL_STATSCOMMANDS", 563 Numeric::RplStatscline => "RPL_STATSCLINE", 564 Numeric::RplStatsnline => "RPL_STATSNLINE", 565 Numeric::RplStatsiline => "RPL_STATSILINE", 566 Numeric::RplStatskline => "RPL_STATSKLINE", 567 Numeric::RplStatsqline => "RPL_STATSQLINE", 568 Numeric::RplStatsyline => "RPL_STATSYLINE", 569 Numeric::RplEndofstats => "RPL_ENDOFSTATS", 570 Numeric::RplUmodeis => "RPL_UMODEIS", 571 Numeric::RplServiceinfo => "RPL_SERVICEINFO", 572 Numeric::RplEndofservices => "RPL_ENDOFSERVICES", 573 Numeric::RplService => "RPL_SERVICE", 574 Numeric::RplServlist => "RPL_SERVLIST", 575 Numeric::RplServlistend => "RPL_SERVLISTEND", 576 Numeric::RplStatsiauth => "RPL_STATSIAUTH", 577 Numeric::RplStatsvline => "RPL_STATSVLINE", 578 Numeric::RplStatslline => "RPL_STATSLLINE", 579 Numeric::RplStatsuptime => "RPL_STATSUPTIME", 580 Numeric::RplStatsoline => "RPL_STATSOLINE", 581 Numeric::RplStatshline => "RPL_STATSHLINE", 582 Numeric::RplStatssline => "RPL_STATSSLINE", 583 Numeric::RplStatsping => "RPL_STATSPING", 584 Numeric::RplStatsbline => "RPL_STATSBLINE", 585 Numeric::RplStatsdefine => "RPL_STATSDEFINE", 586 Numeric::RplStatsdebug => "RPL_STATSDEBUG", 587 Numeric::RplStatsdline => "RPL_STATSDLINE", 588 Numeric::RplLuserclient => "RPL_LUSERCLIENT", 589 Numeric::RplLuserop => "RPL_LUSEROP", 590 Numeric::RplLuserunknown => "RPL_LUSERUNKNOWN", 591 Numeric::RplLuserchannels => "RPL_LUSERCHANNELS", 592 Numeric::RplLuserme => "RPL_LUSERME", 593 Numeric::RplAdminme => "RPL_ADMINME", 594 Numeric::RplAdminloc1 => "RPL_ADMINLOC1", 595 Numeric::RplAdminloc2 => "RPL_ADMINLOC2", 596 Numeric::RplAdminemail => "RPL_ADMINEMAIL", 597 Numeric::RplTraceend => "RPL_TRACEEND", 598 Numeric::RplTryagain => "RPL_TRYAGAIN", 599 Numeric::RplLocalusers => "RPL_LOCALUSERS", 600 Numeric::RplGlobalusers => "RPL_GLOBALUSERS", 601 Numeric::RplNone => "RPL_NONE", 602 Numeric::RplAway => "RPL_AWAY", 603 Numeric::RplUserhost => "RPL_USERHOST", 604 Numeric::RplIson => "RPL_ISON", 605 Numeric::RplText => "RPL_TEXT", 606 Numeric::RplUnaway => "RPL_UNAWAY", 607 Numeric::RplNowaway => "RPL_NOWAWAY", 608 Numeric::RplWhoisuser => "RPL_WHOISUSER", 609 Numeric::RplWhoisserver => "RPL_WHOISSERVER", 610 Numeric::RplWhoisoperator => "RPL_WHOISOPERATOR", 611 Numeric::RplWhowasuser => "RPL_WHOWASUSER", 612 Numeric::RplEndofwho => "RPL_ENDOFWHO", 613 Numeric::RplWhoischanop => "RPL_WHOISCHANOP", 614 Numeric::RplWhoisidle => "RPL_WHOISIDLE", 615 Numeric::RplEndofwhois => "RPL_ENDOFWHOIS", 616 Numeric::RplWhoischannels => "RPL_WHOISCHANNELS", 617 Numeric::RplWhoiscertfp => "RPL_WHOISCERTFP", 618 Numeric::RplWhoisextra => "RPL_WHOISEXTRA", 619 Numeric::RplWhoisaccount => "RPL_WHOISACCOUNT", 620 Numeric::RplWhoisbot => "RPL_WHOISBOT", 621 Numeric::RplListstart => "RPL_LISTSTART", 622 Numeric::RplList => "RPL_LIST", 623 Numeric::RplListend => "RPL_LISTEND", 624 Numeric::RplChannelmodeis => "RPL_CHANNELMODEIS", 625 Numeric::RplUniqopis => "RPL_UNIQOPIS", 626 Numeric::RplNotopic => "RPL_NOTOPIC", 627 Numeric::RplTopic => "RPL_TOPIC", 628 Numeric::RplTopicWhoTime => "RPL_TOPIC_WHO_TIME", 629 Numeric::RplInviting => "RPL_INVITING", 630 Numeric::RplSummoning => "RPL_SUMMONING", 631 Numeric::RplReoplist => "RPL_REOPLIST", 632 Numeric::RplEndofreoplist => "RPL_ENDOFREOPLIST", 633 Numeric::RplInvitelist => "RPL_INVITELIST", 634 Numeric::RplEndofinvitelist => "RPL_ENDOFINVITELIST", 635 Numeric::RplExceptlist => "RPL_EXCEPTLIST", 636 Numeric::RplEndofexceptlist => "RPL_ENDOFEXCEPTLIST", 637 Numeric::RplVersion => "RPL_VERSION", 638 Numeric::RplWhoreply => "RPL_WHOREPLY", 639 Numeric::RplNamreply => "RPL_NAMREPLY", 640 Numeric::RplWhospcrpl => "RPL_WHOSPCRPL", 641 Numeric::RplKilldone => "RPL_KILLDONE", 642 Numeric::RplClosing => "RPL_CLOSING", 643 Numeric::RplCloseend => "RPL_CLOSEEND", 644 Numeric::RplLinks => "RPL_LINKS", 645 Numeric::RplEndoflinks => "RPL_ENDOFLINKS", 646 Numeric::RplEndofnames => "RPL_ENDOFNAMES", 647 Numeric::RplBanlist => "RPL_BANLIST", 648 Numeric::RplEndofbanlist => "RPL_ENDOFBANLIST", 649 Numeric::RplEndofwhowas => "RPL_ENDOFWHOWAS", 650 Numeric::RplInfo => "RPL_INFO", 651 Numeric::RplMotd => "RPL_MOTD", 652 Numeric::RplInfostart => "RPL_INFOSTART", 653 Numeric::RplEndofinfo => "RPL_ENDOFINFO", 654 Numeric::RplMotdstart => "RPL_MOTDSTART", 655 Numeric::RplEndofmotd => "RPL_ENDOFMOTD", 656 Numeric::RplWhoismodes => "RPL_WHOISMODES", 657 Numeric::RplYoureoper => "RPL_YOUREOPER", 658 Numeric::RplRehashing => "RPL_REHASHING", 659 Numeric::RplYoureservice => "RPL_YOURESERVICE", 660 Numeric::RplMyportis => "RPL_MYPORTIS", 661 Numeric::RplNotoperanymore => "RPL_NOTOPERANYMORE", 662 Numeric::RplTime => "RPL_TIME", 663 Numeric::RplUsersstart => "RPL_USERSSTART", 664 Numeric::RplUsers => "RPL_USERS", 665 Numeric::RplEndofusers => "RPL_ENDOFUSERS", 666 Numeric::RplNousers => "RPL_NOUSERS", 667 Numeric::RplHostHidden => "RPL_HOSTHIDDEN", 668 Numeric::ErrNosuchnick => "ERR_NOSUCHNICK", 669 Numeric::ErrNosuchserver => "ERR_NOSUCHSERVER", 670 Numeric::ErrNosuchchannel => "ERR_NOSUCHCHANNEL", 671 Numeric::ErrCannotsendtochan => "ERR_CANNOTSENDTOCHAN", 672 Numeric::ErrToomanychannels => "ERR_TOOMANYCHANNELS", 673 Numeric::ErrWasnosuchnick => "ERR_WASNOSUCHNICK", 674 Numeric::ErrToomanytargets => "ERR_TOOMANYTARGETS", 675 Numeric::ErrNosuchservice => "ERR_NOSUCHSERVICE", 676 Numeric::ErrNoorigin => "ERR_NOORIGIN", 677 Numeric::ErrInvalidcapcmd => "ERR_INVALIDCAPCMD", 678 Numeric::ErrNorecipient => "ERR_NORECIPIENT", 679 Numeric::ErrNotexttosend => "ERR_NOTEXTTOSEND", 680 Numeric::ErrNotoplevel => "ERR_NOTOPLEVEL", 681 Numeric::ErrWildtoplevel => "ERR_WILDTOPLEVEL", 682 Numeric::ErrBadmask => "ERR_BADMASK", 683 Numeric::ErrToomanymatches => "ERR_TOOMANYMATCHES", 684 Numeric::ErrInputtoolong => "ERR_INPUTTOOLONG", 685 Numeric::ErrUnknowncommand => "ERR_UNKNOWNCOMMAND", 686 Numeric::ErrNomotd => "ERR_NOMOTD", 687 Numeric::ErrNoadmininfo => "ERR_NOADMININFO", 688 Numeric::ErrFileerror => "ERR_FILEERROR", 689 Numeric::ErrNonicknamegiven => "ERR_NONICKNAMEGIVEN", 690 Numeric::ErrErroneousnickname => "ERR_ERRONEOUSNICKNAME", 691 Numeric::ErrNicknameinuse => "ERR_NICKNAMEINUSE", 692 Numeric::ErrServicenameinuse => "ERR_SERVICENAMEINUSE", 693 Numeric::ErrServiceconfused => "ERR_SERVICECONFUSED", 694 Numeric::ErrNickcollision => "ERR_NICKCOLLISION", 695 Numeric::ErrUnavailresource => "ERR_UNAVAILRESOURCE", 696 Numeric::ErrUsernotinchannel => "ERR_USERNOTINCHANNEL", 697 Numeric::ErrNotonchannel => "ERR_NOTONCHANNEL", 698 Numeric::ErrUseronchannel => "ERR_USERONCHANNEL", 699 Numeric::ErrNologin => "ERR_NOLOGIN", 700 Numeric::ErrSummondisabled => "ERR_SUMMONDISABLED", 701 Numeric::ErrUsersdisabled => "ERR_USERSDISABLED", 702 Numeric::ErrNotregistered => "ERR_NOTREGISTERED", 703 Numeric::ErrNeedmoreparams => "ERR_NEEDMOREPARAMS", 704 Numeric::ErrAlreadyregistred => "ERR_ALREADYREGISTRED", 705 Numeric::ErrNopermforhost => "ERR_NOPERMFORHOST", 706 Numeric::ErrPasswdmismatch => "ERR_PASSWDMISMATCH", 707 Numeric::ErrYourebannedcreep => "ERR_YOUREBANNEDCREEP", 708 Numeric::ErrYouwillbebanned => "ERR_YOUWILLBEBANNED", 709 Numeric::ErrKeyset => "ERR_KEYSET", 710 Numeric::ErrChannelisfull => "ERR_CHANNELISFULL", 711 Numeric::ErrUnknownmode => "ERR_UNKNOWNMODE", 712 Numeric::ErrInviteonlychan => "ERR_INVITEONLYCHAN", 713 Numeric::ErrBannedfromchan => "ERR_BANNEDFROMCHAN", 714 Numeric::ErrBadchannelkey => "ERR_BADCHANNELKEY", 715 Numeric::ErrBadchanmask => "ERR_BADCHANMASK", 716 Numeric::ErrNochanmodes => "ERR_NOCHANMODES", 717 Numeric::ErrBanlistfull => "ERR_BANLISTFULL", 718 Numeric::ErrNoprivileges => "ERR_NOPRIVILEGES", 719 Numeric::ErrChanoprivsneeded => "ERR_CHANOPRIVSNEEDED", 720 Numeric::ErrCantkillserver => "ERR_CANTKILLSERVER", 721 Numeric::ErrRestricted => "ERR_RESTRICTED", 722 Numeric::ErrUniqoprivsneeded => "ERR_UNIQOPRIVSNEEDED", 723 Numeric::ErrNooperhost => "ERR_NOOPERHOST", 724 Numeric::ErrNoservicehost => "ERR_NOSERVICEHOST", 725 Numeric::ErrStatskline => "ERR_STATSKLINE", 726 Numeric::ErrUmodeunknownflag => "ERR_UMODEUNKNOWNFLAG", 727 Numeric::ErrUsersdontmatch => "ERR_USERSDONTMATCH", 728 Numeric::ErrCantjoinopersonly => "ERR_CANTJOINOPERSONLY", 729 Numeric::RplEtracefull => "RPL_ETRACEFULL", 730 Numeric::RplMononline => "RPL_MONONLINE", 731 Numeric::RplMonoffline => "RPL_MONOFFLINE", 732 Numeric::RplMonlist => "RPL_MONLIST", 733 Numeric::RplKnock => "RPL_KNOCK", 734 Numeric::RplKnockdlvr => "RPL_KNOCKDLVR", 735 Numeric::ErrToomanyknock => "ERR_TOOMANYKNOCK", 736 Numeric::ErrChanopen => "ERR_CHANOPEN", 737 Numeric::ErrKnockonchan => "ERR_KNOCKONCHAN", 738 Numeric::RplAcceptlist => "RPL_ACCEPTLIST", 739 Numeric::RplEndofaccept => "RPL_ENDOFACCEPT", 740 Numeric::ErrAcceptfull => "ERR_ACCEPTFULL", 741 Numeric::ErrAcceptexist => "ERR_ACCEPTEXIST", 742 Numeric::ErrAcceptnot => "ERR_ACCEPTNOT", 743 Numeric::ErrTargumodeg => "ERR_TARGUMODEG", 744 Numeric::RplTargnotify => "RPL_TARGNOTIFY", 745 Numeric::RplUmodegmsg => "RPL_UMODEGMSG", 746 Numeric::RplEndofmonlist => "RPL_ENDOFMONLIST", 747 Numeric::ErrMonlistfull => "ERR_MONLISTFULL", 748 Numeric::RplEtraceend => "RPL_ETRACEEND", 749 Numeric::RplKeyvalue => "RPL_KEYVALUE", 750 Numeric::RplKeynotset => "RPL_KEYNOTSET", 751 Numeric::RplMetadatasubok => "RPL_METADATASUBOK", 752 Numeric::RplMetadataunsubok => "RPL_METADATAUNSUBOK", 753 Numeric::RplMetadatasubs => "RPL_METADATASUBS", 754 Numeric::RplLoggedin => "RPL_LOGGEDIN", 755 Numeric::RplLoggedout => "RPL_LOGGEDOUT", 756 Numeric::ErrNicklocked => "ERR_NICKLOCKED", 757 Numeric::RplSaslsuccess => "RPL_SASLSUCCESS", 758 Numeric::ErrSaslfail => "ERR_SASLFAIL", 759 Numeric::ErrSasltoolong => "ERR_SASLTOOLONG", 760 Numeric::ErrSaslaborted => "ERR_SASLABORTED", 761 Numeric::ErrSaslalready => "ERR_SASLALREADY", 762 Numeric::RplSaslmechs => "RPL_SASLMECHS", 763 } 764 } 765} 766 767impl From<Numeric> for u16 { 768 #[inline] 769 fn from(n: Numeric) -> u16 { 770 n as u16 771 } 772} 773 774/// Error returned by [`Numeric::try_from`] when a code matches no known numeric. 775#[derive(Debug, Clone, Copy, PartialEq, Eq)] 776pub struct UnknownNumeric(pub u16); 777 778impl core::fmt::Display for UnknownNumeric { 779 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 780 write!(f, "unknown IRC numeric {}", self.0) 781 } 782} 783 784impl std::error::Error for UnknownNumeric {} 785 786impl TryFrom<u16> for Numeric { 787 type Error = UnknownNumeric; 788 789 fn try_from(code: u16) -> Result<Self, Self::Error> { 790 match code { 791 1 => Ok(Numeric::RplWelcome), 792 2 => Ok(Numeric::RplYourhost), 793 3 => Ok(Numeric::RplCreated), 794 4 => Ok(Numeric::RplMyinfo), 795 5 => Ok(Numeric::RplIsupport), 796 10 => Ok(Numeric::RplBounce), 797 15 => Ok(Numeric::RplMap), 798 17 => Ok(Numeric::RplMapend), 799 18 => Ok(Numeric::RplMapstart), 800 20 => Ok(Numeric::RplHello), 801 42 => Ok(Numeric::RplYourid), 802 43 => Ok(Numeric::RplSavenick), 803 200 => Ok(Numeric::RplTracelink), 804 201 => Ok(Numeric::RplTraceconnecting), 805 202 => Ok(Numeric::RplTracehandshake), 806 203 => Ok(Numeric::RplTraceunknown), 807 204 => Ok(Numeric::RplTraceoperator), 808 205 => Ok(Numeric::RplTraceuser), 809 206 => Ok(Numeric::RplTraceserver), 810 207 => Ok(Numeric::RplTraceservice), 811 208 => Ok(Numeric::RplTracenewtype), 812 209 => Ok(Numeric::RplTraceclass), 813 211 => Ok(Numeric::RplStatslinkinfo), 814 212 => Ok(Numeric::RplStatscommands), 815 213 => Ok(Numeric::RplStatscline), 816 214 => Ok(Numeric::RplStatsnline), 817 215 => Ok(Numeric::RplStatsiline), 818 216 => Ok(Numeric::RplStatskline), 819 217 => Ok(Numeric::RplStatsqline), 820 218 => Ok(Numeric::RplStatsyline), 821 219 => Ok(Numeric::RplEndofstats), 822 221 => Ok(Numeric::RplUmodeis), 823 231 => Ok(Numeric::RplServiceinfo), 824 232 => Ok(Numeric::RplEndofservices), 825 233 => Ok(Numeric::RplService), 826 234 => Ok(Numeric::RplServlist), 827 235 => Ok(Numeric::RplServlistend), 828 239 => Ok(Numeric::RplStatsiauth), 829 240 => Ok(Numeric::RplStatsvline), 830 241 => Ok(Numeric::RplStatslline), 831 242 => Ok(Numeric::RplStatsuptime), 832 243 => Ok(Numeric::RplStatsoline), 833 244 => Ok(Numeric::RplStatshline), 834 245 => Ok(Numeric::RplStatssline), 835 246 => Ok(Numeric::RplStatsping), 836 247 => Ok(Numeric::RplStatsbline), 837 248 => Ok(Numeric::RplStatsdefine), 838 249 => Ok(Numeric::RplStatsdebug), 839 250 => Ok(Numeric::RplStatsdline), 840 251 => Ok(Numeric::RplLuserclient), 841 252 => Ok(Numeric::RplLuserop), 842 253 => Ok(Numeric::RplLuserunknown), 843 254 => Ok(Numeric::RplLuserchannels), 844 255 => Ok(Numeric::RplLuserme), 845 256 => Ok(Numeric::RplAdminme), 846 257 => Ok(Numeric::RplAdminloc1), 847 258 => Ok(Numeric::RplAdminloc2), 848 259 => Ok(Numeric::RplAdminemail), 849 262 => Ok(Numeric::RplTraceend), 850 263 => Ok(Numeric::RplTryagain), 851 265 => Ok(Numeric::RplLocalusers), 852 266 => Ok(Numeric::RplGlobalusers), 853 300 => Ok(Numeric::RplNone), 854 301 => Ok(Numeric::RplAway), 855 302 => Ok(Numeric::RplUserhost), 856 303 => Ok(Numeric::RplIson), 857 304 => Ok(Numeric::RplText), 858 305 => Ok(Numeric::RplUnaway), 859 306 => Ok(Numeric::RplNowaway), 860 311 => Ok(Numeric::RplWhoisuser), 861 312 => Ok(Numeric::RplWhoisserver), 862 313 => Ok(Numeric::RplWhoisoperator), 863 314 => Ok(Numeric::RplWhowasuser), 864 315 => Ok(Numeric::RplEndofwho), 865 316 => Ok(Numeric::RplWhoischanop), 866 317 => Ok(Numeric::RplWhoisidle), 867 318 => Ok(Numeric::RplEndofwhois), 868 319 => Ok(Numeric::RplWhoischannels), 869 320 => Ok(Numeric::RplWhoisextra), 870 330 => Ok(Numeric::RplWhoisaccount), 871 335 => Ok(Numeric::RplWhoisbot), 872 321 => Ok(Numeric::RplListstart), 873 322 => Ok(Numeric::RplList), 874 323 => Ok(Numeric::RplListend), 875 324 => Ok(Numeric::RplChannelmodeis), 876 325 => Ok(Numeric::RplUniqopis), 877 331 => Ok(Numeric::RplNotopic), 878 332 => Ok(Numeric::RplTopic), 879 333 => Ok(Numeric::RplTopicWhoTime), 880 341 => Ok(Numeric::RplInviting), 881 342 => Ok(Numeric::RplSummoning), 882 344 => Ok(Numeric::RplReoplist), 883 345 => Ok(Numeric::RplEndofreoplist), 884 346 => Ok(Numeric::RplInvitelist), 885 347 => Ok(Numeric::RplEndofinvitelist), 886 348 => Ok(Numeric::RplExceptlist), 887 349 => Ok(Numeric::RplEndofexceptlist), 888 351 => Ok(Numeric::RplVersion), 889 352 => Ok(Numeric::RplWhoreply), 890 353 => Ok(Numeric::RplNamreply), 891 354 => Ok(Numeric::RplWhospcrpl), 892 361 => Ok(Numeric::RplKilldone), 893 362 => Ok(Numeric::RplClosing), 894 363 => Ok(Numeric::RplCloseend), 895 364 => Ok(Numeric::RplLinks), 896 365 => Ok(Numeric::RplEndoflinks), 897 366 => Ok(Numeric::RplEndofnames), 898 367 => Ok(Numeric::RplBanlist), 899 368 => Ok(Numeric::RplEndofbanlist), 900 369 => Ok(Numeric::RplEndofwhowas), 901 371 => Ok(Numeric::RplInfo), 902 372 => Ok(Numeric::RplMotd), 903 373 => Ok(Numeric::RplInfostart), 904 374 => Ok(Numeric::RplEndofinfo), 905 375 => Ok(Numeric::RplMotdstart), 906 376 => Ok(Numeric::RplEndofmotd), 907 381 => Ok(Numeric::RplYoureoper), 908 382 => Ok(Numeric::RplRehashing), 909 383 => Ok(Numeric::RplYoureservice), 910 384 => Ok(Numeric::RplMyportis), 911 385 => Ok(Numeric::RplNotoperanymore), 912 391 => Ok(Numeric::RplTime), 913 392 => Ok(Numeric::RplUsersstart), 914 393 => Ok(Numeric::RplUsers), 915 394 => Ok(Numeric::RplEndofusers), 916 395 => Ok(Numeric::RplNousers), 917 396 => Ok(Numeric::RplHostHidden), 918 401 => Ok(Numeric::ErrNosuchnick), 919 402 => Ok(Numeric::ErrNosuchserver), 920 403 => Ok(Numeric::ErrNosuchchannel), 921 404 => Ok(Numeric::ErrCannotsendtochan), 922 405 => Ok(Numeric::ErrToomanychannels), 923 406 => Ok(Numeric::ErrWasnosuchnick), 924 407 => Ok(Numeric::ErrToomanytargets), 925 408 => Ok(Numeric::ErrNosuchservice), 926 409 => Ok(Numeric::ErrNoorigin), 927 410 => Ok(Numeric::ErrInvalidcapcmd), 928 411 => Ok(Numeric::ErrNorecipient), 929 412 => Ok(Numeric::ErrNotexttosend), 930 413 => Ok(Numeric::ErrNotoplevel), 931 414 => Ok(Numeric::ErrWildtoplevel), 932 415 => Ok(Numeric::ErrBadmask), 933 416 => Ok(Numeric::ErrToomanymatches), 934 417 => Ok(Numeric::ErrInputtoolong), 935 421 => Ok(Numeric::ErrUnknowncommand), 936 422 => Ok(Numeric::ErrNomotd), 937 423 => Ok(Numeric::ErrNoadmininfo), 938 424 => Ok(Numeric::ErrFileerror), 939 431 => Ok(Numeric::ErrNonicknamegiven), 940 432 => Ok(Numeric::ErrErroneousnickname), 941 433 => Ok(Numeric::ErrNicknameinuse), 942 434 => Ok(Numeric::ErrServicenameinuse), 943 435 => Ok(Numeric::ErrServiceconfused), 944 436 => Ok(Numeric::ErrNickcollision), 945 437 => Ok(Numeric::ErrUnavailresource), 946 441 => Ok(Numeric::ErrUsernotinchannel), 947 442 => Ok(Numeric::ErrNotonchannel), 948 443 => Ok(Numeric::ErrUseronchannel), 949 444 => Ok(Numeric::ErrNologin), 950 445 => Ok(Numeric::ErrSummondisabled), 951 446 => Ok(Numeric::ErrUsersdisabled), 952 451 => Ok(Numeric::ErrNotregistered), 953 461 => Ok(Numeric::ErrNeedmoreparams), 954 462 => Ok(Numeric::ErrAlreadyregistred), 955 463 => Ok(Numeric::ErrNopermforhost), 956 464 => Ok(Numeric::ErrPasswdmismatch), 957 465 => Ok(Numeric::ErrYourebannedcreep), 958 466 => Ok(Numeric::ErrYouwillbebanned), 959 467 => Ok(Numeric::ErrKeyset), 960 471 => Ok(Numeric::ErrChannelisfull), 961 472 => Ok(Numeric::ErrUnknownmode), 962 473 => Ok(Numeric::ErrInviteonlychan), 963 474 => Ok(Numeric::ErrBannedfromchan), 964 475 => Ok(Numeric::ErrBadchannelkey), 965 476 => Ok(Numeric::ErrBadchanmask), 966 477 => Ok(Numeric::ErrNochanmodes), 967 478 => Ok(Numeric::ErrBanlistfull), 968 481 => Ok(Numeric::ErrNoprivileges), 969 482 => Ok(Numeric::ErrChanoprivsneeded), 970 483 => Ok(Numeric::ErrCantkillserver), 971 484 => Ok(Numeric::ErrRestricted), 972 485 => Ok(Numeric::ErrUniqoprivsneeded), 973 491 => Ok(Numeric::ErrNooperhost), 974 492 => Ok(Numeric::ErrNoservicehost), 975 499 => Ok(Numeric::ErrStatskline), 976 501 => Ok(Numeric::ErrUmodeunknownflag), 977 502 => Ok(Numeric::ErrUsersdontmatch), 978 520 => Ok(Numeric::ErrCantjoinopersonly), 979 708 => Ok(Numeric::RplEtracefull), 980 710 => Ok(Numeric::RplKnock), 981 711 => Ok(Numeric::RplKnockdlvr), 982 712 => Ok(Numeric::ErrToomanyknock), 983 713 => Ok(Numeric::ErrChanopen), 984 714 => Ok(Numeric::ErrKnockonchan), 985 281 => Ok(Numeric::RplAcceptlist), 986 282 => Ok(Numeric::RplEndofaccept), 987 456 => Ok(Numeric::ErrAcceptfull), 988 457 => Ok(Numeric::ErrAcceptexist), 989 458 => Ok(Numeric::ErrAcceptnot), 990 716 => Ok(Numeric::ErrTargumodeg), 991 717 => Ok(Numeric::RplTargnotify), 992 718 => Ok(Numeric::RplUmodegmsg), 993 730 => Ok(Numeric::RplMononline), 994 731 => Ok(Numeric::RplMonoffline), 995 732 => Ok(Numeric::RplMonlist), 996 733 => Ok(Numeric::RplEndofmonlist), 997 734 => Ok(Numeric::ErrMonlistfull), 998 759 => Ok(Numeric::RplEtraceend), 999 761 => Ok(Numeric::RplKeyvalue), 1000 766 => Ok(Numeric::RplKeynotset), 1001 770 => Ok(Numeric::RplMetadatasubok), 1002 771 => Ok(Numeric::RplMetadataunsubok), 1003 772 => Ok(Numeric::RplMetadatasubs), 1004 900 => Ok(Numeric::RplLoggedin), 1005 901 => Ok(Numeric::RplLoggedout), 1006 902 => Ok(Numeric::ErrNicklocked), 1007 903 => Ok(Numeric::RplSaslsuccess), 1008 904 => Ok(Numeric::ErrSaslfail), 1009 905 => Ok(Numeric::ErrSasltoolong), 1010 906 => Ok(Numeric::ErrSaslaborted), 1011 907 => Ok(Numeric::ErrSaslalready), 1012 908 => Ok(Numeric::RplSaslmechs), 1013 other => Err(UnknownNumeric(other)), 1014 } 1015 } 1016} 1017 1018impl core::fmt::Display for Numeric { 1019 /// Formats as the zero-padded three-digit wire code, e.g. `001`. 1020 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 1021 write!(f, "{:03}", *self as u16) 1022 } 1023} 1024 1025#[cfg(test)] 1026mod tests { 1027 use super::*; 1028 1029 #[test] 1030 fn code_is_discriminant() { 1031 assert_eq!(Numeric::RplWelcome.code(), 1); 1032 assert_eq!(Numeric::RplWelcome as u16, 1); 1033 assert_eq!(Numeric::ErrNicknameinuse.code(), 433); 1034 assert_eq!(u16::from(Numeric::ErrNosuchnick), 401); 1035 } 1036 1037 #[test] 1038 fn display_is_three_digit_padded() { 1039 assert_eq!(Numeric::RplWelcome.to_string(), "001"); 1040 assert_eq!(Numeric::RplBounce.to_string(), "010"); 1041 assert_eq!(Numeric::ErrNicknameinuse.to_string(), "433"); 1042 assert_eq!(Numeric::RplEtraceend.to_string(), "759"); 1043 } 1044 1045 #[test] 1046 fn name_round_trips_symbol() { 1047 assert_eq!(Numeric::RplWelcome.name(), "RPL_WELCOME"); 1048 assert_eq!(Numeric::ErrNosuchnick.name(), "ERR_NOSUCHNICK"); 1049 } 1050 1051 #[test] 1052 fn try_from_known_and_unknown() { 1053 assert_eq!(Numeric::try_from(1), Ok(Numeric::RplWelcome)); 1054 assert_eq!(Numeric::try_from(433), Ok(Numeric::ErrNicknameinuse)); 1055 assert_eq!(Numeric::try_from(999), Err(UnknownNumeric(999))); 1056 // round-trip every code through u16 and back 1057 for n in [ 1058 Numeric::RplWelcome, 1059 Numeric::RplStatskline, 1060 Numeric::ErrStatskline, 1061 Numeric::RplEtraceend, 1062 ] { 1063 assert_eq!(Numeric::try_from(n.code()), Ok(n)); 1064 } 1065 } 1066 1067 #[test] 1068 fn sasl_numerics_round_trip() { 1069 for (n, code, name) in [ 1070 (Numeric::RplLoggedin, 900u16, "RPL_LOGGEDIN"), 1071 (Numeric::RplLoggedout, 901, "RPL_LOGGEDOUT"), 1072 (Numeric::ErrNicklocked, 902, "ERR_NICKLOCKED"), 1073 (Numeric::RplSaslsuccess, 903, "RPL_SASLSUCCESS"), 1074 (Numeric::ErrSaslfail, 904, "ERR_SASLFAIL"), 1075 (Numeric::ErrSasltoolong, 905, "ERR_SASLTOOLONG"), 1076 (Numeric::ErrSaslaborted, 906, "ERR_SASLABORTED"), 1077 (Numeric::ErrSaslalready, 907, "ERR_SASLALREADY"), 1078 (Numeric::RplSaslmechs, 908, "RPL_SASLMECHS"), 1079 ] { 1080 assert_eq!(n.code(), code); 1081 assert_eq!(Numeric::try_from(code), Ok(n)); 1082 assert_eq!(n.name(), name); 1083 // The 9xx SASL block sits outside the 400..600 error range, so `is_error()` is 1084 // false even for the conceptual ERR_* codes (the established 7xx/9xx convention). 1085 assert!(!n.is_error()); 1086 } 1087 } 1088 1089 #[test] 1090 fn callerid_numerics_round_trip() { 1091 for (n, code, name) in [ 1092 (Numeric::RplAcceptlist, 281u16, "RPL_ACCEPTLIST"), 1093 (Numeric::RplEndofaccept, 282, "RPL_ENDOFACCEPT"), 1094 (Numeric::ErrAcceptfull, 456, "ERR_ACCEPTFULL"), 1095 (Numeric::ErrAcceptexist, 457, "ERR_ACCEPTEXIST"), 1096 (Numeric::ErrAcceptnot, 458, "ERR_ACCEPTNOT"), 1097 (Numeric::ErrTargumodeg, 716, "ERR_TARGUMODEG"), 1098 (Numeric::RplTargnotify, 717, "RPL_TARGNOTIFY"), 1099 (Numeric::RplUmodegmsg, 718, "RPL_UMODEGMSG"), 1100 ] { 1101 assert_eq!(n.code(), code); 1102 assert_eq!(Numeric::try_from(code), Ok(n)); 1103 assert_eq!(n.name(), name); 1104 } 1105 // The 45x accept errors sit in the error range; the 28x/71x replies do not. 1106 assert!(Numeric::ErrAcceptfull.is_error()); 1107 assert!(!Numeric::RplAcceptlist.is_error()); 1108 assert!(!Numeric::ErrTargumodeg.is_error()); 1109 } 1110 1111 #[test] 1112 fn statskline_codes_stay_distinct() { 1113 assert_eq!(Numeric::RplStatskline.code(), 216); 1114 assert_eq!(Numeric::ErrStatskline.code(), 499); 1115 } 1116 1117 #[test] 1118 fn is_error_follows_convention() { 1119 assert!(!Numeric::RplWelcome.is_error()); 1120 assert!(!Numeric::RplEtracefull.is_error()); // 708 is a reply 1121 assert!(Numeric::ErrNosuchnick.is_error()); 1122 assert!(Numeric::ErrUsersdontmatch.is_error()); 1123 } 1124 1125 #[test] 1126 fn whois_tls_aliases_whois_extra() { 1127 assert_eq!(Numeric::RPL_WHOISTLS, Numeric::RplWhoisextra); 1128 assert_eq!(Numeric::RPL_WHOISTLS.code(), 320); 1129 } 1130 1131 #[test] 1132 fn whois_bot_round_trips() { 1133 assert_eq!(Numeric::RplWhoisbot.code(), 335); 1134 assert_eq!(Numeric::try_from(335), Ok(Numeric::RplWhoisbot)); 1135 assert_eq!(Numeric::RplWhoisbot.name(), "RPL_WHOISBOT"); 1136 assert!(!Numeric::RplWhoisbot.is_error()); 1137 } 1138 1139 #[test] 1140 fn host_hidden_round_trips() { 1141 assert_eq!(Numeric::RplHostHidden.code(), 396); 1142 assert_eq!(Numeric::try_from(396), Ok(Numeric::RplHostHidden)); 1143 assert_eq!(Numeric::RplHostHidden.name(), "RPL_HOSTHIDDEN"); 1144 assert!( 1145 !Numeric::RplHostHidden.is_error(), 1146 "396 is a reply, not an error" 1147 ); 1148 } 1149}