+usestd::io::{self, BufRead, Read};
+
+usesuper::{ROOT_STATE, PatIdx, StateIdx};
+
+/// An abstraction over automatons and their corresponding iterators.
+pubtraitAutomaton: Sized {
+ /// Return the next state given the current state and next character.
+ fnnext_state(&self, si: StateIdx, b: u8) ->StateIdx;
+
+ /// Return true if and only if the given state and current pattern index
+ /// indicate a match.
+ fnhas_match(&self, si: StateIdx, outi: PatIdx) ->bool;
+
+ /// Build a match given the current state, pattern index and input index.
+ fnget_match(&self, si: StateIdx, outi: PatIdx, texti: usize) ->Match;
+
+ /// Attempt to skip through the input.
+ ///
+ /// This returns the index into `text` at which the next match attempt
+ /// should start. (If no skipping occurred, then the return value should
+ /// be equal to `at`.)
+ ///
+ /// Finally, if no match is possible, then return `text.len()`.
+ fnskip_to(&self, si: StateIdx, text: &[u8], at: usize) ->usize;
+
+ /// Returns true if and only if this automaton can skip through the input.
+ fnis_skippable(&self) ->bool;
+
+ /// Returns all of the patterns matched by this automaton.
+ ///
+ /// The order of the patterns is the order in which they were added.
+ fnpatterns(&self) ->&[String];
+
+ /// Returns the pattern indexed at `i`.
+ ///
+ /// The index corresponds to the position at which the pattern was added
+ /// to the automaton, starting at `0`.
+ fnpattern(&self, i: usize) ->&str;
+
+ /// Return the number of patterns in the automaton.
+ #[inline]
+ fnlen(&self) ->usize {
+ self.patterns().len()
+ }
+
+ /// Returns true if the automaton has no patterns.
+ #[inline]
+ fnis_empty(&self) ->bool {
+ self.len() ==0
+ }
+
+ /// Returns an iterator of non-overlapping matches in `s`.
+ fnfind<'a, 's>(
+ &'aself,
+ s: &'sstr,
+ ) ->Matches<'a, 's, Self> {
+ Matches {
+ aut: self,
+ text: s.as_bytes(),
+ texti: 0,
+ si: ROOT_STATE,
+ }
+ }
+
+ /// Returns an iterator of overlapping matches in `s`.
+ fnfind_overlapping<'a, 's>(
+ &'aself,
+ s: &'sstr,
+ ) ->MatchesOverlapping<'a, 's, Self> {
+ MatchesOverlapping {
+ aut: self,
+ text: s.as_bytes(),
+ texti: 0,
+ si: ROOT_STATE,
+ outi: 0,
+ }
+ }
+
+ /// Returns an iterator of non-overlapping matches in the given reader.
+ fnstream_find<'a, R: io::Read>(
+ &'aself,
+ rdr: R,
+ ) ->StreamMatches<'a, R, Self> {
+ StreamMatches {
+ aut: self,
+ buf: io::BufReader::new(rdr),
+ texti: 0,
+ si: ROOT_STATE,
+ }
+ }
+
+ /// Returns an iterator of overlapping matches in the given reader.
+ fnstream_find_overlapping<'a, R: io::Read>(
+ &'aself,
+ rdr: R,
+ ) ->StreamMatchesOverlapping<'a, R, Self> {
+ StreamMatchesOverlapping {
+ aut: self,
+ buf: io::BufReader::new(rdr),
+ texti: 0,
+ si: ROOT_STATE,
+ outi: 0,
+ }
+ }
+}
+
+/// Records a match in the search text.
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pubstructMatch {
+ /// The pattern index.
+ ///
+ /// This corresponds to the ordering in which the matched pattern was
+ /// added to the automaton, starting at `0`.
+ pubpati: usize,
+ /// The starting byte offset of the match in the search text.
+ pubstart: usize,
+ /// The ending byte offset of the match in the search text.
+ ///
+ /// (This can be re-captiulated with `pati` and adding the pattern's
+ /// length to `start`, but it is convenient to have it here.)
+ pubend: usize,
+}
+
+/// An iterator of non-overlapping matches for in-memory text.
+///
+/// This iterator yields `Match` values.
+///
+/// `'a` is the lifetime of the automaton and `'s` is the lifetime of the
+/// search text.
+#[derive(Debug)]
+pubstructMatches<'a, 's, A: 'a+Automaton> {
+ aut: &'aA,
+ text: &'s [u8],
+ texti: usize,
+ si: StateIdx,
+}
+
+impl<'a, 's, A: Automaton>IteratorforMatches<'a, 's, A> {
+ typeItem=Match;
+
+ fnnext(&mutself) ->Option<Match> {
+ // When there's an initial lone start byte, it is usually worth it
+ // to use `memchr` to skip along the input. The problem is that
+ // the skipping function is called in the inner match loop, which
+ // can be quite costly if the skipping condition is never met.
+ // Therefore, we lift the case analysis outside of the main loop at
+ // the cost of repeating code.
+ ifself.aut.is_skippable() {
+ self.texti=self.aut.skip_to(self.si, self.text, self.texti);
+ whileself.texti<self.text.len() {
+ self.si=self.aut.next_state(self.si, self.text[self.texti]);
+ ifself.aut.has_match(self.si, 0) {
+ letm=self.aut.get_match(self.si, 0, self.texti);
+ self.si=ROOT_STATE;
+ self.texti+=1;
+ returnSome(m);
+ }
+ self.texti=self.aut.skip_to(
+ self.si, self.text, self.texti+1);
+ }
+ } else {
+ whileself.texti<self.text.len() {
+ self.si=self.aut.next_state(self.si, self.text[self.texti]);
+ ifself.aut.has_match(self.si, 0) {
+ letm=self.aut.get_match(self.si, 0, self.texti);
+ self.si=ROOT_STATE;
+ self.texti+=1;
+ returnSome(m);
+ }
+ self.texti+=1;
+ }
+ }
+ None
+ }
+}
+
+/// An iterator of non-overlapping matches for streaming text.
+///
+/// This iterator yields `io::Result<Match>` values.
+///
+/// `'a` is the lifetime of the automaton and `R` is the type of the underlying
+/// `io::Read`er.
+#[derive(Debug)]
+pubstructStreamMatches<'a, R, A: 'a+Automaton> {
+ aut: &'aA,
+ buf: io::BufReader<R>,
+ texti: usize,
+ si: StateIdx,
+}
+
+impl<'a, R: io::Read, A: Automaton>IteratorforStreamMatches<'a, R, A> {
+ typeItem=io::Result<Match>;
+
+ fnnext(&mutself) ->Option<io::Result<Match>> {
+ letmutm=None;
+ letmutconsumed=0;
+'LOOP: loop {
+ self.buf.consume(consumed);
+ letbs=matchself.buf.fill_buf() {
+ Err(err) =>returnSome(Err(err)),
+ Ok(bs) ifbs.len() ==0=>break,
+ Ok(bs) =>bs,
+ };
+ consumed=bs.len(); // is shortened if we find a match
+ for (i, &b) inbs.iter().enumerate() {
+ self.si=self.aut.next_state(self.si, b);
+ ifself.aut.has_match(self.si, 0) {
+ m=Some(Ok(self.aut.get_match(self.si, 0, self.texti)));
+ consumed=i+1;
+ self.texti+=1;
+ self.si=ROOT_STATE;
+ break'LOOP;
+ }
+ self.texti+=1;
+ }
+ }
+ self.buf.consume(consumed);
+ m
+ }
+}
+
+/// An iterator of overlapping matches for in-memory text.
+///
+/// This iterator yields `Match` values.
+///
+/// `'a` is the lifetime of the automaton and `'s` is the lifetime of the
+/// search text.
+#[derive(Debug)]
+pubstructMatchesOverlapping<'a, 's, A: 'a+Automaton> {
+ aut: &'aA,
+ text: &'s [u8],
+ texti: usize,
+ si: StateIdx,
+ outi: usize,
+}
+
+impl<'a, 's, A: Automaton>IteratorforMatchesOverlapping<'a, 's, A> {
+ typeItem=Match;
+
+ fnnext(&mutself) ->Option<Match> {
+ ifself.aut.has_match(self.si, self.outi) {
+ letm=self.aut.get_match(self.si, self.outi, self.texti);
+ self.outi+=1;
+ if!self.aut.has_match(self.si, self.outi) {
+ self.texti+=1;
+ }
+ returnSome(m);
+ }
+
+ self.outi=0;
+ self.texti=self.aut.skip_to(self.si, self.text, self.texti);
+ whileself.texti<self.text.len() {
+ letb=self.text[self.texti];
+ self.si=self.aut.next_state(self.si, b);
+ ifself.aut.has_match(self.si, self.outi) {
+ returnself.next();
+ }
+ self.texti=self.aut.skip_to(self.si, self.text, self.texti+1);
+ }
+ None
+ }
+}
+
+/// An iterator of overlapping matches for streaming text.
+///
+/// This iterator yields `io::Result<Match>` values.
+///
+/// `'a` is the lifetime of the automaton and `R` is the type of the underlying
+/// `io::Read`er.
+#[derive(Debug)]
+pubstructStreamMatchesOverlapping<'a, R, A: 'a+Automaton> {
+ aut: &'aA,
+ buf: io::BufReader<R>,
+ texti: usize,
+ si: StateIdx,
+ outi: usize,
+}
+
+impl<
+ 'a,
+ R: io::Read,
+ A: Automaton,
+>IteratorforStreamMatchesOverlapping<'a, R, A> {
+ typeItem=io::Result<Match>;
+
+ fnnext(&mutself) ->Option<io::Result<Match>> {
+ ifself.aut.has_match(self.si, self.outi) {
+ letm=self.aut.get_match(self.si, self.outi, self.texti);
+ self.outi+=1;
+ if!self.aut.has_match(self.si, self.outi) {
+ self.texti+=1;
+ }
+ returnSome(Ok(m));
+ }
+ letmutm=None;
+ letmutconsumed=0;
+ self.outi=0;
+'LOOP: loop {
+ self.buf.consume(consumed);
+ letbs=matchself.buf.fill_buf() {
+ Err(err) =>returnSome(Err(err)),
+ Ok(bs) ifbs.len() ==0=>break,
+ Ok(bs) =>bs,
+ };
+ consumed=bs.len(); // is shortened if we find a match
+ for (i, &b) inbs.iter().enumerate() {
+ self.si=self.aut.next_state(self.si, b);
+ ifself.aut.has_match(self.si, self.outi) {
+ m=Some(Ok(self.aut.get_match(self.si, self.outi,
+ self.texti)));
+ consumed=i+1;
+ self.outi+=1;
+ if!self.aut.has_match(self.si, self.outi) {
+ self.texti+=1;
+ }
+ break'LOOP;
+ }
+ self.texti+=1;
+ }
+ }
+ self.buf.consume(consumed);
+ m
+ }
+}
+
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+
+//! *Carboxyl* provides primitives for functional reactive programming in Rust.
+//! It draws inspiration from the [Sodium][sodium] libraries and Push-Pull FRP,
+//! as described by [Elliott (2009)][elliott_push_pull].
+//!
+//! [sodium]: https://github.com/SodiumFRP/sodium/
+//! [elliott_push_pull]: http://conal.net/papers/push-pull-frp/push-pull-frp.pdf
+//!
+//!
+//! # Overview
+//!
+//! Functional reactive programming (FRP) is a composable and modular
+//! abstraction for creating dynamic and reactive systems. In its most general
+//! form it models these systems as a composition of two basic primitives:
+//! *streams* are a series of singular events and *signals* are continuously
+//! changing values.
+//!
+//! *Carboxyl* is an imperative, hybrid push- and pull-based implementation of
+//! FRP. Streams and the discrete components of signals are data-driven, i.e.
+//! whenever an event occurs the resulting changes are propagated to everything
+//! that depends on it.
+//!
+//! However, the continuous components of signals are demand-driven. Internally,
+//! *Carboxyl* stores the state of a signal as a function. This function has to
+//! be evaluated by consumers of a signal to obtain a concrete value.
+//!
+//! Nonetheless, *Carboxyl* has no explicit notion of time. Its signals are
+//! functions that can be evaluated at any time, but they do not carry any
+//! inherent notion of time. Synchronization and atomicity is achieved by a
+//! transaction system.
+//!
+//!
+//! # Functional reactive primitives
+//!
+//! This library provides two basic types: `Stream` and `Signal`. A stream is a
+//! discrete sequence of events, a signal is a container for values that change
+//! (discretely) over time.
+//!
+//! The FRP primitives are mostly implemented as methods of the basic types to
+//! ease method chaining, except for the various lifting functions, as they do
+//! not really belong to any type in particular.
+//!
+//! In addition, the `Sink` type allows one to create a stream of events by
+//! sending values into it. It is the only way to create a stream from scratch,
+//! i.e. without using any of the other primitives.
+//!
+//!
+//! # Usage example
+//!
+//! Here is a simple example of how you can use the primitives provided by
+//! *Carboxyl*. First of all, events can be sent into a *sink*. From a sink one
+//! can create a *stream* of events. Streams can also be filtered, mapped and
+//! merged. One can e.g. hold the last event from a stream as a signal.
+//!
+//! ```
+//! use carboxyl::Sink;
+//!
+//! let sink = Sink::new();
+//! let stream = sink.stream();
+//! let signal = stream.hold(3);
+//!
+//! // The current value of the signal is initially 3
+//! assert_eq!(signal.sample(), 3);
+//!
+//! // When we fire an event, the signal get updated accordingly
+//! sink.send(5);
+//! assert_eq!(signal.sample(), 5);
+//! ```
+//!
+//! One can also directly iterate over the stream instead of holding it in a
+//! signal:
+//!
+//! ```
+//! # use carboxyl::Sink;
+//! # let sink = Sink::new();
+//! # let stream = sink.stream();
+//! let mut events = stream.events();
+//! sink.send(4);
+//! assert_eq!(events.next(), Some(4));
+//! ```
+//!
+//! Streams and signals can be combined using various primitives. We can map a
+//! stream to another stream using a function:
+//!
+//! ```
+//! # use carboxyl::Sink;
+//! # let sink: Sink<i32> = Sink::new();
+//! # let stream = sink.stream();
+//! let squares = stream.map(|x| x * x).hold(0);
+//! sink.send(4);
+//! assert_eq!(squares.sample(), 16);
+//! ```
+//!
+//! Or we can filter a stream to create a new one that only contains events that
+//! satisfy a certain predicate:
+//!
+//! ```
+//! # use carboxyl::Sink;
+//! # let sink: Sink<i32> = Sink::new();
+//! # let stream = sink.stream();
+//! let negatives = stream.filter(|&x| x < 0).hold(0);
+//!
+//! // This won't arrive at the signal.
+//! sink.send(4);
+//! assert_eq!(negatives.sample(), 0);
+//!
+//! // But this will!
+//! sink.send(-3);
+//! assert_eq!(negatives.sample(), -3);
+//! ```
+//!
+//! There are some other methods on streams and signals, that you can find in
+//! their respective APIs.
+//!
+//! Note that all these objects are `Send + Sync + Clone`. This means you can
+//! easily pass them around in your code, make clones, give them to another
+//! thread, and they will still be updated correctly.
+//!
+//! You may have noticed that certain primitives take a function as an argument.
+//! There is a limitation on what kind of functions can and should be used here.
+//! In general, as FRP provides an abstraction around mutable state, they should
+//! be pure functions (i.e. free of side effects).
+//!
+//! For the most part this is guaranteed by Rust's type system. A static
+//! function with a matching signature always works. A closure though is very
+//! restricted: it must not borrow its environment, as it is impossible to
+//! satisfy the lifetime requirements for that. So you can only move stuff into
+//! it from the environment. However, the moved contents of the closure may also
+//! not be altered, which is guaranteed by the `Fn(…) -> …)` trait bound.
+//!
+//! However, both closures and functions could still have side effects such as
+//! I/O, changing mutable state via `Mutex` or `RefCell`, etc. While Rust's type
+//! system cannot prevent this, you should generally not pass such functions to
+//! the FRP primitives, as they break the benefits you get from using FRP.
+//! (An exception here is debugging output.)
+
+#![feature(arc_weak, fnbox)]
+#![cfg_attr(test, feature(test, scoped))]
+#![warn(missing_docs)]
+
+#[cfg(test)]
+externcratetest;
+#[cfg(test)]
+externcraterand;
+#[cfg(test)]
+externcratequickcheck;
+#[macro_use(lazy_static)]
+externcratelazy_static;
+
+pubusestream::{ Sink, Stream };
+pubusesignal::{ Signal, SignalMut };
+
+modtransaction;
+modsource;
+modpending;
+modreadonly;
+modstream;
+modsignal;
+#[macro_use]
+pubmodlift;
+#[cfg(test)]
+modtesting;
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+
+//! A trivial global lock transaction system.
+//!
+//! At the moment, this is really just a global static mutex, that needs to be
+//! locked, to ensure the atomicity of a transaction.
+
+usestd::sync::Mutex;
+usestd::cell::RefCell;
+usestd::boxed::FnBox;
+
+
+/// The global transaction lock.
+///
+/// TODO: revert this to use a static mutex, as soon as that is stabilized in
+/// the standard library.
+lazy_static! {
+ staticrefTRANSACTION_MUTEX: Mutex<()>=Mutex::new(());
+}
+
+/// Registry for callbacks to be executed at the end of a transaction.
+thread_local!(
+ staticCURRENT_TRANSACTION: RefCell<Option<Transaction>>=
+ RefCell::new(None)
+);
+
+
+/// A callback.
+typeCallback=Box<FnBox() +'static>;
+
+
+/// A transaction.
+pubstructTransaction {
+ intermediate: Vec<Callback>,
+ finalizers: Vec<Callback>,
+}
+
+implTransaction {
+ /// Create a new transaction
+ fnnew() ->Transaction {
+ Transaction {
+ intermediate: vec![],
+ finalizers: vec![],
+ }
+ }
+
+ /// Add a callback that will be called, when the transaction is done
+ /// except for finalizers.
+ pubfnlater<F: FnOnce() +'static>(&mutself, callback: F) {
+ self.intermediate.push(Box::new(callback));
+ }
+
+ /// Add a finalizing callback. This should not have far reaching
+ /// side-effects, and in particular not commit by itself. Typical operations
+ /// for a finalizer are executing queued state updates.
+ pubfnend<F: FnOnce() +'static>(&mutself, callback: F) {
+ self.finalizers.push(Box::new(callback));
+ }
+
+ /// Advance transactions by moving out intermediate stage callbacks.
+ fnadvance(&mutself) ->Vec<Callback> {
+ usestd::mem;
+ letmutintermediate=vec![];
+ mem::swap(&mutintermediate, &mutself.intermediate);
+ intermediate
+ }
+
+ /// Finalize the transaction
+ fnfinalize(self) {
+ forfinalizerinself.finalizers {
+ finalizer.call_box(());
+ }
+ }
+}
+
+
+/// Commit a transaction.
+///
+/// If the thread is not running any transactions currently, the global lock is
+/// acquired. Otherwise a new transaction begins, since given the interface of
+/// this module it is safely assumed that the lock is already held.
+pubfncommit<A, F: FnOnce() ->A>(body: F) ->A {
+ usestd::mem;
+ // Begin a new transaction
+ letmutprev=CURRENT_TRANSACTION.with(|current| {
+ letmutprev=Some(Transaction::new());
+ mem::swap(&mutprev, &mutcurrent.borrow_mut());
+ prev
+ });
+ // Acquire global lock if necessary
+ let_lock=matchprev {
+ None=>Some(TRANSACTION_MUTEX.lock().ok()
+ .expect("global transaction mutex poisoned")
+ ),
+ Some(_) =>None,
+ };
+ // Perform the main body of the transaction
+ letresult=body();
+ // Advance the transaction as long as necessary
+ loop {
+ letcallbacks=with_current(Transaction::advance);
+ ifcallbacks.is_empty() { break }
+ forcallbackincallbacks {
+ callback.call_box(());
+ }
+ }
+ // Call all finalizers and drop the transaction
+ CURRENT_TRANSACTION.with(|current|
+ mem::swap(&mutprev, &mutcurrent.borrow_mut())
+ );
+ prev.unwrap().finalize();
+ // Return
+ result
+}
+
+
+/// Register a callback during a transaction.
+pubfnwith_current<A, F: FnOnce(&mutTransaction) ->A>(action: F) ->A {
+ CURRENT_TRANSACTION.with(|current|
+ match&mut*current.borrow_mut() {
+ &mutSome(refmuttrans) =>action(trans),
+ _ =>panic!("there is no active transaction to register a callback"),
+ }
+ )
+}
+
+pubfnlater<F: FnOnce() +'static>(action: F) {
+ with_current(|c|c.later(action))
+}
+
+pubfnend<F: FnOnce() +'static>(action: F) {
+ with_current(|c|c.end(action))
+}
+
+
+#[cfg(test)]
+modtest {
+ usesuper::*;
+
+ #[test]
+ fncommit_single() {
+ letmutv=3;
+ commit(||v+=5);
+ assert_eq!(v, 8);
+ }
+
+ #[test]
+ fncommit_nested() {
+ letmutv=3;
+ commit(|| {
+ commit(||v*=2);
+ v+=4;
+ });
+ assert_eq!(v, 10);
+ }
+
+ #[test]
+ fncommits_parallel() {
+ usestd::sync::{Arc, Mutex};
+ usestd::thread;
+
+ // Set up a ref-counted value
+ letv=Arc::new(Mutex::new(3));
+ // Spawn a couple of scoped threads performing atomic operations on it
+ letguards: Vec<_>= (0..3)
+ .map(|_| {
+ letv=v.clone();
+ thread::spawn(move||commit(move|| {
+ // Acquire locks independently, s.t. commit atomicity does
+ // not rely on the local locks here
+ *v.lock().unwrap() *=2;
+ // …and sleep for a bit
+ thread::sleep_ms(1);
+ *v.lock().unwrap() -=1;
+ }))
+ })
+ .collect();
+ // Rejoin with all guards
+ forguardinguards { guard.join().ok().expect("thread failed"); }
+ // Check result
+ assert_eq!(&*v.lock().unwrap(), &17);
+ }
+}
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+
+#![allow(non_camel_case_types)]
+
+usestd::borrow::{ Cow, IntoCow, Borrow, ToOwned };
+usestd::borrow::Cow::*;
+usestd::iter::Extend;
+
+usemessage::{ Message, MsgType };
+
+pubtypeCS<'a>=Cow<'a, str>;
+
+// If you hoped it couldn't get any uglier... I'm sorry, it does.
+// Why a giant match? API.
+//
+// I tried structuring it as a bunch of structs that impl a `Command` trait,
+// but the user would have to use Any and lots of cats. Also, extensibility isn't
+// really a goal; the IRC protocol doesn't seem to evolve very fast.
+//
+// Granted, I *could* have used a phf-map to map to functions to parse this, which
+// - while more readable - shouldn't have resulted in performance gains.
+//
+// Please don't cry.
+
+#[derive(Debug, Hash, Clone, PartialEq, Eq)]
+pubenumCommand<'a> {
+ /// ```text
+ /// 3.1.1 Password message
+ ///
+ /// Command: PASS
+ /// Parameters: <password>
+ ///
+ /// The PASS command is used to set a 'connection password'. The
+ /// optional password can and MUST be set before any attempt to register
+ /// the connection is made. Currently this requires that user send a
+ /// PASS command before sending the NICK/USER combination.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NEEDMOREPARAMS ERR_ALREADYREGISTRED
+ ///
+ /// Example:
+ ///
+ /// PASS secretpasswordhere
+ /// ```
+ PASS(CS<'a>),
+
+ /// ```text
+ /// 3.1.2 Nick message
+ ///
+ /// Command: NICK
+ /// Parameters: <nickname>
+ ///
+ /// NICK command is used to give user a nickname or change the existing
+ /// one.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NONICKNAMEGIVEN ERR_ERRONEUSNICKNAME
+ /// ERR_NICKNAMEINUSE ERR_NICKCOLLISION
+ /// ERR_UNAVAILRESOURCE ERR_RESTRICTED
+ ///
+ /// Examples:
+ ///
+ /// NICK Wiz ; Introducing new nick "Wiz" if session is
+ /// still unregistered, or user changing his
+ /// nickname to "Wiz"
+ ///
+ /// :WiZ!jto@tolsun.oulu.fi NICK Kilroy
+ /// ; Server telling that WiZ changed his
+ /// nickname to Kilroy.
+ /// ```
+ NICK(CS<'a>),
+
+ /// ```text
+ /// 3.1.3 User message
+ ///
+ /// Command: USER
+ /// Parameters: <user> <mode> <unused> <realname>
+ ///
+ /// The USER command is used at the beginning of connection to specify
+ /// the username, hostname and realname of a new user.
+ ///
+ /// The <mode> parameter should be a numeric, and can be used to
+ /// automatically set user modes when registering with the server. This
+ /// parameter is a bitmask, with only 2 bits having any signification: if
+ /// the bit 2 is set, the user mode 'w' will be set and if the bit 3 is
+ /// set, the user mode 'i' will be set. (See Section 3.1.5 "User
+ /// Modes").
+ ///
+ /// The <realname> may contain space characters.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NEEDMOREPARAMS ERR_ALREADYREGISTRED
+ ///
+ /// Example:
+ ///
+ /// USER guest 0 * :Ronnie Reagan ; User registering themselves with a
+ /// username of "guest" and real name
+ /// "Ronnie Reagan".
+ ///
+ /// USER guest 8 * :Ronnie Reagan ; User registering themselves with a
+ /// username of "guest" and real name
+ /// "Ronnie Reagan", and asking to be set
+ /// invisible.
+ /// ```
+ USER(CS<'a>, CS<'a>, CS<'a>, CS<'a>),
+
+ /// ```text
+ /// 3.1.4 Oper message
+ ///
+ /// Command: OPER
+ /// Parameters: <name> <password>
+ ///
+ /// A normal user uses the OPER command to obtain operator privileges.
+ /// The combination of <name> and <password> are REQUIRED to gain
+ /// Operator privileges. Upon success, the user will receive a MODE
+ /// message (see section 3.1.5) indicating the new user modes.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NEEDMOREPARAMS RPL_YOUREOPER
+ /// ERR_NOOPERHOST ERR_PASSWDMISMATCH
+ ///
+ /// Example:
+ ///
+ /// OPER foo bar ; Attempt to register as an operator
+ /// using a username of "foo" and "bar"
+ /// as the password.
+ /// ```
+ OPER(CS<'a>, CS<'a>),
+
+ /// ```text
+ /// 3.1.5 User mode message
+ ///
+ /// Command: MODE
+ /// Parameters: <nickname>
+ /// *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )
+ ///
+ /// The user MODE's are typically changes which affect either how the
+ /// client is seen by others or what 'extra' messages the client is sent.
+ ///
+ /// A user MODE command MUST only be accepted if both the sender of the
+ /// message and the nickname given as a parameter are both the same. If
+ /// no other parameter is given, then the server will return the current
+ /// settings for the nick.
+ ///
+ /// The available modes are as follows:
+ ///
+ /// a - user is flagged as away;
+ /// i - marks a users as invisible;
+ /// w - user receives wallops;
+ /// r - restricted user connection;
+ /// o - operator flag;
+ /// O - local operator flag;
+ /// s - marks a user for receipt of server notices.
+ ///
+ /// Additional modes may be available later on.
+ ///
+ /// The flag 'a' SHALL NOT be toggled by the user using the MODE command,
+ /// instead use of the AWAY command is REQUIRED.
+ ///
+ /// If a user attempts to make themselves an operator using the "+o" or
+ /// "+O" flag, the attempt SHOULD be ignored as users could bypass the
+ /// authentication mechanisms of the OPER command. There is no
+ /// restriction, however, on anyone `deopping' themselves (using "-o" or
+ /// "-O").
+ ///
+ /// On the other hand, if a user attempts to make themselves unrestricted
+ /// using the "-r" flag, the attempt SHOULD be ignored. There is no
+ /// restriction, however, on anyone `deopping' themselves (using "+r").
+ /// This flag is typically set by the server upon connection for
+ /// administrative reasons. While the restrictions imposed are left up
+ /// to the implementation, it is typical that a restricted user not be
+ /// allowed to change nicknames, nor make use of the channel operator
+ /// status on channels.
+ ///
+ /// The flag 's' is obsolete but MAY still be used.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NEEDMOREPARAMS ERR_USERSDONTMATCH
+ /// ERR_UMODEUNKNOWNFLAG RPL_UMODEIS
+ ///
+ /// Examples:
+ ///
+ /// MODE WiZ -w ; Command by WiZ to turn off
+ /// reception of WALLOPS messages.
+ ///
+ /// MODE Angel +i ; Command from Angel to make herself
+ /// invisible.
+ ///
+ /// MODE WiZ -o ; WiZ 'deopping' (removing operator
+ /// status).
+ /// ```
+ UMODE(CS<'a>),
+
+ /// ```text
+ /// 3.1.6 Service message
+ ///
+ /// Command: SERVICE
+ /// Parameters: <nickname> <reserved> <distribution> <type>
+ /// <reserved> <info>
+ ///
+ /// The SERVICE command to register a new service. Command parameters
+ /// specify the service nickname, distribution, type and info of a new
+ /// service.
+ ///
+ /// The <distribution> parameter is used to specify the visibility of a
+ /// service. The service may only be known to servers which have a name
+ /// matching the distribution. For a matching server to have knowledge
+ /// of the service, the network path between that server and the server
+ /// on which the service is connected MUST be composed of servers which
+ /// names all match the mask.
+ ///
+ /// The <type> parameter is currently reserved for future usage.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_ALREADYREGISTRED ERR_NEEDMOREPARAMS
+ /// ERR_ERRONEUSNICKNAME
+ /// RPL_YOURESERVICE RPL_YOURHOST
+ /// RPL_MYINFO
+ ///
+ /// Example:
+ ///
+ /// SERVICE dict * *.fr 0 0 :French Dictionary ; Service registering
+ /// itself with a name of "dict". This
+ /// service will only be available on
+ /// servers which name matches "*.fr".
+ /// ```
+ SERVICE(CS<'a>, CS<'a>, CS<'a>, CS<'a>, CS<'a>, CS<'a>),
+
+ /// ```text
+ /// 3.1.7 Quit
+ ///
+ /// Command: QUIT
+ /// Parameters: [ <Quit Message> ]
+ ///
+ /// A client session is terminated with a quit message. The server
+ /// acknowledges this by sending an ERROR message to the client.
+ ///
+ /// Numeric Replies:
+ ///
+ /// None.
+ ///
+ /// Example:
+ ///
+ /// QUIT :Gone to have lunch ; Preferred message format.
+ ///
+ /// :syrk!kalt@millennium.stealth.net QUIT :Gone to have lunch ; User
+ /// syrk has quit IRC to have lunch.
+ /// ```
+ QUIT(Option<CS<'a>>),
+
+ /// ```text
+ /// 3.1.8 Squit
+ ///
+ /// Command: SQUIT
+ /// Parameters: <server> <comment>
+ ///
+ /// The SQUIT command is available only to operators. It is used to
+ /// disconnect server links. Also servers can generate SQUIT messages on
+ /// error conditions. A SQUIT message may also target a remote server
+ /// connection. In this case, the SQUIT message will simply be sent to
+ /// the remote server without affecting the servers in between the
+ /// operator and the remote server.
+ ///
+ /// The <comment> SHOULD be supplied by all operators who execute a SQUIT
+ /// for a remote server. The server ordered to disconnect its peer
+ /// generates a WALLOPS message with <comment> included, so that other
+ /// users may be aware of the reason of this action.
+ ///
+ /// Numeric replies:
+ ///
+ /// ERR_NOPRIVILEGES ERR_NOSUCHSERVER
+ /// ERR_NEEDMOREPARAMS
+ ///
+ /// Examples:
+ ///
+ /// SQUIT tolsun.oulu.fi :Bad Link ? ; Command to uplink of the server
+ /// tolson.oulu.fi to terminate its
+ /// connection with comment "Bad Link".
+ ///
+ /// :Trillian SQUIT cm22.eng.umd.edu :Server out of control ; Command
+ /// from Trillian from to disconnect
+ /// "cm22.eng.umd.edu" from the net with
+ /// comment "Server out of control".
+ /// ```
+ SQUIT(CS<'a>, CS<'a>),
+
+ /// ```text
+ /// 3.2.1 Join message
+ ///
+ /// Command: JOIN
+ /// Parameters: ( <channel> *( "," <channel> ) [ <key> *( "," <key> ) ] )
+ /// / "0"
+ ///
+ /// The JOIN command is used by a user to request to start listening to
+ /// the specific channel. Servers MUST be able to parse arguments in the
+ /// form of a list of target, but SHOULD NOT use lists when sending JOIN
+ /// messages to clients.
+ ///
+ /// Once a user has joined a channel, he receives information about
+ /// all commands his server receives affecting the channel. This
+ /// includes JOIN, MODE, KICK, PART, QUIT and of course PRIVMSG/NOTICE.
+ /// This allows channel members to keep track of the other channel
+ /// members, as well as channel modes.
+ ///
+ /// If a JOIN is successful, the user receives a JOIN message as
+ /// confirmation and is then sent the channel's topic (using RPL_TOPIC) and
+ /// the list of users who are on the channel (using RPL_NAMREPLY), which
+ /// MUST include the user joining.
+ ///
+ /// Note that this message accepts a special argument ("0"), which is
+ /// a special request to leave all channels the user is currently a member
+ /// of. The server will process this message as if the user had sent
+ /// a PART command (See Section 3.2.2) for each channel he is a member
+ /// of.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NEEDMOREPARAMS ERR_BANNEDFROMCHAN
+ /// ERR_INVITEONLYCHAN ERR_BADCHANNELKEY
+ /// ERR_CHANNELISFULL ERR_BADCHANMASK
+ /// ERR_NOSUCHCHANNEL ERR_TOOMANYCHANNELS
+ /// ERR_TOOMANYTARGETS ERR_UNAVAILRESOURCE
+ /// RPL_TOPIC
+ ///
+ /// Examples:
+ ///
+ /// JOIN #foobar ; Command to join channel #foobar.
+ ///
+ /// JOIN &foo fubar ; Command to join channel &foo using
+ /// key "fubar".
+ ///
+ /// JOIN #foo,&bar fubar ; Command to join channel #foo using
+ /// key "fubar" and &bar using no key.
+ ///
+ /// JOIN #foo,#bar fubar,foobar ; Command to join channel #foo using
+ /// key "fubar", and channel #bar using
+ /// key "foobar".
+ ///
+ /// JOIN #foo,#bar ; Command to join channels #foo and
+ /// #bar.
+ ///
+ /// JOIN 0 ; Leave all currently joined
+ /// channels.
+ ///
+ /// :WiZ!jto@tolsun.oulu.fi JOIN #Twilight_zone ; JOIN message from WiZ
+ /// on channel #Twilight_zone
+ /// ```
+ JOIN(Vec<CS<'a>>, Vec<CS<'a>>),
+
+ /// ```text
+ /// 3.2.2 Part message
+ ///
+ /// Command: PART
+ /// Parameters: <channel> *( "," <channel> ) [ <Part Message> ]
+ ///
+ /// The PART command causes the user sending the message to be removed
+ /// from the list of active members for all given channels listed in the
+ /// parameter string. If a "Part Message" is given, this will be sent
+ /// instead of the default message, the nickname. This request is always
+ /// granted by the server.
+ ///
+ /// Servers MUST be able to parse arguments in the form of a list of
+ /// target, but SHOULD NOT use lists when sending PART messages to
+ /// clients.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NEEDMOREPARAMS ERR_NOSUCHCHANNEL
+ /// ERR_NOTONCHANNEL
+ ///
+ /// Examples:
+ ///
+ /// PART #twilight_zone ; Command to leave channel
+ /// "#twilight_zone"
+ ///
+ /// PART #oz-ops,&group5 ; Command to leave both channels
+ /// "&group5" and "#oz-ops".
+ ///
+ /// :WiZ!jto@tolsun.oulu.fi PART #playzone :I lost
+ /// ; User WiZ leaving channel
+ /// "#playzone" with the message "I
+ /// lost".
+ /// ```
+ PART(Vec<CS<'a>>, Option<CS<'a>>),
+
+ /// ```text
+ /// 3.2.3 Channel mode message
+ ///
+ /// Command: MODE
+ /// Parameters: <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
+ ///
+ /// The MODE command is provided so that users may query and change the
+ /// characteristics of a channel. For more details on available modes
+ /// and their uses, see "Internet Relay Chat: Channel Management" [IRC-
+ /// CHAN]. Note that there is a maximum limit of three (3) changes per
+ /// command for modes that take a parameter.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NEEDMOREPARAMS ERR_KEYSET
+ /// ERR_NOCHANMODES ERR_CHANOPRIVSNEEDED
+ /// ERR_USERNOTINCHANNEL ERR_UNKNOWNMODE
+ /// RPL_CHANNELMODEIS
+ /// RPL_BANLIST RPL_ENDOFBANLIST
+ /// RPL_EXCEPTLIST RPL_ENDOFEXCEPTLIST
+ /// RPL_INVITELIST RPL_ENDOFINVITELIST
+ /// RPL_UNIQOPIS
+ ///
+ /// The following examples are given to help understanding the syntax of
+ /// the MODE command, but refer to modes defined in "Internet Relay Chat:
+ /// Channel Management" [IRC-CHAN].
+ ///
+ /// Examples:
+ ///
+ /// MODE #Finnish +imI *!*@*.fi ; Command to make #Finnish channel
+ /// moderated and 'invite-only' with user
+ /// with a hostname matching *.fi
+ /// automatically invited.
+ ///
+ /// MODE #Finnish +o Kilroy ; Command to give 'chanop' privileges
+ /// to Kilroy on channel #Finnish.
+ ///
+ /// MODE #Finnish +v Wiz ; Command to allow WiZ to speak on
+ /// #Finnish.
+ ///
+ /// MODE #Fins -s ; Command to remove 'secret' flag
+ /// from channel #Fins.
+ ///
+ /// MODE #42 +k oulu ; Command to set the channel key to
+ /// "oulu".
+ ///
+ /// MODE #42 -k oulu ; Command to remove the "oulu"
+ /// channel key on channel "#42".
+ ///
+ /// MODE #eu-opers +l 10 ; Command to set the limit for the
+ /// number of users on channel
+ /// "#eu-opers" to 10.
+ ///
+ /// :WiZ!jto@tolsun.oulu.fi MODE #eu-opers -l
+ /// ; User "WiZ" removing the limit for
+ /// the number of users on channel "#eu-
+ /// opers".
+ ///
+ /// MODE &oulu +b ; Command to list ban masks set for
+ /// the channel "&oulu".
+ ///
+ /// MODE &oulu +b *!*@* ; Command to prevent all users from
+ /// joining.
+ ///
+ /// MODE &oulu +b *!*@*.edu +e *!*@*.bu.edu
+ /// ; Command to prevent any user from a
+ /// hostname matching *.edu from joining,
+ /// except if matching *.bu.edu
+ ///
+ /// MODE #bu +be *!*@*.edu *!*@*.bu.edu
+ /// ; Comment to prevent any user from a
+ /// hostname matching *.edu from joining,
+ /// except if matching *.bu.edu
+ ///
+ /// MODE #meditation e ; Command to list exception masks set
+ /// for the channel "#meditation".
+ ///
+ /// MODE #meditation I ; Command to list invitations masks
+ /// set for the channel "#meditation".
+ ///
+ /// MODE !12345ircd O ; Command to ask who the channel
+ /// creator for "!12345ircd" is
+ /// ```
+ MODE(CS<'a>, Vec<(CS<'a>, CS<'a>)>),
+
+ /// ```text```
+ /// 3.2.4 Topic message
+ ///
+ /// Command: TOPIC
+ /// Parameters: <channel> [ <topic> ]
+ ///
+ /// The TOPIC command is used to change or view the topic of a channel.
+ /// The topic for channel <channel> is returned if there is no <topic>
+ /// given. If the <topic> parameter is present, the topic for that
+ /// channel will be changed, if this action is allowed for the user
+ /// requesting it. If the <topic> parameter is an empty string, the
+ /// topic for that channel will be removed.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NEEDMOREPARAMS ERR_NOTONCHANNEL
+ /// RPL_NOTOPIC RPL_TOPIC
+ /// ERR_CHANOPRIVSNEEDED ERR_NOCHANMODES
+ ///
+ /// Examples:
+ ///
+ /// :WiZ!jto@tolsun.oulu.fi TOPIC #test :New topic ; User Wiz setting the
+ /// topic.
+ ///
+ /// TOPIC #test :another topic ; Command to set the topic on #test
+ /// to "another topic".
+ ///
+ /// TOPIC #test : ; Command to clear the topic on
+ /// #test.
+ ///
+ /// TOPIC #test ; Command to check the topic for
+ /// #test.
+ /// ```
+ TOPIC(CS<'a>, Option<CS<'a>>),
+
+ /// ```text
+ /// 3.2.5 Names message
+ ///
+ /// Command: NAMES
+ /// Parameters: [ <channel> *( "," <channel> ) [ <target> ] ]
+ ///
+ /// By using the NAMES command, a user can list all nicknames that are
+ /// visible to him. For more details on what is visible and what is not,
+ /// see "Internet Relay Chat: Channel Management" [IRC-CHAN]. The
+ /// <channel> parameter specifies which channel(s) to return information
+ /// about. There is no error reply for bad channel names.
+ ///
+ /// If no <channel> parameter is given, a list of all channels and their
+ /// occupants is returned. At the end of this list, a list of users who
+ /// are visible but either not on any channel or not on a visible channel
+ /// are listed as being on `channel' "*".
+ ///
+ /// If the <target> parameter is specified, the request is forwarded to
+ /// that server which will generate the reply.
+ ///
+ /// Wildcards are allowed in the <target> parameter.
+ ///
+ /// Numerics:
+ ///
+ /// ERR_TOOMANYMATCHES ERR_NOSUCHSERVER
+ /// RPL_NAMREPLY RPL_ENDOFNAMES
+ ///
+ /// Examples:
+ ///
+ /// NAMES #twilight_zone,#42 ; Command to list visible users on
+ /// #twilight_zone and #42
+ ///
+ /// NAMES ; Command to list all visible
+ /// channels and users
+ /// ```
+ NAMES(Vec<CS<'a>>, Option<CS<'a>>),
+
+ /// ```text
+ /// 3.2.6 List message
+ ///
+ /// Command: LIST
+ /// Parameters: [ <channel> *( "," <channel> ) [ <target> ] ]
+ ///
+ /// The list command is used to list channels and their topics. If the
+ /// <channel> parameter is used, only the status of that channel is
+ /// displayed.
+ ///
+ /// If the <target> parameter is specified, the request is forwarded to
+ /// that server which will generate the reply.
+ ///
+ /// Wildcards are allowed in the <target> parameter.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_TOOMANYMATCHES ERR_NOSUCHSERVER
+ /// RPL_LIST RPL_LISTEND
+ ///
+ /// Examples:
+ ///
+ /// LIST ; Command to list all channels.
+ ///
+ /// LIST #twilight_zone,#42 ; Command to list channels
+ /// #twilight_zone and #42
+ /// ```
+ LIST(Vec<CS<'a>>, Option<CS<'a>>),
+
+ /// ```text
+ /// 3.2.7 Invite message
+ ///
+ /// Command: INVITE
+ /// Parameters: <nickname> <channel>
+ ///
+ /// The INVITE command is used to invite a user to a channel. The
+ /// parameter <nickname> is the nickname of the person to be invited to
+ /// the target channel <channel>. There is no requirement that the
+ /// channel the target user is being invited to must exist or be a valid
+ /// channel. However, if the channel exists, only members of the channel
+ /// are allowed to invite other users. When the channel has invite-only
+ /// flag set, only channel operators may issue INVITE command.
+ ///
+ /// Only the user inviting and the user being invited will receive
+ /// notification of the invitation. Other channel members are not
+ /// notified. (This is unlike the MODE changes, and is occasionally the
+ /// source of trouble for users.)
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NEEDMOREPARAMS ERR_NOSUCHNICK
+ /// ERR_NOTONCHANNEL ERR_USERONCHANNEL
+ /// ERR_CHANOPRIVSNEEDED
+ /// RPL_INVITING RPL_AWAY
+ ///
+ /// Examples:
+ ///
+ /// :Angel!wings@irc.org INVITE Wiz #Dust
+ /// ; Message to WiZ when he has been
+ /// invited by user Angel to channel
+ /// #Dust
+ ///
+ /// INVITE Wiz #Twilight_Zone ; Command to invite WiZ to
+ /// #Twilight_zone
+ /// ```
+ INVITE(CS<'a>, CS<'a>),
+
+ /// ```text
+ /// 3.2.8 Kick command
+ ///
+ /// Command: KICK
+ /// Parameters: <channel> *( "," <channel> ) <user> *( "," <user> )
+ /// [<comment>]
+ ///
+ /// The KICK command can be used to request the forced removal of a user
+ /// from a channel. It causes the <user> to PART from the <channel> by
+ /// force. For the message to be syntactically correct, there MUST be
+ /// either one channel parameter and multiple user parameter, or as many
+ /// channel parameters as there are user parameters. If a "comment" is
+ /// given, this will be sent instead of the default message, the nickname
+ /// of the user issuing the KICK.
+ ///
+ /// The server MUST NOT send KICK messages with multiple channels or
+ /// users to clients. This is necessarily to maintain backward
+ /// compatibility with old client software.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NEEDMOREPARAMS ERR_NOSUCHCHANNEL
+ /// ERR_BADCHANMASK ERR_CHANOPRIVSNEEDED
+ /// ERR_USERNOTINCHANNEL ERR_NOTONCHANNEL
+ ///
+ /// Examples:
+ ///
+ /// KICK &Melbourne Matthew ; Command to kick Matthew from
+ /// &Melbourne
+ ///
+ /// KICK #Finnish John :Speaking English
+ /// ; Command to kick John from #Finnish
+ /// using "Speaking English" as the
+ /// reason (comment).
+ ///
+ /// :WiZ!jto@tolsun.oulu.fi KICK #Finnish John
+ /// ; KICK message on channel #Finnish
+ /// from WiZ to remove John from channel
+ /// ```
+ KICK(Vec<CS<'a>>, Vec<CS<'a>>, Option<CS<'a>>),
+
+ /// ```text
+ /// 3.3.1 Private messages
+ ///
+ /// Command: PRIVMSG
+ /// Parameters: <msgtarget> <text to be sent>
+ ///
+ /// PRIVMSG is used to send private messages between users, as well as to
+ /// send messages to channels. <msgtarget> is usually the nickname of
+ /// the recipient of the message, or a channel name.
+ ///
+ /// The <msgtarget> parameter may also be a host mask (#<mask>) or server
+ /// mask ($<mask>). In both cases the server will only send the PRIVMSG
+ /// to those who have a server or host matching the mask. The mask MUST
+ /// have at least 1 (one) "." in it and no wildcards following the last
+ /// ".". This requirement exists to prevent people sending messages to
+ /// "#*" or "$*", which would broadcast to all users. Wildcards are the
+ /// '*' and '?' characters. This extension to the PRIVMSG command is
+ /// only available to operators.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NORECIPIENT ERR_NOTEXTTOSEND
+ /// ERR_CANNOTSENDTOCHAN ERR_NOTOPLEVEL
+ /// ERR_WILDTOPLEVEL ERR_TOOMANYTARGETS
+ /// ERR_NOSUCHNICK
+ /// RPL_AWAY
+ ///
+ /// Examples:
+ ///
+ /// :Angel!wings@irc.org PRIVMSG Wiz :Are you receiving this message ?
+ /// ; Message from Angel to Wiz.
+ ///
+ /// PRIVMSG Angel :yes I'm receiving it !
+ /// ; Command to send a message to Angel.
+ ///
+ /// PRIVMSG jto@tolsun.oulu.fi :Hello !
+ /// ; Command to send a message to a user
+ /// on server tolsun.oulu.fi with
+ /// username of "jto".
+ ///
+ /// PRIVMSG kalt%millennium.stealth.net@irc.stealth.net :Are you a frog?
+ /// ; Message to a user on server
+ /// irc.stealth.net with username of
+ /// "kalt", and connected from the host
+ /// millennium.stealth.net.
+ ///
+ /// PRIVMSG kalt%millennium.stealth.net :Do you like cheese?
+ /// ; Message to a user on the local
+ /// server with username of "kalt", and
+ /// connected from the host
+ /// millennium.stealth.net.
+ ///
+ /// PRIVMSG Wiz!jto@tolsun.oulu.fi :Hello !
+ /// ; Message to the user with nickname
+ /// Wiz who is connected from the host
+ /// tolsun.oulu.fi and has the username
+ /// "jto".
+ ///
+ /// PRIVMSG $*.fi :Server tolsun.oulu.fi rebooting.
+ /// ; Message to everyone on a server
+ /// which has a name matching *.fi.
+ ///
+ /// PRIVMSG #*.edu :NSFNet is undergoing work, expect interruptions
+ /// ; Message to all users who come from
+ /// a host which has a name matching
+ /// *.edu.
+ /// ```
+ PRIVMSG(CS<'a>, CS<'a>),
+
+ /// ```text
+ /// 3.3.2 Notice
+ ///
+ /// Command: NOTICE
+ /// Parameters: <msgtarget> <text>
+ ///
+ /// The NOTICE command is used similarly to PRIVMSG. The difference
+ /// between NOTICE and PRIVMSG is that automatic replies MUST NEVER be
+ /// sent in response to a NOTICE message. This rule applies to servers
+ /// too - they MUST NOT send any error reply back to the client on
+ /// receipt of a notice. The object of this rule is to avoid loops
+ /// between clients automatically sending something in response to
+ /// something it received.
+ ///
+ /// This command is available to services as well as users.
+ ///
+ /// This is typically used by services, and automatons (clients with
+ /// either an AI or other interactive program controlling their actions).
+ ///
+ /// See PRIVMSG for more details on replies and examples.
+ /// ```
+ NOTICE(CS<'a>, CS<'a>),
+
+ /// ```text
+ /// 3.4.1 Motd message
+ ///
+ /// Command: MOTD
+ /// Parameters: [ <target> ]
+ ///
+ /// The MOTD command is used to get the "Message Of The Day" of the given
+ /// server, or current server if <target> is omitted.
+ ///
+ /// Wildcards are allowed in the <target> parameter.
+ ///
+ /// Numeric Replies:
+ /// RPL_MOTDSTART RPL_MOTD
+ /// RPL_ENDOFMOTD ERR_NOMOTD
+ /// ```
+ MOTD(Option<CS<'a>>),
+
+ /// ```text
+ /// 3.4.2 Lusers message
+ ///
+ /// Command: LUSERS
+ /// Parameters: [ <mask> [ <target> ] ]
+ ///
+ /// The LUSERS command is used to get statistics about the size of the
+ /// IRC network. If no parameter is given, the reply will be about the
+ /// whole net. If a <mask> is specified, then the reply will only
+ /// concern the part of the network formed by the servers matching the
+ /// mask. Finally, if the <target> parameter is specified, the request
+ /// is forwarded to that server which will generate the reply.
+ ///
+ /// Wildcards are allowed in the <target> parameter.
+ ///
+ /// Numeric Replies:
+ ///
+ /// RPL_LUSERCLIENT RPL_LUSEROP
+ /// RPL_LUSERUNKOWN RPL_LUSERCHANNELS
+ /// RPL_LUSERME ERR_NOSUCHSERVER
+ /// ```
+ LUSERS(Option<(CS<'a>, Option<CS<'a>>)>),
+
+ /// ```text
+ /// 3.4.3 Version message
+ ///
+ /// Command: VERSION
+ /// Parameters: [ <target> ]
+ ///
+ /// The VERSION command is used to query the version of the server
+ /// program. An optional parameter <target> is used to query the version
+ /// of the server program which a client is not directly connected to.
+ ///
+ /// Wildcards are allowed in the <target> parameter.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOSUCHSERVER RPL_VERSION
+ ///
+ /// Examples:
+ ///
+ /// VERSION tolsun.oulu.fi ; Command to check the version of
+ /// server "tolsun.oulu.fi".
+ /// ```
+ VERSION(Option<CS<'a>>),
+
+ /// ```text
+ /// 3.4.4 Stats message
+ ///
+ /// Command: STATS
+ /// Parameters: [ <query> [ <target> ] ]
+ ///
+ /// The stats command is used to query statistics of certain server. If
+ /// <query> parameter is omitted, only the end of stats reply is sent
+ /// back.
+ ///
+ /// A query may be given for any single letter which is only checked by
+ /// the destination server and is otherwise passed on by intermediate
+ /// servers, ignored and unaltered.
+ ///
+ /// Wildcards are allowed in the <target> parameter.
+ ///
+ /// Except for the ones below, the list of valid queries is
+ /// implementation dependent. The standard queries below SHOULD be
+ /// supported by the server:
+ ///
+ /// l - returns a list of the server's connections, showing how
+ /// long each connection has been established and the
+ /// traffic over that connection in Kbytes and messages for
+ /// each direction;
+ /// m - returns the usage count for each of commands supported
+ /// by the server; commands for which the usage count is
+ /// zero MAY be omitted;
+ /// o - returns a list of configured privileged users,
+ /// operators;
+ /// u - returns a string showing how long the server has been
+ /// up.
+ ///
+ /// It is also RECOMMENDED that client and server access configuration be
+ /// published this way.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOSUCHSERVER
+ /// RPL_STATSLINKINFO RPL_STATSUPTIME
+ /// RPL_STATSCOMMANDS RPL_STATSOLINE
+ /// RPL_ENDOFSTATS
+ ///
+ /// Examples:
+ ///
+ /// STATS m ; Command to check the command usage
+ /// for the server you are connected to
+ /// ```
+ STATS(Option<(CS<'a>, Option<CS<'a>>)>),
+
+ /// ```text
+ /// 3.4.5 Links message
+ ///
+ /// Command: LINKS
+ /// Parameters: [ [ <remote server> ] <server mask> ]
+ ///
+ /// With LINKS, a user can list all servernames, which are known by the
+ /// server answering the query. The returned list of servers MUST match
+ /// the mask, or if no mask is given, the full list is returned.
+ ///
+ /// If <remote server> is given in addition to <server mask>, the LINKS
+ /// command is forwarded to the first server found that matches that name
+ /// (if any), and that server is then required to answer the query.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOSUCHSERVER
+ /// RPL_LINKS RPL_ENDOFLINKS
+ ///
+ /// Examples:
+ ///
+ /// LINKS *.au ; Command to list all servers which
+ /// have a name that matches *.au;
+ ///
+ /// LINKS *.edu *.bu.edu ; Command to list servers matching
+ /// *.bu.edu as seen by the first server
+ /// matching *.edu.
+ /// ```
+ LINKS(Option<(Option<CS<'a>>, CS<'a>)>),
+
+ /// ```text
+ /// 3.4.6 Time message
+ ///
+ /// Command: TIME
+ /// Parameters: [ <target> ]
+ ///
+ /// The time command is used to query local time from the specified
+ /// server. If the <target> parameter is not given, the server receiving
+ /// the command must reply to the query.
+ ///
+ /// Wildcards are allowed in the <target> parameter.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOSUCHSERVER RPL_TIME
+ ///
+ /// Examples:
+ /// TIME tolsun.oulu.fi ; check the time on the server
+ /// "tolson.oulu.fi"
+ /// ```
+ TIME(Option<CS<'a>>),
+
+ /// ```text
+ /// 3.4.7 Connect message
+ ///
+ /// Command: CONNECT
+ /// Parameters: <target server> <port> [ <remote server> ]
+ ///
+ /// The CONNECT command can be used to request a server to try to
+ /// establish a new connection to another server immediately. CONNECT is
+ /// a privileged command and SHOULD be available only to IRC Operators.
+ /// If a <remote server> is given and its mask doesn't match name of the
+ /// parsing server, the CONNECT attempt is sent to the first match of
+ /// remote server. Otherwise the CONNECT attempt is made by the server
+ /// processing the request.
+ ///
+ /// The server receiving a remote CONNECT command SHOULD generate a
+ /// WALLOPS message describing the source and target of the request.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOSUCHSERVER ERR_NOPRIVILEGES
+ /// ERR_NEEDMOREPARAMS
+ ///
+ /// Examples:
+ ///
+ /// CONNECT tolsun.oulu.fi 6667 ; Command to attempt to connect local
+ /// server to tolsun.oulu.fi on port 6667
+ ///
+ CONNECT(CS<'a>, i16, Option<CS<'a>>),
+
+ /// ```text
+ /// 3.4.8 Trace message
+ ///
+ /// Command: TRACE
+ /// Parameters: [ <target> ]
+ ///
+ /// TRACE command is used to find the route to specific server and
+ /// information about its peers. Each server that processes this command
+ /// MUST report to the sender about it. The replies from pass-through
+ /// links form a chain, which shows route to destination. After sending
+ /// this reply back, the query MUST be sent to the next server until
+ /// given <target> server is reached.
+ ///
+ /// TRACE command is used to find the route to specific server. Each
+ /// server that processes this message MUST tell the sender about it by
+ /// sending a reply indicating it is a pass-through link, forming a chain
+ /// of replies. After sending this reply back, it MUST then send the
+ /// TRACE message to the next server until given server is reached. If
+ /// the <target> parameter is omitted, it is RECOMMENDED that TRACE
+ /// command sends a message to the sender telling which servers the local
+ /// server has direct connection to.
+ ///
+ /// If the destination given by <target> is an actual server, the
+ /// destination server is REQUIRED to report all servers, services and
+ /// operators which are connected to it; if the command was issued by an
+ /// operator, the server MAY also report all users which are connected to
+ /// it. If the destination given by <target> is a nickname, then only a
+ /// reply for that nickname is given. If the <target> parameter is
+ /// omitted, it is RECOMMENDED that the TRACE command is parsed as
+ /// targeted to the processing server.
+ ///
+ /// Wildcards are allowed in the <target> parameter.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOSUCHSERVER
+ ///
+ /// If the TRACE message is destined for another server, all
+ /// intermediate servers must return a RPL_TRACELINK reply to indicate
+ /// that the TRACE passed through it and where it is going next.
+ ///
+ /// RPL_TRACELINK
+ ///
+ /// A TRACE reply may be composed of any number of the following
+ /// numeric replies.
+ ///
+ /// RPL_TRACECONNECTING RPL_TRACEHANDSHAKE
+ /// RPL_TRACEUNKNOWN RPL_TRACEOPERATOR
+ /// RPL_TRACEUSER RPL_TRACESERVER
+ /// RPL_TRACESERVICE RPL_TRACENEWTYPE
+ /// RPL_TRACECLASS RPL_TRACELOG
+ /// RPL_TRACEEND
+ ///
+ /// Examples:
+ ///
+ /// TRACE *.oulu.fi ; TRACE to a server matching
+ /// *.oulu.fi
+ /// ```
+ TRACE(Option<CS<'a>>),
+
+ /// ```text
+ /// 3.4.9 Admin command
+ ///
+ /// Command: ADMIN
+ /// Parameters: [ <target> ]
+ ///
+ /// The admin command is used to find information about the administrator
+ /// of the given server, or current server if <target> parameter is
+ /// omitted. Each server MUST have the ability to forward ADMIN messages
+ /// to other servers.
+ ///
+ /// Wildcards are allowed in the <target> parameter.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOSUCHSERVER
+ /// RPL_ADMINME RPL_ADMINLOC1
+ /// RPL_ADMINLOC2 RPL_ADMINEMAIL
+ ///
+ /// Examples:
+ ///
+ /// ADMIN tolsun.oulu.fi ; request an ADMIN reply from
+ /// tolsun.oulu.fi
+ ///
+ /// ADMIN syrk ; ADMIN request for the server to
+ /// which the user syrk is connected
+ /// ```
+ ADMIN(Option<CS<'a>>),
+
+ /// ```text
+ /// 3.4.10 Info command
+ ///
+ /// Command: INFO
+ /// Parameters: [ <target> ]
+ ///
+ /// The INFO command is REQUIRED to return information describing the
+ /// server: its version, when it was compiled, the patchlevel, when it
+ /// was started, and any other miscellaneous information which may be
+ /// considered to be relevant.
+ ///
+ /// Wildcards are allowed in the <target> parameter.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOSUCHSERVER
+ /// RPL_INFO RPL_ENDOFINFO
+ ///
+ /// Examples:
+ ///
+ /// INFO csd.bu.edu ; request an INFO reply from
+ /// csd.bu.edu
+ ///
+ /// INFO Angel ; request info from the server that
+ /// Angel is connected to.
+ /// ```
+ INFO(Option<CS<'a>>),
+
+ /// ```text
+ /// 3.5.1 Servlist message
+ ///
+ /// Command: SERVLIST
+ /// Parameters: [ <mask> [ <type> ] ]
+ ///
+ /// The SERVLIST command is used to list services currently connected to
+ /// the network and visible to the user issuing the command. The
+ /// optional parameters may be used to restrict the result of the query
+ /// (to matching services names, and services type).
+ ///
+ /// Numeric Replies:
+ ///
+ /// RPL_SERVLIST RPL_SERVLISTEND
+ /// ```
+ SERVLIST(Option<(CS<'a>, Option<CS<'a>>)>),
+
+ /// ```text
+ /// 3.5.2 Squery
+ ///
+ /// Command: SQUERY
+ /// Parameters: <servicename> <text>
+ ///
+ /// The SQUERY command is used similarly to PRIVMSG. The only difference
+ /// is that the recipient MUST be a service. This is the only way for a
+ /// text message to be delivered to a service.
+ ///
+ /// See PRIVMSG for more details on replies and example.
+ ///
+ /// Examples:
+ ///
+ /// SQUERY irchelp :HELP privmsg
+ /// ; Message to the service with
+ /// nickname irchelp.
+ ///
+ /// SQUERY dict@irc.fr :fr2en blaireau
+ /// ; Message to the service with name
+ /// dict@irc.fr.
+ /// ```
+ SQUERY(CS<'a>, CS<'a>),
+
+ /// ```text
+ /// 3.6.1 Who query
+ ///
+ /// Command: WHO
+ /// Parameters: [ <mask> [ "o" ] ]
+ ///
+ /// The WHO command is used by a client to generate a query which returns
+ /// a list of information which 'matches' the <mask> parameter given by
+ /// the client. In the absence of the <mask> parameter, all visible
+ /// (users who aren't invisible (user mode +i) and who don't have a
+ /// common channel with the requesting client) are listed. The same
+ /// result can be achieved by using a <mask> of "0" or any wildcard which
+ /// will end up matching every visible user.
+ ///
+ /// The <mask> passed to WHO is matched against users' host, server, real
+ /// name and nickname if the channel <mask> cannot be found.
+ ///
+ /// If the "o" parameter is passed only operators are returned according
+ /// to the <mask> supplied.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOSUCHSERVER
+ /// RPL_WHOREPLY RPL_ENDOFWHO
+ ///
+ /// Examples:
+ ///
+ /// WHO *.fi ; Command to list all users who match
+ /// against "*.fi".
+ ///
+ /// WHO jto* o ; Command to list all users with a
+ /// match against "jto*" if they are an
+ /// operator.
+ /// ```
+ WHO(Option<CS<'a>>, bool),
+
+ /// ```text
+ /// 3.6.2 Whois query
+ ///
+ /// Command: WHOIS
+ /// Parameters: [ <target> ] <mask> *( "," <mask> )
+ ///
+ /// This command is used to query information about particular user.
+ /// The server will answer this command with several numeric messages
+ /// indicating different statuses of each user which matches the mask (if
+ /// you are entitled to see them). If no wildcard is present in the
+ /// <mask>, any information about that nick which you are allowed to see
+ /// is presented.
+ ///
+ /// If the <target> parameter is specified, it sends the query to a
+ /// specific server. It is useful if you want to know how long the user
+ /// in question has been idle as only local server (i.e., the server the
+ /// user is directly connected to) knows that information, while
+ /// everything else is globally known.
+ ///
+ /// Wildcards are allowed in the <target> parameter.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOSUCHSERVER ERR_NONICKNAMEGIVEN
+ /// RPL_WHOISUSER RPL_WHOISCHANNELS
+ /// RPL_WHOISCHANNELS RPL_WHOISSERVER
+ /// RPL_AWAY RPL_WHOISOPERATOR
+ /// RPL_WHOISIDLE ERR_NOSUCHNICK
+ /// RPL_ENDOFWHOIS
+ ///
+ /// Examples:
+ ///
+ /// WHOIS wiz ; return available user information
+ /// about nick WiZ
+ ///
+ /// WHOIS eff.org trillian ; ask server eff.org for user
+ /// information about trillian
+ /// ```
+ WHOIS(Option<CS<'a>>, Vec<CS<'a>>),
+
+ /// ```text
+ /// 3.6.3 Whowas
+ ///
+ /// Command: WHOWAS
+ /// Parameters: <nickname> *( "," <nickname> ) [ <count> [ <target> ] ]
+ ///
+ /// Whowas asks for information about a nickname which no longer exists.
+ /// This may either be due to a nickname change or the user leaving IRC.
+ /// In response to this query, the server searches through its nickname
+ /// history, looking for any nicks which are lexically the same (no wild
+ /// card matching here). The history is searched backward, returning the
+ /// most recent entry first. If there are multiple entries, up to
+ /// <count> replies will be returned (or all of them if no <count>
+ /// parameter is given). If a non-positive number is passed as being
+ /// <count>, then a full search is done.
+ ///
+ /// Wildcards are allowed in the <target> parameter.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NONICKNAMEGIVEN ERR_WASNOSUCHNICK
+ /// RPL_WHOWASUSER RPL_WHOISSERVER
+ /// RPL_ENDOFWHOWAS
+ ///
+ /// Examples:
+ ///
+ /// WHOWAS Wiz ; return all information in the nick
+ /// history about nick "WiZ";
+ ///
+ /// WHOWAS Mermaid 9 ; return at most, the 9 most recent
+ /// entries in the nick history for
+ /// "Mermaid";
+ ///
+ /// WHOWAS Trillian 1 *.edu ; return the most recent history for
+ /// "Trillian" from the first server
+ /// found to match "*.edu".
+ /// ```
+ WHOWAS(Vec<CS<'a>>, Option<(CS<'a>, Option<CS<'a>>)>),
+
+ /// ```text
+ /// 3.7.1 Kill message
+ ///
+ /// Command: KILL
+ /// Parameters: <nickname> <comment>
+ ///
+ /// The KILL command is used to cause a client-server connection to be
+ /// closed by the server which has the actual connection. Servers
+ /// generate KILL messages on nickname collisions. It MAY also be
+ /// available available to users who have the operator status.
+ ///
+ /// Clients which have automatic reconnect algorithms effectively make
+ /// this command useless since the disconnection is only brief. It does
+ /// however break the flow of data and can be used to stop large amounts
+ /// of 'flooding' from abusive users or accidents. Abusive users usually
+ /// don't care as they will reconnect promptly and resume their abusive
+ /// behaviour. To prevent this command from being abused, any user may
+ /// elect to receive KILL messages generated for others to keep an 'eye'
+ /// on would be trouble spots.
+ ///
+ /// In an arena where nicknames are REQUIRED to be globally unique at all
+ /// times, KILL messages are sent whenever 'duplicates' are detected
+ /// (that is an attempt to register two users with the same nickname) in
+ /// the hope that both of them will disappear and only 1 reappear.
+ ///
+ /// When a client is removed as the result of a KILL message, the server
+ /// SHOULD add the nickname to the list of unavailable nicknames in an
+ /// attempt to avoid clients to reuse this name immediately which is
+ /// usually the pattern of abusive behaviour often leading to useless
+ /// "KILL loops". See the "IRC Server Protocol" document [IRC-SERVER]
+ /// for more information on this procedure.
+ ///
+ /// The comment given MUST reflect the actual reason for the KILL. For
+ /// server-generated KILLs it usually is made up of details concerning
+ /// the origins of the two conflicting nicknames. For users it is left
+ /// up to them to provide an adequate reason to satisfy others who see
+ /// it. To prevent/discourage fake KILLs from being generated to hide
+ /// the identify of the KILLer, the comment also shows a 'kill-path'
+ /// which is updated by each server it passes through, each prepending
+ /// its name to the path.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOPRIVILEGES ERR_NEEDMOREPARAMS
+ /// ERR_NOSUCHNICK ERR_CANTKILLSERVER
+ ///
+ /// NOTE:
+ /// It is RECOMMENDED that only Operators be allowed to kill other users
+ /// with KILL command. This command has been the subject of many
+ /// controversies over the years, and along with the above
+ /// recommendation, it is also widely recognized that not even operators
+ /// should be allowed to kill users on remote servers.
+ /// ```
+ KILL(CS<'a>, CS<'a>),
+
+ /// ```text
+ /// 3.7.2 Ping message
+ ///
+ /// Command: PING
+ /// Parameters: <server1> [ <server2> ]
+ ///
+ /// The PING command is used to test the presence of an active client or
+ /// server at the other end of the connection. Servers send a PING
+ /// message at regular intervals if no other activity detected coming
+ /// from a connection. If a connection fails to respond to a PING
+ /// message within a set amount of time, that connection is closed. A
+ /// PING message MAY be sent even if the connection is active.
+ ///
+ /// When a PING message is received, the appropriate PONG message MUST be
+ /// sent as reply to <server1> (server which sent the PING message out)
+ /// as soon as possible. If the <server2> parameter is specified, it
+ /// represents the target of the ping, and the message gets forwarded
+ /// there.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOORIGIN ERR_NOSUCHSERVER
+ ///
+ /// Examples:
+ ///
+ /// PING tolsun.oulu.fi ; Command to send a PING message to
+ /// server
+ ///
+ /// PING WiZ tolsun.oulu.fi ; Command from WiZ to send a PING
+ /// message to server "tolsun.oulu.fi"
+ ///
+ /// PING :irc.funet.fi ; Ping message sent by server
+ /// "irc.funet.fi"
+ /// ```
+ PING(CS<'a>, Option<CS<'a>>),
+
+ /// ```text
+ /// 3.7.3 Pong message
+ ///
+ /// Command: PONG
+ /// Parameters: <server> [ <server2> ]
+ ///
+ /// PONG message is a reply to ping message. If parameter <server2> is
+ /// given, this message MUST be forwarded to given target. The <server>
+ /// parameter is the name of the entity who has responded to PING message
+ /// and generated this message.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOORIGIN ERR_NOSUCHSERVER
+ ///
+ /// Example:
+ ///
+ /// PONG csd.bu.edu tolsun.oulu.fi ; PONG message from csd.bu.edu to
+ /// tolsun.oulu.fi
+ /// ```
+ PONG(CS<'a>, Option<CS<'a>>),
+
+ /// ```text
+ /// 3.7.4 Error
+ ///
+ /// Command: ERROR
+ /// Parameters: <error message>
+ ///
+ /// The ERROR command is for use by servers when reporting a serious or
+ /// fatal error to its peers. It may also be sent from one server to
+ /// another but MUST NOT be accepted from any normal unknown clients.
+ ///
+ /// Only an ERROR message SHOULD be used for reporting errors which occur
+ /// with a server-to-server link. An ERROR message is sent to the server
+ /// at the other end (which reports it to appropriate local users and
+ /// logs) and to appropriate local users and logs. It is not to be
+ /// passed onto any other servers by a server if it is received from a
+ /// server.
+ ///
+ /// The ERROR message is also used before terminating a client
+ /// connection.
+ ///
+ /// When a server sends a received ERROR message to its operators, the
+ /// message SHOULD be encapsulated inside a NOTICE message, indicating
+ /// that the client was not responsible for the error.
+ ///
+ /// Numerics:
+ ///
+ /// None.
+ ///
+ /// Examples:
+ ///
+ /// ERROR :Server *.fi already exists ; ERROR message to the other server
+ /// which caused this error.
+ ///
+ /// NOTICE WiZ :ERROR from csd.bu.edu -- Server *.fi already exists
+ /// ; Same ERROR message as above but
+ /// sent to user WiZ on the other server.
+ /// ```
+ ERROR(CS<'a>),
+
+ /// ```text
+ /// 4.1 Away
+ ///
+ /// Command: AWAY
+ /// Parameters: [ <text> ]
+ ///
+ /// With the AWAY command, clients can set an automatic reply string for
+ /// any PRIVMSG commands directed at them (not to a channel they are on).
+ /// The server sends an automatic reply to the client sending the PRIVMSG
+ /// command. The only replying server is the one to which the sending
+ /// client is connected to.
+ ///
+ /// The AWAY command is used either with one parameter, to set an AWAY
+ /// message, or with no parameters, to remove the AWAY message.
+ ///
+ /// Because of its high cost (memory and bandwidth wise), the AWAY
+ /// message SHOULD only be used for client-server communication. A
+ /// server MAY choose to silently ignore AWAY messages received from
+ /// other servers. To update the away status of a client across servers,
+ /// the user mode 'a' SHOULD be used instead. (See Section 3.1.5)
+ ///
+ /// Numeric Replies:
+ ///
+ /// RPL_UNAWAY RPL_NOWAWAY
+ ///
+ /// Example:
+ ///
+ /// AWAY :Gone to lunch. Back in 5 ; Command to set away message to
+ /// "Gone to lunch. Back in 5".
+ /// ```
+ AWAY(Option<CS<'a>>),
+
+ /// ```text
+ /// 4.2 Rehash message
+ ///
+ /// Command: REHASH
+ /// Parameters: None
+ ///
+ /// The rehash command is an administrative command which can be used by
+ /// an operator to force the server to re-read and process its
+ /// configuration file.
+ ///
+ /// Numeric Replies:
+ ///
+ /// RPL_REHASHING ERR_NOPRIVILEGES
+ ///
+ /// Example:
+ ///
+ /// REHASH ; message from user with operator
+ /// status to server asking it to reread
+ /// its configuration file.
+ /// ```
+ REHASH,
+
+ /// ```text
+ /// 4.3 Die message
+ ///
+ /// Command: DIE
+ /// Parameters: None
+ ///
+ /// An operator can use the DIE command to shutdown the server. This
+ /// message is optional since it may be viewed as a risk to allow
+ /// arbitrary people to connect to a server as an operator and execute
+ /// this command.
+ ///
+ /// The DIE command MUST always be fully processed by the server to which
+ /// the sending client is connected and MUST NOT be passed onto other
+ /// connected servers.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOPRIVILEGES
+ ///
+ /// Example:
+ ///
+ /// DIE ; no parameters required.
+ /// ```
+ DIE,
+
+ /// ```text
+ /// 4.4 Restart message
+ ///
+ /// Command: RESTART
+ /// Parameters: None
+ ///
+ /// An operator can use the restart command to force the server to
+ /// restart itself. This message is optional since it may be viewed as a
+ /// risk to allow arbitrary people to connect to a server as an operator
+ /// and execute this command, causing (at least) a disruption to service.
+ ///
+ /// The RESTART command MUST always be fully processed by the server to
+ /// which the sending client is connected and MUST NOT be passed onto
+ /// other connected servers.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOPRIVILEGES
+ ///
+ /// Example:
+ ///
+ /// RESTART ; no parameters required.
+ /// ```
+ RESTART,
+
+ /// ```text
+ /// 4.5 Summon message
+ ///
+ /// Command: SUMMON
+ /// Parameters: <user> [ <target> [ <channel> ] ]
+ ///
+ /// The SUMMON command can be used to give users who are on a host
+ /// running an IRC server a message asking them to please join IRC. This
+ /// message is only sent if the target server (a) has SUMMON enabled, (b)
+ /// the user is logged in and (c) the server process can write to the
+ /// user's tty (or similar).
+ ///
+ /// If no <server> parameter is given it tries to summon <user> from the
+ /// server the client is connected to is assumed as the target.
+ ///
+ /// If summon is not enabled in a server, it MUST return the
+ /// ERR_SUMMONDISABLED numeric.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NORECIPIENT ERR_FILEERROR
+ /// ERR_NOLOGIN ERR_NOSUCHSERVER
+ /// ERR_SUMMONDISABLED RPL_SUMMONING
+ ///
+ /// Examples:
+ ///
+ /// SUMMON jto ; summon user jto on the server's
+ /// host
+ ///
+ /// SUMMON jto tolsun.oulu.fi ; summon user jto on the host which a
+ /// server named "tolsun.oulu.fi" is
+ /// running.
+ /// ```
+ SUMMON(CS<'a>, Option<(CS<'a>, Option<CS<'a>>)>),
+
+ /// ```text
+ /// 4.6 Users
+ ///
+ /// Command: USERS
+ /// Parameters: [ <target> ]
+ ///
+ /// The USERS command returns a list of users logged into the server in a
+ /// format similar to the UNIX commands who(1), rusers(1) and finger(1).
+ /// If disabled, the correct numeric MUST be returned to indicate this.
+ ///
+ /// Because of the security implications of such a command, it SHOULD be
+ /// disabled by default in server implementations. Enabling it SHOULD
+ /// require recompiling the server or some equivalent change rather than
+ /// simply toggling an option and restarting the server. The procedure
+ /// to enable this command SHOULD also include suitable large comments.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NOSUCHSERVER ERR_FILEERROR
+ /// RPL_USERSSTART RPL_USERS
+ /// RPL_NOUSERS RPL_ENDOFUSERS
+ /// ERR_USERSDISABLED
+ ///
+ /// Disabled Reply:
+ ///
+ /// ERR_USERSDISABLED
+ ///
+ /// Example:
+ ///
+ /// USERS eff.org ; request a list of users logged in
+ /// on server eff.org
+ /// ```
+ USERS(Option<CS<'a>>),
+
+ /// ```text
+ /// 4.7 Operwall message
+ ///
+ /// Command: WALLOPS
+ /// Parameters: <Text to be sent>
+ ///
+ /// The WALLOPS command is used to send a message to all currently
+ /// connected users who have set the 'w' user mode for themselves. (See
+ /// Section 3.1.5 "User modes").
+ ///
+ /// After implementing WALLOPS as a user command it was found that it was
+ /// often and commonly abused as a means of sending a message to a lot of
+ /// people. Due to this, it is RECOMMENDED that the implementation of
+ /// WALLOPS allows and recognizes only servers as the originators of
+ /// WALLOPS.
+ ///
+ /// Numeric Replies:
+ ///
+ /// ERR_NEEDMOREPARAMS
+ ///
+ /// Example:
+ ///
+ /// :csd.bu.edu WALLOPS :Connect '*.uiuc.edu 6667' from Joshua ; WALLOPS
+ /// message from csd.bu.edu announcing a
+ /// CONNECT message it received from
+ /// Joshua and acted upon.
+ /// ```
+ WALLOPS(CS<'a>),
+
+ /// ```text
+ /// 4.8 Userhost message
+ ///
+ /// Command: USERHOST
+ /// Parameters: <nickname> *( SPACE <nickname> )
+ ///
+ /// The USERHOST command takes a list of up to 5 nicknames, each
+ /// separated by a space character and returns a list of information
+ /// about each nickname that it found. The returned list has each reply
+ /// separated by a space.
+ ///
+ /// Numeric Replies:
+ ///
+ /// RPL_USERHOST ERR_NEEDMOREPARAMS
+ ///
+ /// Example:
+ ///
+ /// USERHOST Wiz Michael syrk ; USERHOST request for information on
+ /// nicks "Wiz", "Michael", and "syrk"
+ ///
+ /// :ircd.stealth.net 302 yournick :syrk=+syrk@millennium.stealth.net
+ /// ; Reply for user syrk
+ /// ```
+ USERHOST(Vec<CS<'a>>),
+}
+
+/*impl<'a> Clone for Command<'a> {
+ fn clone(&self) -> Command<'a> {
+ use self::Command::*;
+ match self {
+ &PASS(ref pw) => PASS(pw.to_owned().clone()),
+ /*&NICK(ref nick) =>
+ Message::format(None, Borrowed("NICK"), vec![], Some(nick.clone()), MsgType::Irc),
+ &USER(ref user, ref mode, ref unused, ref realname) =>
+ Message::format(None, Borrowed("USER"),
+ vec![user.clone(), mode.clone(), unused.clone()],
+ Some(realname.clone()), MsgType::Irc),
+ &OPER(ref name, ref pw) =>
+ Message::format(None, Borrowed("OPER"),
+ vec![name.clone(), pw.clone()], None, MsgType::Irc),
+ &UMODE(ref mode) =>
+ Message::format(None, Borrowed("MODE"), vec![], Some(mode.clone()), MsgType::Irc),
+ &SERVICE(ref nick, ref reserved, ref distribution, ref type_, ref reserved2, ref info) =>
+ Message::format(None, Borrowed("SERVICE"),
+ vec![nick.clone(), reserved.clone(), distribution.clone(),
+ type_.clone(), reserved2.clone()], Some(info.clone()), MsgType::Irc),
+ &QUIT(ref msg) =>
+ Message::format(None, Borrowed("QUIT"), vec![], msg.clone(), MsgType::Irc),
+ &SQUIT(ref server, ref comment) =>
+ Message::format(None, Borrowed("SQUIT"),
+ vec![server.clone()], Some(comment.clone()), MsgType::Irc),
+ &JOIN(ref ch, ref pw) =>
+ Message::format(None, Borrowed("JOIN"),
+ vec![Owned(ch.connect(",")), Owned(pw.connect(","))], None, MsgType::Irc),
+ &PART(ref ch, ref reason) =>
+ Message::format(None, Borrowed("PART"),
+ vec![Owned(ch.connect(","))], reason.clone(), MsgType::Irc),
+ &MODE(ref channel, ref modes) =>
+ // Screw this, folding will have to do.
+ Message::format(None, Borrowed("MODE"),
+ modes.iter().fold(vec![channel.clone()], |mut v, &(ref a, ref b)| {
+ v.push(a.clone());
+ v.push(b.clone());
+ v
+ }), None, MsgType::Irc),
+ &TOPIC(ref channel, ref topic) =>
+ Message::format(None, Borrowed("TOPIC"),
+ vec![channel.clone()], topic.clone(), MsgType::Irc),
+ &NAMES(ref ch, ref target) =>
+ Message::format(None, Borrowed("NAMES"),
+ vec![Owned(ch.connect(","))], target.clone(), MsgType::Irc),
+ &LIST(ref ch, ref target) =>
+ Message::format(None, Borrowed("LIST"),
+ vec![Owned(ch.connect(","))], target.clone(), MsgType::Irc),
+ &INVITE(ref nick, ref channel) =>
+ Message::format(None, Borrowed("INVITE"),
+ vec![nick.clone()], Some(channel.clone()), MsgType::Irc),
+ &KICK(ref ch, ref users, ref comment) =>
+ Message::format(None, Borrowed("KICK"),
+ vec![Owned(ch.connect(",")), Owned(users.connect(","))],
+ comment.clone(), MsgType::Irc),
+ &PRIVMSG(ref target, ref msg) =>
+ Message::format(None, Borrowed("PRIVMSG"),
+ vec![target.clone()], Some(msg.clone()), MsgType::Irc),
+ &NOTICE(ref target, ref text) =>
+ Message::format(None, Borrowed("NOTICE"),
+ vec![target.clone()], Some(text.clone()), MsgType::Irc),
+ &MOTD(ref target) =>
+ Message::format(None, Borrowed("MOTD"), vec![], target.clone(), MsgType::Irc),
+ &LUSERS(ref lu) =>
+ Message::format(None, Borrowed("LUSERS"),
+ lu.as_ref().map(|&(ref mask, _)| vec![mask.clone()]).unwrap_or(vec![]),
+ lu.as_ref().and_then(|&(_, ref target)| target.clone()), MsgType::Irc),
+ &VERSION(ref target) =>
+ Message::format(None, Borrowed("VERSION"), vec![], target.clone(), MsgType::Irc),
+ &STATS(ref st) =>
+ Message::format(None, Borrowed("STATS"),
+ st.as_ref().map(|&(ref query, _)| vec![query.clone()]).unwrap_or(vec![]),
+ st.as_ref().and_then(|&(_, ref target)| target.clone()), MsgType::Irc),
+ &LINKS(ref l) =>
+ Message::format(None, Borrowed("LINKS"),
+ l.as_ref().map(|&(ref remote, ref mask)| if remote.is_some() {
+ vec![remote.clone().unwrap(), mask.clone()] } else { vec![mask.clone()] }).unwrap_or(vec![]),
+ None, MsgType::Irc),
+ &TIME(ref target) =>
+ Message::format(None, Borrowed("TIME"), vec![], target.clone(), MsgType::Irc),
+ &CONNECT(ref server, ref port, ref remote) =>
+ Message::format(None, Borrowed("CONNECT"),
+ vec![server.clone(), Owned(format!("{}", port))], remote.clone(), MsgType::Irc),
+ &TRACE(ref target) =>
+ Message::format(None, Borrowed("TRACE"), vec![], target.clone(), MsgType::Irc),
+ &ADMIN(ref target) =>
+ Message::format(None, Borrowed("ADMIN"), vec![], target.clone(), MsgType::Irc),
+ &INFO(ref target) =>
+ Message::format(None, Borrowed("INFO"), vec![], target.clone(), MsgType::Irc),
+ &SERVLIST(ref sl) =>
+ Message::format(None, Borrowed("SERVLIST"),
+ sl.as_ref().map(|&(ref mask, ref target)| target.as_ref()
+ .map(|t| vec![mask.clone(), t.clone()])
+ .unwrap_or_else(|| vec![mask.clone()]))
+ .unwrap_or(vec![]), None, MsgType::Irc),
+ &SQUERY(ref name, ref text) =>
+ Message::format(None, Borrowed("SQUERY"),
+ vec![name.clone()], Some(text.clone()), MsgType::Irc),
+ &WHO(ref mask, o) =>
+ Message::format(None, Borrowed("WHO"),
+ match (mask, o) {
+ (&Some(ref m), true) => vec![m.clone(), Borrowed("o")],
+ (&Some(ref m), false) => vec![m.clone()],
+ (&None, _) => vec![]
+ }, None, MsgType::Irc),
+ &WHOIS(ref target, ref masks) =>
+ Message::format(None, Borrowed("WHOIS"),
+ target.as_ref().map(|t| vec![t.clone(), Owned(masks.connect(","))])
+ .unwrap_or_else(|| vec![Owned(masks.connect(","))]), None, MsgType::Irc),
+ &WHOWAS(ref nick, ref count) =>
+ Message::format(None, Borrowed("WHOWAS"), match count {
+ &Some((ref c, Some(ref t))) => vec![Owned(nick.connect(",")), c.clone(), t.clone()],
+ &Some((ref c, None)) => vec![Owned(nick.connect(",")), c.clone()],
+ &None => vec![Owned(nick.connect(","))]
+ }, None, MsgType::Irc),
+ &PING(ref s1, ref s2) =>
+ Message::format(None, Borrowed("PING"), vec![s1.clone()], s2.clone(), MsgType::Irc),
+ &PONG(ref s1, ref s2) =>
+ Message::format(None, Borrowed("PONG"), vec![s1.clone()], s2.clone(), MsgType::Irc),
+ */ /*&Command::PING(ref server1, ref server2) => {
+ let mut c = Vec::new();
+ c.push(server1.clone());
+ if let &Some(ref s) = server2 { c.push(s.clone()) }
+ Message::format(None, "PING", c, None, MsgType::Irc)
+ },
+ &Command::PONG(ref server1, ref server2) => {
+ let mut c = Vec::new();
+ c.push(server1.clone());
+ if let &Some(ref s) = server2 { c.push(s.clone()) }
+ Message::format(None, "PONG", c, None, MsgType::Irc)
+ },*/
+ _ => unimplemented!()
+ }
+
+ }
+}*/
+
+impl<'a>Command<'a> {
+ pubfnfrom_message(msg: &'aMessage) ->Option<Command<'a>> {
+ useself::Command::*;
+ matchmsg.command() {
+ "PASS"=>msg.elements().last().map(|&m|m).map(Borrowed).map(PASS),
+ "NICK"=>msg.suffix().or_else(||msg.content().last().map(|&m|m))
+ .map(Borrowed).map(NICK),
+ "USER"=>iflet [user, mode, unused, realname, ..] =msg.elements().as_ref() {
+ Some(USER(Borrowed(user), Borrowed(mode),
+ Borrowed(unused), Borrowed(realname)))
+ } else { None },
+ "OPER"=>iflet [name, pw, ..] =msg.elements().as_ref() {
+ Some(OPER(Borrowed(name), Borrowed(pw)))
+ } else { None },
+ "PING"=> {
+ lete=msg.elements();
+ e.first().map(|s1|PING(Borrowed(s1), e.get(1).map(|&m|m).map(Borrowed)))
+ },
+ "PONG"=> {
+ lete=msg.elements();
+ e.first().map(|s1|PONG(Borrowed(s1), e.get(1).map(|&m|m).map(Borrowed)))
+ },
+ "JOIN"=>iflet [ch, pw, ..] =msg.elements().as_ref() {
+ Some(JOIN(ch.split(",").map(Borrowed).collect(),
+ pw.split(",").map(Borrowed).collect()))
+ } else { None },
+ "PART"=>iflet [ch, reason..] =msg.elements().as_ref() {
+ Some(PART(ch.split(",").map(Borrowed).collect(),
+ reason.first().map(|&m|m).map(Borrowed)))
+ } else { None },
+ "PRIVMSG"=>iflet [target, msg, ..] =msg.elements().as_ref() {
+ Some(PRIVMSG(Borrowed(target), Borrowed(msg)))
+ } else { None },
+ "NOTICE"=>iflet [target, msg, ..] =msg.elements().as_ref() {
+ Some(NOTICE(Borrowed(target), Borrowed(msg)))
+ } else { None },
+ _ =>None
+ }
+ }
+
+ /// It started out pretty, but was quickly infested with `ref` and cloning.
+ /// I'm sorry, this one might not make it.
+ pubfnto_message(&'aself) ->Message {
+ useself::Command::*;
+ matchself {
+ &PASS(refpw) =>
+ Message::format(None, Borrowed("PASS"), vec![], Some(pw.clone()), MsgType::Irc),
+ &NICK(refnick) =>
+ Message::format(None, Borrowed("NICK"), vec![], Some(nick.clone()), MsgType::Irc),
+ &USER(refuser, refmode, refunused, refrealname) =>
+ Message::format(None, Borrowed("USER"),
+ vec![user.clone(), mode.clone(), unused.clone()],
+ Some(realname.clone()), MsgType::Irc),
+ &OPER(refname, refpw) =>
+ Message::format(None, Borrowed("OPER"),
+ vec![name.clone(), pw.clone()], None, MsgType::Irc),
+ &UMODE(refmode) =>
+ Message::format(None, Borrowed("MODE"), vec![], Some(mode.clone()), MsgType::Irc),
+ &SERVICE(refnick, refreserved, refdistribution, reftype_, refreserved2, refinfo) =>
+ Message::format(None, Borrowed("SERVICE"),
+ vec![nick.clone(), reserved.clone(), distribution.clone(),
+ type_.clone(), reserved2.clone()], Some(info.clone()), MsgType::Irc),
+ &QUIT(refmsg) =>
+ Message::format(None, Borrowed("QUIT"), vec![], msg.clone(), MsgType::Irc),
+ &SQUIT(refserver, refcomment) =>
+ Message::format(None, Borrowed("SQUIT"),
+ vec![server.clone()], Some(comment.clone()), MsgType::Irc),
+ &JOIN(refch, refpw) =>
+ Message::format(None, Borrowed("JOIN"),
+ vec![Owned(ch.connect(",")), Owned(pw.connect(","))], None, MsgType::Irc),
+ &PART(refch, refreason) =>
+ Message::format(None, Borrowed("PART"),
+ vec![Owned(ch.connect(","))], reason.clone(), MsgType::Irc),
+ &MODE(refchannel, refmodes) =>
+ // Screw this, folding will have to do.
+ Message::format(None, Borrowed("MODE"),
+ modes.iter().fold(vec![channel.clone()], |mutv, &(refa, refb)| {
+ v.push(a.clone());
+ v.push(b.clone());
+ v
+ }), None, MsgType::Irc),
+ &TOPIC(refchannel, reftopic) =>
+ Message::format(None, Borrowed("TOPIC"),
+ vec![channel.clone()], topic.clone(), MsgType::Irc),
+ &NAMES(refch, reftarget) =>
+ Message::format(None, Borrowed("NAMES"),
+ vec![Owned(ch.connect(","))], target.clone(), MsgType::Irc),
+ &LIST(refch, reftarget) =>
+ Message::format(None, Borrowed("LIST"),
+ vec![Owned(ch.connect(","))], target.clone(), MsgType::Irc),
+ &INVITE(refnick, refchannel) =>
+ Message::format(None, Borrowed("INVITE"),
+ vec![nick.clone()], Some(channel.clone()), MsgType::Irc),
+ &KICK(refch, refusers, refcomment) =>
+ Message::format(None, Borrowed("KICK"),
+ vec![Owned(ch.connect(",")), Owned(users.connect(","))],
+ comment.clone(), MsgType::Irc),
+ &PRIVMSG(reftarget, refmsg) =>
+ Message::format(None, Borrowed("PRIVMSG"),
+ vec![target.clone()], Some(msg.clone()), MsgType::Irc),
+ &NOTICE(reftarget, reftext) =>
+ Message::format(None, Borrowed("NOTICE"),
+ vec![target.clone()], Some(text.clone()), MsgType::Irc),
+ &MOTD(reftarget) =>
+ Message::format(None, Borrowed("MOTD"), vec![], target.clone(), MsgType::Irc),
+ &LUSERS(reflu) =>
+ Message::format(None, Borrowed("LUSERS"),
+ lu.as_ref().map(|&(refmask, _)|vec![mask.clone()]).unwrap_or(vec![]),
+ lu.as_ref().and_then(|&(_, reftarget)|target.clone()), MsgType::Irc),
+ &VERSION(reftarget) =>
+ Message::format(None, Borrowed("VERSION"), vec![], target.clone(), MsgType::Irc),
+ &STATS(refst) =>
+ Message::format(None, Borrowed("STATS"),
+ st.as_ref().map(|&(refquery, _)|vec![query.clone()]).unwrap_or(vec![]),
+ st.as_ref().and_then(|&(_, reftarget)|target.clone()), MsgType::Irc),
+ &LINKS(refl) =>
+ Message::format(None, Borrowed("LINKS"),
+ l.as_ref().map(|&(refremote, refmask)|ifremote.is_some() {
+ vec![remote.clone().unwrap(), mask.clone()] } else { vec![mask.clone()] }).unwrap_or(vec![]),
+ None, MsgType::Irc),
+ &TIME(reftarget) =>
+ Message::format(None, Borrowed("TIME"), vec![], target.clone(), MsgType::Irc),
+ &CONNECT(refserver, refport, refremote) =>
+ Message::format(None, Borrowed("CONNECT"),
+ vec![server.clone(), Owned(format!("{}", port))], remote.clone(), MsgType::Irc),
+ &TRACE(reftarget) =>
+ Message::format(None, Borrowed("TRACE"), vec![], target.clone(), MsgType::Irc),
+ &ADMIN(reftarget) =>
+ Message::format(None, Borrowed("ADMIN"), vec![], target.clone(), MsgType::Irc),
+ &INFO(reftarget) =>
+ Message::format(None, Borrowed("INFO"), vec![], target.clone(), MsgType::Irc),
+ &SERVLIST(refsl) =>
+ Message::format(None, Borrowed("SERVLIST"),
+ sl.as_ref().map(|&(refmask, reftarget)|target.as_ref()
+ .map(|t|vec![mask.clone(), t.clone()])
+ .unwrap_or_else(||vec![mask.clone()]))
+ .unwrap_or(vec![]), None, MsgType::Irc),
+ &SQUERY(refname, reftext) =>
+ Message::format(None, Borrowed("SQUERY"),
+ vec![name.clone()], Some(text.clone()), MsgType::Irc),
+ &WHO(refmask, o) =>
+ Message::format(None, Borrowed("WHO"),
+ match (mask, o) {
+ (&Some(refm), true) =>vec![m.clone(), Borrowed("o")],
+ (&Some(refm), false) =>vec![m.clone()],
+ (&None, _) =>vec![]
+ }, None, MsgType::Irc),
+ &WHOIS(reftarget, refmasks) =>
+ Message::format(None, Borrowed("WHOIS"),
+ target.as_ref().map(|t|vec![t.clone(), Owned(masks.connect(","))])
+ .unwrap_or_else(||vec![Owned(masks.connect(","))]), None, MsgType::Irc),
+ &WHOWAS(refnick, refcount) =>
+ Message::format(None, Borrowed("WHOWAS"), matchcount {
+ &Some((refc, Some(reft))) =>vec![Owned(nick.connect(",")), c.clone(), t.clone()],
+ &Some((refc, None)) =>vec![Owned(nick.connect(",")), c.clone()],
+ &None=>vec![Owned(nick.connect(","))]
+ }, None, MsgType::Irc),
+ &PING(refs1, refs2) =>
+ Message::format(None, Borrowed("PING"), vec![s1.clone()], s2.clone(), MsgType::Irc),
+ &PONG(refs1, refs2) =>
+ Message::format(None, Borrowed("PONG"), vec![s1.clone()], s2.clone(), MsgType::Irc),
+ _ =>unimplemented!()
+ }
+ }
+
+ pubfnto_static(&self) ->Command<'static> {
+ fns(c: &Cow<str>) ->Cow<'static, str> {
+ Cow::Owned(c.clone().into_owned())
+ }
+ fnso(c: &Option<Cow<str>>) ->Option<Cow<'static, str>> {
+ c.as_ref().map(s)
+ }
+ fnsv(c: &Vec<Cow<str>>) ->Vec<Cow<'static, str>> {
+ c.iter().map(s).collect()
+ }
+ useself::Command::*;
+ matchself {
+ &PASS(refpw) =>PASS(s(pw)),
+ &NICK(refnick) =>NICK(s(nick)),
+ &USER(refuser, refmode, refunused, refrealname) =>
+ USER(s(user), s(mode), s(unused), s(realname)),
+ &OPER(refname, refpw) =>OPER(s(name), s(pw)),
+ &UMODE(refmode) =>UMODE(s(mode)),
+ &SERVICE(refnick, refreserved, refdistribution, reftype_, refreserved2, refinfo) =>
+ SERVICE(s(nick), s(reserved), s(distribution), s(type_), s(reserved2), s(info)),
+ &QUIT(refmsg) =>QUIT(so(msg)),
+ &SQUIT(refserver, refcomment) =>SQUIT(s(server), s(comment)),
+ &JOIN(refch, refpw) =>JOIN(sv(ch), sv(pw)),
+ &PART(refch, refreason) =>PART(sv(ch), so(reason)),
+ &MODE(refchannel, refmodes) =>
+ MODE(s(channel), modes.iter().map(|&(refa, refb)| (s(a), s(b))).collect()),
+ &TOPIC(refchannel, reftopic) =>TOPIC(s(channel), so(topic)),
+ &NAMES(refch, reftarget) =>NAMES(sv(ch), so(target)),
+ &LIST(refch, reftarget) =>LIST(sv(ch), so(target)),
+ &INVITE(refnick, refchannel) =>INVITE(s(nick), s(channel)),
+ &KICK(refch, refusers, refcomment) =>KICK(sv(ch), sv(users), so(comment)),
+ &PRIVMSG(reftarget, refmsg) =>PRIVMSG(s(target), s(msg)),
+ &NOTICE(reftarget, reftext) =>NOTICE(s(target), s(text)),
+ &MOTD(reftarget) =>MOTD(so(target)),
+ &LUSERS(reflu) =>LUSERS(lu.as_ref().map(|&(refa, refb)| (s(a), so(b)))),
+ &VERSION(reftarget) =>VERSION(so(target)),
+ &STATS(refst) =>STATS(st.as_ref().map(|&(refa, refb)| (s(a), so(b)))),
+ &LINKS(refl) =>LINKS(l.as_ref().map(|&(refa, refb)| (so(a), s(b)))),
+ &TIME(reftarget) =>TIME(so(target)),
+ &CONNECT(refserver, port, refremote) =>CONNECT(s(server), port, so(remote)),
+ &TRACE(reftarget) =>TRACE(so(target)),
+ &ADMIN(reftarget) =>ADMIN(so(target)),
+ &INFO(reftarget) =>INFO(so(target)),
+ &SERVLIST(refsl) =>SERVLIST(sl.as_ref().map(|&(refa, refb)| (s(a), so(b)))),
+ &SQUERY(refname, reftext) =>SQUERY(s(name), s(text)),
+ &WHO(refmask, o) =>WHO(so(mask), o),
+ &WHOIS(reftarget, refmasks) =>WHOIS(so(target), sv(masks)),
+ &WHOWAS(refnick, refcount) =>
+ WHOWAS(sv(nick), count.as_ref().map(|&(refa, refb)| (s(a), so(b)))),
+ &PING(refs1, refs2) =>PING(s(s1), so(s2)),
+ &PONG(refs1, refs2) =>PONG(s(s1), so(s2)),
+ _ =>unimplemented!()
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+
+usestd::str::FromStr;
+usestd::borrow::{ Cow, ToOwned };
+usestd::borrow::Cow::*;
+
+use ::{ Result, IrscError };
+use ::message::{ MsgType, Message };
+
+pubtypeCS<'a>=Cow<'a, str>;
+
+#[allow(non_camel_case_types)]
+#[derive(Debug, Hash, Clone, PartialEq, Eq)]
+pubenumReply<'a> {
+ /// 001 RPL_WELCOME
+ /// "Welcome to the Internet Relay Network
+ /// <nick>!<user>@<host>"
+ RPL_WELCOME(CS<'a>),
+
+ /// 002 RPL_YOURHOST
+ /// "Your host is <servername>, running version <ver>"
+ RPL_YOURHOST(CS<'a>),
+
+ /// 003 RPL_CREATED
+ /// "This server was created <date>"
+ RPL_CREATED(CS<'a>),
+
+ /// 004 RPL_MYINFO
+ /// "<servername> <version> <available user modes>
+ /// <available channel modes>"
+ ///
+ /// - The server sends Replies 001 to 004 to a user upon
+ /// successful registration.
+ ///
+ RPL_MYINFO(CS<'a>),
+
+ /// 005 RPL_BOUNCE
+ /// "Try server <server name>, port <port number>"
+ ///
+ /// - Sent by the server to a user to suggest an alternative
+ /// server. This is often used when the connection is
+ /// refused because the server is already full.
+ ///
+ RPL_BOUNCE(CS<'a>),
+
+ /// 302 RPL_USERHOST
+ /// ":*1<reply> *( " " <reply> )"
+ ///
+ /// - Reply format used by USERHOST to list replies to
+ /// the query list. The reply string is composed as
+ /// follows:
+ ///
+ /// reply = nickname [ "*" ] "=" ( "+" / "-" ) hostname
+ ///
+ /// The '*' indicates whether the client has registered
+ /// as an Operator. The '-' or '+' characters represent
+ /// whether the client has set an AWAY message or not
+ /// respectively.
+ ///
+ RPL_USERHOST(CS<'a>),
+
+ /// 303 RPL_ISON
+ /// ":*1<nick> *( " " <nick> )"
+ ///
+ /// - Reply format used by ISON to list replies to the
+ /// query list.
+ ///
+ RPL_ISON(CS<'a>),
+
+ /// 301 RPL_AWAY
+ /// "<nick> :<away message>"
+ RPL_AWAY(CS<'a>),
+
+ /// 305 RPL_UNAWAY
+ /// ":You are no longer marked as being away"
+ RPL_UNAWAY(CS<'a>),
+
+ /// 306 RPL_NOWAWAY
+ /// ":You have been marked as being away"
+ ///
+ /// - These replies are used with the AWAY command (if
+ /// allowed). RPL_AWAY is sent to any client sending a
+ /// PRIVMSG to a client which is away. RPL_AWAY is only
+ /// sent by the server to which the client is connected.
+ /// Replies RPL_UNAWAY and RPL_NOWAWAY are sent when the
+ /// client removes and sets an AWAY message.
+ ///
+ RPL_NOWAWAY(CS<'a>),
+
+ /// 311 RPL_WHOISUSER
+ /// "<nick> <user> <host> * :<real name>"
+ RPL_WHOISUSER(CS<'a>),
+
+ /// 312 RPL_WHOISSERVER
+ /// "<nick> <server> :<server info>"
+ RPL_WHOISSERVER(CS<'a>),
+
+ /// 313 RPL_WHOISOPERATOR
+ /// "<nick> :is an IRC operator"
+ RPL_WHOISOPERATOR(CS<'a>),
+
+ /// 317 RPL_WHOISIDLE
+ /// "<nick> <integer> :seconds idle"
+ RPL_WHOISIDLE(CS<'a>),
+
+ /// 318 RPL_ENDOFWHOIS
+ /// "<nick> :End of WHOIS list"
+ RPL_ENDOFWHOIS(CS<'a>),
+
+ /// 319 RPL_WHOISCHANNELS
+ /// "<nick> :*( ( "@" / "+" ) <channel> " " )"
+ ///
+ /// - Replies 311 - 313, 317 - 319 are all replies
+ /// generated in response to a WHOIS message. Given that
+ /// there are enough parameters present, the answering
+ /// server MUST either formulate a reply out of the above
+ /// numerics (if the query nick is found) or return an
+ /// error reply. The '*' in RPL_WHOISUSER is there as
+ /// the literal character and not as a wild card. For
+ /// each reply set, only RPL_WHOISCHANNELS may appear
+ /// more than once (for long lists of channel names).
+ /// The '@' and '+' characters next to the channel name
+ /// indicate whether a client is a channel operator or
+ /// has been granted permission to speak on a moderated
+ /// channel. The RPL_ENDOFWHOIS reply is used to mark
+ /// the end of processing a WHOIS message.
+ ///
+ RPL_WHOISCHANNELS(CS<'a>),
+
+ /// 314 RPL_WHOWASUSER
+ /// "<nick> <user> <host> * :<real name>"
+ RPL_WHOWASUSER(CS<'a>),
+
+ /// 369 RPL_ENDOFWHOWAS
+ /// "<nick> :End of WHOWAS"
+ ///
+ /// - When replying to a WHOWAS message, a server MUST use
+ /// the replies RPL_WHOWASUSER, RPL_WHOISSERVER or
+ /// ERR_WASNOSUCHNICK for each nickname in the presented
+ /// list. At the end of all reply batches, there MUST
+ /// be RPL_ENDOFWHOWAS (even if there was only one reply
+ /// and it was an error).
+ ///
+ RPL_ENDOFWHOWAS(CS<'a>),
+
+ /// 321 RPL_LISTSTART
+ /// Obsolete. Not used.
+ ///
+ RPL_LISTSTART,
+
+ /// 322 RPL_LIST
+ /// "<channel> <# visible> :<topic>"
+ RPL_LIST(CS<'a>),
+
+ /// 323 RPL_LISTEND
+ /// ":End of LIST"
+ ///
+ /// - Replies RPL_LIST, RPL_LISTEND mark the actual replies
+ /// with data and end of the server's response to a LIST
+ /// command. If there are no channels available to return,
+ /// only the end reply MUST be sent.
+ ///
+ RPL_LISTEND(CS<'a>),
+
+ /// 325 RPL_UNIQOPIS
+ /// "<channel> <nickname>"
+ ///
+ RPL_UNIQOPIS(CS<'a>),
+
+ /// 324 RPL_CHANNELMODEIS
+ /// "<channel> <mode> <mode params>"
+ ///
+ RPL_CHANNELMODEIS(CS<'a>),
+
+ /// 331 RPL_NOTOPIC
+ /// "<channel> :No topic is set"
+ RPL_NOTOPIC(CS<'a>),
+
+ /// 332 RPL_TOPIC
+ /// "<channel> :<topic>"
+ ///
+ /// - When sending a TOPIC message to determine the
+ /// channel topic, one of two replies is sent. If
+ /// the topic is set, RPL_TOPIC is sent back else
+ /// RPL_NOTOPIC.
+ ///
+ RPL_TOPIC(CS<'a>),
+
+ /// 341 RPL_INVITING
+ /// "<channel> <nick>"
+ ///
+ /// - Returned by the server to indicate that the
+ /// attempted INVITE message was successful and is
+ /// being passed onto the end client.
+ ///
+ RPL_INVITING(CS<'a>),
+
+ /// 342 RPL_SUMMONING
+ /// "<user> :Summoning user to IRC"
+ ///
+ /// - Returned by a server answering a SUMMON message to
+ /// indicate that it is summoning that user.
+ ///
+ RPL_SUMMONING(CS<'a>),
+
+ /// 346 RPL_INVITELIST
+ /// "<channel> <invitemask>"
+ RPL_INVITELIST(CS<'a>),
+
+ /// 347 RPL_ENDOFINVITELIST
+ /// "<channel> :End of channel invite list"
+ ///
+ /// - When listing the 'invitations masks' for a given channel,
+ /// a server is required to send the list back using the
+ /// RPL_INVITELIST and RPL_ENDOFINVITELIST messages. A
+ /// separate RPL_INVITELIST is sent for each active mask.
+ /// After the masks have been listed (or if none present) a
+ /// RPL_ENDOFINVITELIST MUST be sent.
+ ///
+ RPL_ENDOFINVITELIST(CS<'a>),
+
+ /// 348 RPL_EXCEPTLIST
+ /// "<channel> <exceptionmask>"
+ RPL_EXCEPTLIST(CS<'a>),
+
+ /// 349 RPL_ENDOFEXCEPTLIST
+ /// "<channel> :End of channel exception list"
+ ///
+ /// - When listing the 'exception masks' for a given channel,
+ /// a server is required to send the list back using the
+ /// RPL_EXCEPTLIST and RPL_ENDOFEXCEPTLIST messages. A
+ /// separate RPL_EXCEPTLIST is sent for each active mask.
+ /// After the masks have been listed (or if none present)
+ /// a RPL_ENDOFEXCEPTLIST MUST be sent.
+ ///
+ RPL_ENDOFEXCEPTLIST(CS<'a>),
+
+ /// 351 RPL_VERSION
+ /// "<version>.<debuglevel> <server> :<comments>"
+ ///
+ /// - Reply by the server showing its version details.
+ /// The <version> is the version of the software being
+ /// used (including any patchlevel revisions) and the
+ /// <debuglevel> is used to indicate if the server is
+ /// running in "debug mode".
+ ///
+ /// The "comments" field may contain any comments about
+ /// the version or further version details.
+ ///
+ RPL_VERSION(CS<'a>),
+
+ /// 352 RPL_WHOREPLY
+ /// "<channel> <user> <host> <server> <nick>
+ /// ( "H" / "G" > ["*"] [ ( "@" / "+" ) ]
+ /// :<hopcount> <real name>"
+ ///
+ RPL_WHOREPLY(CS<'a>),
+
+ /// 315 RPL_ENDOFWHO
+ /// "<name> :End of WHO list"
+ ///
+ /// - The RPL_WHOREPLY and RPL_ENDOFWHO pair are used
+ /// to answer a WHO message. The RPL_WHOREPLY is only
+ /// sent if there is an appropriate match to the WHO
+ /// query. If there is a list of parameters supplied
+ /// with a WHO message, a RPL_ENDOFWHO MUST be sent
+ /// after processing each list item with <name> being
+ /// the item.
+ ///
+ RPL_ENDOFWHO(CS<'a>),
+
+ /// 353 RPL_NAMREPLY
+ /// "( "=" / "*" / "@" ) <channel>
+ /// :[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )
+ /// - "@" is used for secret channels, "*" for private
+ /// channels, and "=" for others (public channels).
+ ///
+ RPL_NAMREPLY(CS<'a>),
+
+ /// 366 RPL_ENDOFNAMES
+ /// "<channel> :End of NAMES list"
+ ///
+ /// - To reply to a NAMES message, a reply pair consisting
+ /// of RPL_NAMREPLY and RPL_ENDOFNAMES is sent by the
+ /// server back to the client. If there is no channel
+ /// found as in the query, then only RPL_ENDOFNAMES is
+ /// returned. The exception to this is when a NAMES
+ /// message is sent with no parameters and all visible
+ /// channels and contents are sent back in a series of
+ /// RPL_NAMEREPLY messages with a RPL_ENDOFNAMES to mark
+ /// the end.
+ ///
+ RPL_ENDOFNAMES(CS<'a>),
+
+ /// 364 RPL_LINKS
+ /// "<mask> <server> :<hopcount> <server info>"
+ RPL_LINKS(CS<'a>),
+
+ /// 365 RPL_ENDOFLINKS
+ /// "<mask> :End of LINKS list"
+ ///
+ /// - In replying to the LINKS message, a server MUST send
+ /// replies back using the RPL_LINKS numeric and mark the
+ /// end of the list using an RPL_ENDOFLINKS reply.
+ ///
+ RPL_ENDOFLINKS(CS<'a>),
+
+ /// 367 RPL_BANLIST
+ /// "<channel> <banmask>"
+ RPL_BANLIST(CS<'a>),
+
+ /// 368 RPL_ENDOFBANLIST
+ /// "<channel> :End of channel ban list"
+ ///
+ /// - When listing the active 'bans' for a given channel,
+ /// a server is required to send the list back using the
+ /// RPL_BANLIST and RPL_ENDOFBANLIST messages. A separate
+ /// RPL_BANLIST is sent for each active banmask. After the
+ /// banmasks have been listed (or if none present) a
+ /// RPL_ENDOFBANLIST MUST be sent.
+ ///
+ RPL_ENDOFBANLIST(CS<'a>),
+
+ /// 371 RPL_INFO
+ /// ":<string>"
+ RPL_INFO(CS<'a>),
+
+ /// 374 RPL_ENDOFINFO
+ /// ":End of INFO list"
+ ///
+ /// - A server responding to an INFO message is required to
+ /// send all its 'info' in a series of RPL_INFO messages
+ /// with a RPL_ENDOFINFO reply to indicate the end of the
+ /// replies.
+ ///
+ RPL_ENDOFINFO(CS<'a>),
+
+ /// 375 RPL_MOTDSTART
+ /// ":- <server> Message of the day - "
+ RPL_MOTDSTART(CS<'a>),
+
+ /// 372 RPL_MOTD
+ /// ":- <text>"
+ RPL_MOTD(CS<'a>),
+
+ /// 376 RPL_ENDOFMOTD
+ /// ":End of MOTD command"
+ ///
+ /// - When responding to the MOTD message and the MOTD file
+ /// is found, the file is displayed line by line, with
+ /// each line no longer than 80 characters, using
+ /// RPL_MOTD format replies. These MUST be surrounded
+ /// by a RPL_MOTDSTART (before the RPL_MOTDs) and an
+ /// RPL_ENDOFMOTD (after).
+ ///
+ RPL_ENDOFMOTD(CS<'a>),
+
+ /// 381 RPL_YOUREOPER
+ /// ":You are now an IRC operator"
+ ///
+ /// - RPL_YOUREOPER is sent back to a client which has
+ /// just successfully issued an OPER message and gained
+ /// operator status.
+ ///
+ RPL_YOUREOPER(CS<'a>),
+
+ /// 382 RPL_REHASHING
+ /// "<config file> :Rehashing"
+ ///
+ /// - If the REHASH option is used and an operator sends
+ /// a REHASH message, an RPL_REHASHING is sent back to
+ /// the operator.
+ ///
+ RPL_REHASHING(CS<'a>),
+
+ /// 383 RPL_YOURESERVICE
+ /// "You are service <servicename>"
+ ///
+ /// - Sent by the server to a service upon successful
+ /// registration.
+ ///
+ RPL_YOURESERVICE(CS<'a>),
+
+ /// 391 RPL_TIME
+ /// "<server> :<string showing server's local time>"
+ ///
+ /// - When replying to the TIME message, a server MUST send
+ /// the reply using the RPL_TIME format above. The string
+ /// showing the time need only contain the correct day and
+ /// time there. There is no further requirement for the
+ /// time string.
+ ///
+ RPL_TIME(CS<'a>),
+
+ /// 392 RPL_USERSSTART
+ /// ":UserID Terminal Host"
+ RPL_USERSSTART(CS<'a>),
+
+ /// 393 RPL_USERS
+ /// ":<username> <ttyline> <hostname>"
+ RPL_USERS(CS<'a>),
+
+ /// 394 RPL_ENDOFUSERS
+ /// ":End of users"
+ RPL_ENDOFUSERS(CS<'a>),
+
+ /// 395 RPL_NOUSERS
+ /// ":Nobody logged in"
+ ///
+ /// - If the USERS message is handled by a server, the
+ /// replies RPL_USERSTART, RPL_USERS, RPL_ENDOFUSERS and
+ /// RPL_NOUSERS are used. RPL_USERSSTART MUST be sent
+ /// first, following by either a sequence of RPL_USERS
+ /// or a single RPL_NOUSER. Following this is
+ /// RPL_ENDOFUSERS.
+ ///
+ RPL_NOUSERS(CS<'a>),
+
+ /// 200 RPL_TRACELINK
+ /// "Link <version & debug level> <destination>
+ /// <next server> V<protocol version>
+ /// <link uptime in seconds> <backstream sendq>
+ /// <upstream sendq>"
+ RPL_TRACELINK(CS<'a>),
+
+ /// 201 RPL_TRACECONNECTING
+ /// "Try. <class> <server>"
+ RPL_TRACECONNECTING(CS<'a>),
+
+ /// 202 RPL_TRACEHANDSHAKE
+ /// "H.S. <class> <server>"
+ RPL_TRACEHANDSHAKE(CS<'a>),
+
+ /// 203 RPL_TRACEUNKNOWN
+ /// "???? <class> [<client IP address in dot form>]"
+ RPL_TRACEUNKNOWN(CS<'a>),
+
+ /// 204 RPL_TRACEOPERATOR
+ /// "Oper <class> <nick>"
+ RPL_TRACEOPERATOR(CS<'a>),
+
+ /// 205 RPL_TRACEUSER
+ /// "User <class> <nick>"
+ RPL_TRACEUSER(CS<'a>),
+
+ /// 206 RPL_TRACESERVER
+ /// "Serv <class> <int>S <int>C <server>
+ /// <nick!user|*!*>@<host|server> V<protocol version>"
+ RPL_TRACESERVER(CS<'a>),
+
+ /// 207 RPL_TRACESERVICE
+ /// "Service <class> <name> <type> <active type>"
+ RPL_TRACESERVICE(CS<'a>),
+
+ /// 208 RPL_TRACENEWTYPE
+ /// "<newtype> 0 <client name>"
+ RPL_TRACENEWTYPE(CS<'a>),
+
+ /// 209 RPL_TRACECLASS
+ /// "Class <class> <count>"
+ RPL_TRACECLASS(CS<'a>),
+
+ /// 210 RPL_TRACERECONNECT
+ /// Unused.
+ RPL_TRACERECONNECT(CS<'a>),
+
+ /// 261 RPL_TRACELOG
+ /// "File <logfile> <debug level>"
+ RPL_TRACELOG(CS<'a>),
+
+ /// 262 RPL_TRACEEND
+ /// "<server name> <version & debug level> :End of TRACE"
+ ///
+ /// - The RPL_TRACE* are all returned by the server in
+ /// response to the TRACE message. How many are
+ /// returned is dependent on the TRACE message and
+ /// whether it was sent by an operator or not. There
+ /// is no predefined order for which occurs first.
+ /// Replies RPL_TRACEUNKNOWN, RPL_TRACECONNECTING and
+ /// RPL_TRACEHANDSHAKE are all used for connections
+ /// which have not been fully established and are either
+ /// unknown, still attempting to connect or in the
+ /// process of completing the 'server handshake'.
+ /// RPL_TRACELINK is sent by any server which handles
+ /// a TRACE message and has to pass it on to another
+ /// server. The list of RPL_TRACELINKs sent in
+ /// response to a TRACE command traversing the IRC
+ /// network should reflect the actual connectivity of
+ /// the servers themselves along that path.
+ ///
+ /// RPL_TRACENEWTYPE is to be used for any connection
+ /// which does not fit in the other categories but is
+ /// being displayed anyway.
+ /// RPL_TRACEEND is sent to indicate the end of the list.
+ ///
+ RPL_TRACEEND(CS<'a>),
+
+ /// 211 RPL_STATSLINKINFO
+ /// "<linkname> <sendq> <sent messages>
+ /// <sent Kbytes> <received messages>
+ /// <received Kbytes> <time open>"
+ ///
+ /// - reports statistics on a connection. <linkname>
+ /// identifies the particular connection, <sendq> is
+ /// the amount of data that is queued and waiting to be
+ /// sent <sent messages> the number of messages sent,
+ /// and <sent Kbytes> the amount of data sent, in
+ /// Kbytes. <received messages> and <received Kbytes>
+ /// are the equivalent of <sent messages> and <sent
+ /// Kbytes> for received data, respectively. <time
+ /// open> indicates how long ago the connection was
+ /// opened, in seconds.
+ ///
+ RPL_STATSLINKINFO(CS<'a>),
+
+ /// 212 RPL_STATSCOMMANDS
+ /// "<command> <count> <byte count> <remote count>"
+ ///
+ /// - reports statistics on commands usage.
+ ///
+ RPL_STATSCOMMANDS(CS<'a>),
+
+ /// 219 RPL_ENDOFSTATS
+ /// "<stats letter> :End of STATS report"
+ ///
+ RPL_ENDOFSTATS(CS<'a>),
+
+ /// 242 RPL_STATSUPTIME
+ /// ":Server Up %d days %d:%02d:%02d"
+ ///
+ /// - reports the server uptime.
+ ///
+ RPL_STATSUPTIME(CS<'a>),
+
+ /// 243 RPL_STATSOLINE
+ /// "O <hostmask> * <name>"
+ ///
+ /// - reports the allowed hosts from where user may become IRC
+ /// operators.
+ ///
+ RPL_STATSOLINE(CS<'a>),
+
+ /// 221 RPL_UMODEIS
+ /// "<user mode string>"
+ ///
+ /// - To answer a query about a client's own mode,
+ /// RPL_UMODEIS is sent back.
+ ///
+ RPL_UMODEIS(CS<'a>),
+
+ /// 234 RPL_SERVLIST
+ /// "<name> <server> <mask> <type> <hopcount> <info>"
+ ///
+ RPL_SERVLIST(CS<'a>),
+
+ /// 235 RPL_SERVLISTEND
+ /// "<mask> <type> :End of service listing"
+ ///
+ /// - When listing services in reply to a SERVLIST message,
+ /// a server is required to send the list back using the
+ /// RPL_SERVLIST and RPL_SERVLISTEND messages. A separate
+ /// RPL_SERVLIST is sent for each service. After the
+ /// services have been listed (or if none present) a
+ /// RPL_SERVLISTEND MUST be sent.
+ ///
+ RPL_SERVLISTEND(CS<'a>),
+
+ /// 251 RPL_LUSERCLIENT
+ /// ":There are <integer> users and <integer>
+ /// services on <integer> servers"
+ RPL_LUSERCLIENT(CS<'a>),
+
+ /// 252 RPL_LUSEROP
+ /// "<integer> :operator(s) online"
+ RPL_LUSEROP(CS<'a>),
+
+ /// 253 RPL_LUSERUNKNOWN
+ /// "<integer> :unknown connection(s)"
+ RPL_LUSERUNKNOWN(CS<'a>),
+
+ /// 254 RPL_LUSERCHANNELS
+ /// "<integer> :channels formed"
+ RPL_LUSERCHANNELS(CS<'a>),
+
+ /// 255 RPL_LUSERME
+ /// ":I have <integer> clients and <integer>
+ /// servers"
+ ///
+ /// - In processing an LUSERS message, the server
+ /// sends a set of replies from RPL_LUSERCLIENT,
+ /// RPL_LUSEROP, RPL_USERUNKNOWN,
+ /// RPL_LUSERCHANNELS and RPL_LUSERME. When
+ /// replying, a server MUST send back
+ /// RPL_LUSERCLIENT and RPL_LUSERME. The other
+ /// replies are only sent back if a non-zero count
+ /// is found for them.
+ ///
+ RPL_LUSERME(CS<'a>),
+
+ /// 256 RPL_ADMINME
+ /// "<server> :Administrative info"
+ RPL_ADMINME(CS<'a>),
+
+ /// 257 RPL_ADMINLOC1
+ /// ":<admin info>"
+ RPL_ADMINLOC1(CS<'a>),
+
+ /// 258 RPL_ADMINLOC2
+ /// ":<admin info>"
+ RPL_ADMINLOC2(CS<'a>),
+
+ /// 259 RPL_ADMINEMAIL
+ /// ":<admin info>"
+ ///
+ /// - When replying to an ADMIN message, a server
+ /// is expected to use replies RPL_ADMINME
+ /// through to RPL_ADMINEMAIL and provide a text
+ /// message with each. For RPL_ADMINLOC1 a
+ /// description of what city, state and country
+ /// the server is in is expected, followed by
+ /// details of the institution (RPL_ADMINLOC2)
+ ///
+ /// and finally the administrative contact for the
+ /// server (an email address here is REQUIRED)
+ /// in RPL_ADMINEMAIL.
+ ///
+ RPL_ADMINEMAIL(CS<'a>),
+
+ /// 263 RPL_TRYAGAIN
+ /// "<command> :Please wait a while and try again."
+ ///
+ /// - When a server drops a command without processing it,
+ /// it MUST use the reply RPL_TRYAGAIN to inform the
+ /// originating client.
+ ///
+ RPL_TRYAGAIN(CS<'a>),
+
+ /// 401 ERR_NOSUCHNICK
+ /// "<nickname> :No such nick/channel"
+ ///
+ /// - Used to indicate the nickname parameter supplied to a
+ /// command is currently unused.
+ ///
+ ERR_NOSUCHNICK(CS<'a>),
+
+ /// 402 ERR_NOSUCHSERVER
+ /// "<server name> :No such server"
+ ///
+ /// - Used to indicate the server name given currently
+ /// does not exist.
+ ///
+ ERR_NOSUCHSERVER(CS<'a>),
+
+ /// 403 ERR_NOSUCHCHANNEL
+ /// "<channel name> :No such channel"
+ ///
+ /// - Used to indicate the given channel name is invalid.
+ ///
+ ERR_NOSUCHCHANNEL(CS<'a>),
+
+ /// 404 ERR_CANNOTSENDTOCHAN
+ /// "<channel name> :Cannot send to channel"
+ ///
+ /// - Sent to a user who is either (a) not on a channel
+ /// which is mode +n or (b) not a chanop (or mode +v) on
+ /// a channel which has mode +m set or where the user is
+ /// banned and is trying to send a PRIVMSG message to
+ /// that channel.
+ ///
+ ERR_CANNOTSENDTOCHAN(CS<'a>),
+
+ /// 405 ERR_TOOMANYCHANNELS
+ /// "<channel name> :You have joined too many channels"
+ ///
+ /// - Sent to a user when they have joined the maximum
+ /// number of allowed channels and they try to join
+ /// another channel.
+ ///
+ ERR_TOOMANYCHANNELS(CS<'a>),
+
+ /// 406 ERR_WASNOSUCHNICK
+ /// "<nickname> :There was no such nickname"
+ ///
+ /// - Returned by WHOWAS to indicate there is no history
+ /// information for that nickname.
+ ///
+ ERR_WASNOSUCHNICK(CS<'a>),
+
+ /// 407 ERR_TOOMANYTARGETS
+ /// "<target> :<error code> recipients. <abort message>"
+ ///
+ /// - Returned to a client which is attempting to send a
+ /// PRIVMSG/NOTICE using the user@host destination format
+ /// and for a user@host which has several occurrences.
+ ///
+ /// - Returned to a client which trying to send a
+ /// PRIVMSG/NOTICE to too many recipients.
+ ///
+ /// - Returned to a client which is attempting to JOIN a safe
+ /// channel using the shortname when there are more than one
+ /// such channel.
+ ///
+ ERR_TOOMANYTARGETS(CS<'a>),
+
+ /// 408 ERR_NOSUCHSERVICE
+ /// "<service name> :No such service"
+ ///
+ /// - Returned to a client which is attempting to send a SQUERY
+ /// to a service which does not exist.
+ ///
+ ERR_NOSUCHSERVICE(CS<'a>),
+
+ /// 409 ERR_NOORIGIN
+ /// ":No origin specified"
+ ///
+ /// - PING or PONG message missing the originator parameter.
+ ///
+ ERR_NOORIGIN(CS<'a>),
+
+ /// 411 ERR_NORECIPIENT
+ /// ":No recipient given (<command>)"
+ ERR_NORECIPIENT(CS<'a>),
+
+ /// 412 ERR_NOTEXTTOSEND
+ /// ":No text to send"
+ ERR_NOTEXTTOSEND(CS<'a>),
+
+ /// 413 ERR_NOTOPLEVEL
+ /// "<mask> :No toplevel domain specified"
+ ERR_NOTOPLEVEL(CS<'a>),
+
+ /// 414 ERR_WILDTOPLEVEL
+ /// "<mask> :Wildcard in toplevel domain"
+ ERR_WILDTOPLEVEL(CS<'a>),
+
+ /// 415 ERR_BADMASK
+ /// "<mask> :Bad Server/host mask"
+ ///
+ /// - 412 - 415 are returned by PRIVMSG to indicate that
+ /// the message wasn't delivered for some reason.
+ /// ERR_NOTOPLEVEL and ERR_WILDTOPLEVEL are errors that
+ /// are returned when an invalid use of
+ /// "PRIVMSG $<server>" or "PRIVMSG #<host>" is attempted.
+ ///
+ ERR_BADMASK(CS<'a>),
+
+ /// 421 ERR_UNKNOWNCOMMAND
+ /// "<command> :Unknown command"
+ ///
+ /// - Returned to a registered client to indicate that the
+ /// command sent is unknown by the server.
+ ///
+ ERR_UNKNOWNCOMMAND(CS<'a>),
+
+ /// 422 ERR_NOMOTD
+ /// ":MOTD File is missing"
+ ///
+ /// - Server's MOTD file could not be opened by the server.
+ ///
+ ERR_NOMOTD(CS<'a>),
+
+ /// 423 ERR_NOADMININFO
+ /// "<server> :No administrative info available"
+ ///
+ /// - Returned by a server in response to an ADMIN message
+ /// when there is an error in finding the appropriate
+ /// information.
+ ///
+ ERR_NOADMININFO(CS<'a>),
+
+ /// 424 ERR_FILEERROR
+ /// ":File error doing <file op> on <file>"
+ ///
+ /// - Generic error message used to report a failed file
+ /// operation during the processing of a message.
+ ///
+ ERR_FILEERROR(CS<'a>),
+
+ /// 431 ERR_NONICKNAMEGIVEN
+ /// ":No nickname given"
+ ///
+ /// - Returned when a nickname parameter expected for a
+ /// command and isn't found.
+ ///
+ ERR_NONICKNAMEGIVEN(CS<'a>),
+
+ /// 432 ERR_ERRONEUSNICKNAME
+ /// "<nick> :Erroneous nickname"
+ ///
+ /// - Returned after receiving a NICK message which contains
+ /// characters which do not fall in the defined set. See
+ /// section 2.3.1 for details on valid nicknames.
+ ///
+ ERR_ERRONEUSNICKNAME(CS<'a>),
+
+ /// 433 ERR_NICKNAMEINUSE
+ /// "<nick> :Nickname is already in use"
+ ///
+ /// - Returned when a NICK message is processed that results
+ /// in an attempt to change to a currently existing
+ /// nickname.
+ ///
+ ERR_NICKNAMEINUSE(CS<'a>),
+
+ /// 436 ERR_NICKCOLLISION
+ /// "<nick> :Nickname collision KILL from <user>@<host>"
+ ///
+ /// - Returned by a server to a client when it detects a
+ /// nickname collision (registered of a NICK that
+ /// already exists by another server).
+ ///
+ ERR_NICKCOLLISION(CS<'a>),
+
+ /// 437 ERR_UNAVAILRESOURCE
+ /// "<nick/channel> :Nick/channel is temporarily unavailable"
+ ///
+ /// - Returned by a server to a user trying to join a channel
+ /// currently blocked by the channel delay mechanism.
+ ///
+ /// - Returned by a server to a user trying to change nickname
+ /// when the desired nickname is blocked by the nick delay
+ /// mechanism.
+ ///
+ ERR_UNAVAILRESOURCE(CS<'a>),
+
+ /// 441 ERR_USERNOTINCHANNEL
+ /// "<nick> <channel> :They aren't on that channel"
+ ///
+ /// - Returned by the server to indicate that the target
+ /// user of the command is not on the given channel.
+ ///
+ ERR_USERNOTINCHANNEL(CS<'a>),
+
+ /// 442 ERR_NOTONCHANNEL
+ /// "<channel> :You're not on that channel"
+ ///
+ /// - Returned by the server whenever a client tries to
+ /// perform a channel affecting command for which the
+ /// client isn't a member.
+ ///
+ ERR_NOTONCHANNEL(CS<'a>),
+
+ /// 443 ERR_USERONCHANNEL
+ /// "<user> <channel> :is already on channel"
+ ///
+ /// - Returned when a client tries to invite a user to a
+ /// channel they are already on.
+ ///
+ ERR_USERONCHANNEL(CS<'a>),
+
+ /// 444 ERR_NOLOGIN
+ /// "<user> :User not logged in"
+ ///
+ /// - Returned by the summon after a SUMMON command for a
+ /// user was unable to be performed since they were not
+ /// logged in.
+ ///
+ ERR_NOLOGIN(CS<'a>),
+
+ /// 445 ERR_SUMMONDISABLED
+ /// ":SUMMON has been disabled"
+ ///
+ /// - Returned as a response to the SUMMON command. MUST be
+ /// returned by any server which doesn't implement it.
+ ///
+ ERR_SUMMONDISABLED(CS<'a>),
+
+ /// 446 ERR_USERSDISABLED
+ /// ":USERS has been disabled"
+ ///
+ /// - Returned as a response to the USERS command. MUST be
+ /// returned by any server which does not implement it.
+ ///
+ ERR_USERSDISABLED(CS<'a>),
+
+ /// 451 ERR_NOTREGISTERED
+ /// ":You have not registered"
+ ///
+ /// - Returned by the server to indicate that the client
+ /// MUST be registered before the server will allow it
+ /// to be parsed in detail.
+ ///
+ ERR_NOTREGISTERED(CS<'a>),
+
+ /// 461 ERR_NEEDMOREPARAMS
+ /// "<command> :Not enough parameters"
+ ///
+ /// - Returned by the server by numerous commands to
+ /// indicate to the client that it didn't supply enough
+ /// parameters.
+ ///
+ ERR_NEEDMOREPARAMS(CS<'a>),
+
+ /// 462 ERR_ALREADYREGISTRED
+ /// ":Unauthorized command (already registered)"
+ ///
+ /// - Returned by the server to any link which tries to
+ /// change part of the registered details (such as
+ /// password or user details from second USER message).
+ ///
+ ERR_ALREADYREGISTRED(CS<'a>),
+
+ /// 463 ERR_NOPERMFORHOST
+ /// ":Your host isn't among the privileged"
+ ///
+ /// - Returned to a client which attempts to register with
+ /// a server which does not been setup to allow
+ /// connections from the host the attempted connection
+ /// is tried.
+ ///
+ ERR_NOPERMFORHOST(CS<'a>),
+
+ /// 464 ERR_PASSWDMISMATCH
+ /// ":Password incorrect"
+ ///
+ /// - Returned to indicate a failed attempt at registering
+ /// a connection for which a password was required and
+ /// was either not given or incorrect.
+ ///
+ ERR_PASSWDMISMATCH(CS<'a>),
+
+ /// 465 ERR_YOUREBANNEDCREEP
+ /// ":You are banned from this server"
+ ///
+ /// - Returned after an attempt to connect and register
+ /// yourself with a server which has been setup to
+ /// explicitly deny connections to you.
+ ///
+ ERR_YOUREBANNEDCREEP(CS<'a>),
+
+ /// 466 ERR_YOUWILLBEBANNED
+ ///
+ /// - Sent by a server to a user to inform that access to the
+ /// server will soon be denied.
+ ///
+ ERR_YOUWILLBEBANNED(CS<'a>),
+
+ /// 467 ERR_KEYSET
+ /// "<channel> :Channel key already set"
+ ERR_KEYSET(CS<'a>),
+
+ /// 471 ERR_CHANNELISFULL
+ /// "<channel> :Cannot join channel (+l)"
+ ERR_CHANNELISFULL(CS<'a>),
+
+ /// 472 ERR_UNKNOWNMODE
+ /// "<char> :is unknown mode char to me for <channel>"
+ ERR_UNKNOWNMODE(CS<'a>),
+
+ /// 473 ERR_INVITEONLYCHAN
+ /// "<channel> :Cannot join channel (+i)"
+ ERR_INVITEONLYCHAN(CS<'a>),
+
+ /// 474 ERR_BANNEDFROMCHAN
+ /// "<channel> :Cannot join channel (+b)"
+ ERR_BANNEDFROMCHAN(CS<'a>),
+
+ /// 475 ERR_BADCHANNELKEY
+ /// "<channel> :Cannot join channel (+k)"
+ ERR_BADCHANNELKEY(CS<'a>),
+
+ /// 476 ERR_BADCHANMASK
+ /// "<channel> :Bad Channel Mask"
+ ERR_BADCHANMASK(CS<'a>),
+
+ /// 477 ERR_NOCHANMODES
+ /// "<channel> :Channel doesn't support modes"
+ ERR_NOCHANMODES(CS<'a>),
+
+ /// 478 ERR_BANLISTFULL
+ /// "<channel> <char> :Channel list is full"
+ ///
+ ERR_BANLISTFULL(CS<'a>),
+
+ /// 481 ERR_NOPRIVILEGES
+ /// ":Permission Denied- You're not an IRC operator"
+ ///
+ /// - Any command requiring operator privileges to operate
+ /// MUST return this error to indicate the attempt was
+ /// unsuccessful.
+ ///
+ ERR_NOPRIVILEGES(CS<'a>),
+
+ /// 482 ERR_CHANOPRIVSNEEDED
+ /// "<channel> :You're not channel operator"
+ ///
+ /// - Any command requiring 'chanop' privileges (such as
+ /// MODE messages) MUST return this error if the client
+ /// making the attempt is not a chanop on the specified
+ /// channel.
+ ///
+ ERR_CHANOPRIVSNEEDED(CS<'a>),
+
+ /// 483 ERR_CANTKILLSERVER
+ /// ":You can't kill a server!"
+ ///
+ /// - Any attempts to use the KILL command on a server
+ /// are to be refused and this error returned directly
+ /// to the client.
+ ///
+ ERR_CANTKILLSERVER(CS<'a>),
+
+ /// 484 ERR_RESTRICTED
+ /// ":Your connection is restricted!"
+ ///
+ /// - Sent by the server to a user upon connection to indicate
+ /// the restricted nature of the connection (user mode "+r").
+ ///
+ ERR_RESTRICTED(CS<'a>),
+
+ /// 485 ERR_UNIQOPPRIVSNEEDED
+ /// ":You're not the original channel operator"
+ ///
+ /// - Any MODE requiring "channel creator" privileges MUST
+ /// return this error if the client making the attempt is not
+ /// a chanop on the specified channel.
+ ///
+ ERR_UNIQOPPRIVSNEEDED(CS<'a>),
+
+ /// 491 ERR_NOOPERHOST
+ /// ":No O-lines for your host"
+ ///
+ /// - If a client sends an OPER message and the server has
+ /// not been configured to allow connections from the
+ /// client's host as an operator, this error MUST be
+ /// returned.
+ ///
+ ERR_NOOPERHOST(CS<'a>),
+
+ /// 501 ERR_UMODEUNKNOWNFLAG
+ /// ":Unknown MODE flag"
+ ///
+ /// - Returned by the server to indicate that a MODE
+ /// message was sent with a nickname parameter and that
+ /// the a mode flag sent was not recognized.
+ ///
+ ERR_UMODEUNKNOWNFLAG(CS<'a>),
+
+ /// 502 ERR_USERSDONTMATCH
+ /// ":Cannot change mode for other users"
+ ///
+ /// - Error sent to any user trying to view or change the
+ /// user mode for a user other than themselves.
+ ///
+ ERR_USERSDONTMATCH(CS<'a>),
+
+}
+
+impl<'a>Reply<'a> {
+ pubfnfrom_message(msg: &'aMessage) ->Option<Reply<'a>> {
+ useself::Reply::*;
+ matchmsg.command() {
+ "001"=>msg.elements().last().map(|&e|RPL_WELCOME(Borrowed(e))),
+ "002"=>msg.elements().last().map(|&e|RPL_YOURHOST(Borrowed(e))),
+ "003"=>msg.elements().last().map(|&e|RPL_CREATED(Borrowed(e))),
+ "004"=>msg.elements().last().map(|&e|RPL_MYINFO(Borrowed(e))),
+ "005"=>msg.elements().last().map(|&e|RPL_BOUNCE(Borrowed(e))),
+ "302"=>msg.elements().last().map(|&e|RPL_USERHOST(Borrowed(e))),
+ "303"=>msg.elements().last().map(|&e|RPL_ISON(Borrowed(e))),
+ "301"=>msg.elements().last().map(|&e|RPL_AWAY(Borrowed(e))),
+ "305"=>msg.elements().last().map(|&e|RPL_UNAWAY(Borrowed(e))),
+ "306"=>msg.elements().last().map(|&e|RPL_NOWAWAY(Borrowed(e))),
+ "311"=>msg.elements().last().map(|&e|RPL_WHOISUSER(Borrowed(e))),
+ "312"=>msg.elements().last().map(|&e|RPL_WHOISSERVER(Borrowed(e))),
+ "313"=>msg.elements().last().map(|&e|RPL_WHOISOPERATOR(Borrowed(e))),
+ "317"=>msg.elements().last().map(|&e|RPL_WHOISIDLE(Borrowed(e))),
+ "318"=>msg.elements().last().map(|&e|RPL_ENDOFWHOIS(Borrowed(e))),
+ "319"=>msg.elements().last().map(|&e|RPL_WHOISCHANNELS(Borrowed(e))),
+ "314"=>msg.elements().last().map(|&e|RPL_WHOWASUSER(Borrowed(e))),
+ "369"=>msg.elements().last().map(|&e|RPL_ENDOFWHOWAS(Borrowed(e))),
+ "321"=>Some(RPL_LISTSTART),
+ "322"=>msg.elements().last().map(|&e|RPL_LIST(Borrowed(e))),
+ "323"=>msg.elements().last().map(|&e|RPL_LISTEND(Borrowed(e))),
+ "325"=>msg.elements().last().map(|&e|RPL_UNIQOPIS(Borrowed(e))),
+ "324"=>msg.elements().last().map(|&e|RPL_CHANNELMODEIS(Borrowed(e))),
+ "331"=>msg.elements().last().map(|&e|RPL_NOTOPIC(Borrowed(e))),
+ "332"=>msg.elements().last().map(|&e|RPL_TOPIC(Borrowed(e))),
+ "341"=>msg.elements().last().map(|&e|RPL_INVITING(Borrowed(e))),
+ "342"=>msg.elements().last().map(|&e|RPL_SUMMONING(Borrowed(e))),
+ "346"=>msg.elements().last().map(|&e|RPL_INVITELIST(Borrowed(e))),
+ "347"=>msg.elements().last().map(|&e|RPL_ENDOFINVITELIST(Borrowed(e))),
+ "348"=>msg.elements().last().map(|&e|RPL_EXCEPTLIST(Borrowed(e))),
+ "349"=>msg.elements().last().map(|&e|RPL_ENDOFEXCEPTLIST(Borrowed(e))),
+ "351"=>msg.elements().last().map(|&e|RPL_VERSION(Borrowed(e))),
+ "352"=>msg.elements().last().map(|&e|RPL_WHOREPLY(Borrowed(e))),
+ "315"=>msg.elements().last().map(|&e|RPL_ENDOFWHO(Borrowed(e))),
+ "353"=>msg.elements().last().map(|&e|RPL_NAMREPLY(Borrowed(e))),
+ "366"=>msg.elements().last().map(|&e|RPL_ENDOFNAMES(Borrowed(e))),
+ "364"=>msg.elements().last().map(|&e|RPL_LINKS(Borrowed(e))),
+ "365"=>msg.elements().last().map(|&e|RPL_ENDOFLINKS(Borrowed(e))),
+ "367"=>msg.elements().last().map(|&e|RPL_BANLIST(Borrowed(e))),
+ "368"=>msg.elements().last().map(|&e|RPL_ENDOFBANLIST(Borrowed(e))),
+ "371"=>msg.elements().last().map(|&e|RPL_INFO(Borrowed(e))),
+ "374"=>msg.elements().last().map(|&e|RPL_ENDOFINFO(Borrowed(e))),
+ "375"=>msg.elements().last().map(|&e|RPL_MOTDSTART(Borrowed(e))),
+ "372"=>msg.elements().last().map(|&e|RPL_MOTD(Borrowed(e))),
+ "376"=>msg.elements().last().map(|&e|RPL_ENDOFMOTD(Borrowed(e))),
+ "381"=>msg.elements().last().map(|&e|RPL_YOUREOPER(Borrowed(e))),
+ "382"=>msg.elements().last().map(|&e|RPL_REHASHING(Borrowed(e))),
+ "383"=>msg.elements().last().map(|&e|RPL_YOURESERVICE(Borrowed(e))),
+ "391"=>msg.elements().last().map(|&e|RPL_TIME(Borrowed(e))),
+ "392"=>msg.elements().last().map(|&e|RPL_USERSSTART(Borrowed(e))),
+ "393"=>msg.elements().last().map(|&e|RPL_USERS(Borrowed(e))),
+ "394"=>msg.elements().last().map(|&e|RPL_ENDOFUSERS(Borrowed(e))),
+ "395"=>msg.elements().last().map(|&e|RPL_NOUSERS(Borrowed(e))),
+ "200"=>msg.elements().last().map(|&e|RPL_TRACELINK(Borrowed(e))),
+ "201"=>msg.elements().last().map(|&e|RPL_TRACECONNECTING(Borrowed(e))),
+ "202"=>msg.elements().last().map(|&e|RPL_TRACEHANDSHAKE(Borrowed(e))),
+ "203"=>msg.elements().last().map(|&e|RPL_TRACEUNKNOWN(Borrowed(e))),
+ "204"=>msg.elements().last().map(|&e|RPL_TRACEOPERATOR(Borrowed(e))),
+ "205"=>msg.elements().last().map(|&e|RPL_TRACEUSER(Borrowed(e))),
+ "206"=>msg.elements().last().map(|&e|RPL_TRACESERVER(Borrowed(e))),
+ "207"=>msg.elements().last().map(|&e|RPL_TRACESERVICE(Borrowed(e))),
+ "208"=>msg.elements().last().map(|&e|RPL_TRACENEWTYPE(Borrowed(e))),
+ "209"=>msg.elements().last().map(|&e|RPL_TRACECLASS(Borrowed(e))),
+ "210"=>msg.elements().last().map(|&e|RPL_TRACERECONNECT(Borrowed(e))),
+ "261"=>msg.elements().last().map(|&e|RPL_TRACELOG(Borrowed(e))),
+ "262"=>msg.elements().last().map(|&e|RPL_TRACEEND(Borrowed(e))),
+ "211"=>msg.elements().last().map(|&e|RPL_STATSLINKINFO(Borrowed(e))),
+ "212"=>msg.elements().last().map(|&e|RPL_STATSCOMMANDS(Borrowed(e))),
+ "219"=>msg.elements().last().map(|&e|RPL_ENDOFSTATS(Borrowed(e))),
+ "242"=>msg.elements().last().map(|&e|RPL_STATSUPTIME(Borrowed(e))),
+ "243"=>msg.elements().last().map(|&e|RPL_STATSOLINE(Borrowed(e))),
+ "221"=>msg.elements().last().map(|&e|RPL_UMODEIS(Borrowed(e))),
+ "234"=>msg.elements().last().map(|&e|RPL_SERVLIST(Borrowed(e))),
+ "235"=>msg.elements().last().map(|&e|RPL_SERVLISTEND(Borrowed(e))),
+ "251"=>msg.elements().last().map(|&e|RPL_LUSERCLIENT(Borrowed(e))),
+ "252"=>msg.elements().last().map(|&e|RPL_LUSEROP(Borrowed(e))),
+ "253"=>msg.elements().last().map(|&e|RPL_LUSERUNKNOWN(Borrowed(e))),
+ "254"=>msg.elements().last().map(|&e|RPL_LUSERCHANNELS(Borrowed(e))),
+ "255"=>msg.elements().last().map(|&e|RPL_LUSERME(Borrowed(e))),
+ "256"=>msg.elements().last().map(|&e|RPL_ADMINME(Borrowed(e))),
+ "257"=>msg.elements().last().map(|&e|RPL_ADMINLOC1(Borrowed(e))),
+ "258"=>msg.elements().last().map(|&e|RPL_ADMINLOC2(Borrowed(e))),
+ "259"=>msg.elements().last().map(|&e|RPL_ADMINEMAIL(Borrowed(e))),
+ "263"=>msg.elements().last().map(|&e|RPL_TRYAGAIN(Borrowed(e))),
+ "401"=>msg.elements().last().map(|&e|ERR_NOSUCHNICK(Borrowed(e))),
+ "402"=>msg.elements().last().map(|&e|ERR_NOSUCHSERVER(Borrowed(e))),
+ "403"=>msg.elements().last().map(|&e|ERR_NOSUCHCHANNEL(Borrowed(e))),
+ "404"=>msg.elements().last().map(|&e|ERR_CANNOTSENDTOCHAN(Borrowed(e))),
+ "405"=>msg.elements().last().map(|&e|ERR_TOOMANYCHANNELS(Borrowed(e))),
+ "406"=>msg.elements().last().map(|&e|ERR_WASNOSUCHNICK(Borrowed(e))),
+ "407"=>msg.elements().last().map(|&e|ERR_TOOMANYTARGETS(Borrowed(e))),
+ "408"=>msg.elements().last().map(|&e|ERR_NOSUCHSERVICE(Borrowed(e))),
+ "409"=>msg.elements().last().map(|&e|ERR_NOORIGIN(Borrowed(e))),
+ "411"=>msg.elements().last().map(|&e|ERR_NORECIPIENT(Borrowed(e))),
+ "412"=>msg.elements().last().map(|&e|ERR_NOTEXTTOSEND(Borrowed(e))),
+ "413"=>msg.elements().last().map(|&e|ERR_NOTOPLEVEL(Borrowed(e))),
+ "414"=>msg.elements().last().map(|&e|ERR_WILDTOPLEVEL(Borrowed(e))),
+ "415"=>msg.elements().last().map(|&e|ERR_BADMASK(Borrowed(e))),
+ "421"=>msg.elements().last().map(|&e|ERR_UNKNOWNCOMMAND(Borrowed(e))),
+ "422"=>msg.elements().last().map(|&e|ERR_NOMOTD(Borrowed(e))),
+ "423"=>msg.elements().last().map(|&e|ERR_NOADMININFO(Borrowed(e))),
+ "424"=>msg.elements().last().map(|&e|ERR_FILEERROR(Borrowed(e))),
+ "431"=>msg.elements().last().map(|&e|ERR_NONICKNAMEGIVEN(Borrowed(e))),
+ "432"=>msg.elements().last().map(|&e|ERR_ERRONEUSNICKNAME(Borrowed(e))),
+ "433"=>msg.elements().last().map(|&e|ERR_NICKNAMEINUSE(Borrowed(e))),
+ "436"=>msg.elements().last().map(|&e|ERR_NICKCOLLISION(Borrowed(e))),
+ "437"=>msg.elements().last().map(|&e|ERR_UNAVAILRESOURCE(Borrowed(e))),
+ "441"=>msg.elements().last().map(|&e|ERR_USERNOTINCHANNEL(Borrowed(e))),
+ "442"=>msg.elements().last().map(|&e|ERR_NOTONCHANNEL(Borrowed(e))),
+ "443"=>msg.elements().last().map(|&e|ERR_USERONCHANNEL(Borrowed(e))),
+ "444"=>msg.elements().last().map(|&e|ERR_NOLOGIN(Borrowed(e))),
+ "445"=>msg.elements().last().map(|&e|ERR_SUMMONDISABLED(Borrowed(e))),
+ "446"=>msg.elements().last().map(|&e|ERR_USERSDISABLED(Borrowed(e))),
+ "451"=>msg.elements().last().map(|&e|ERR_NOTREGISTERED(Borrowed(e))),
+ "461"=>msg.elements().last().map(|&e|ERR_NEEDMOREPARAMS(Borrowed(e))),
+ "462"=>msg.elements().last().map(|&e|ERR_ALREADYREGISTRED(Borrowed(e))),
+ "463"=>msg.elements().last().map(|&e|ERR_NOPERMFORHOST(Borrowed(e))),
+ "464"=>msg.elements().last().map(|&e|ERR_PASSWDMISMATCH(Borrowed(e))),
+ "465"=>msg.elements().last().map(|&e|ERR_YOUREBANNEDCREEP(Borrowed(e))),
+ "466"=>msg.elements().last().map(|&e|ERR_YOUWILLBEBANNED(Borrowed(e))),
+ "467"=>msg.elements().last().map(|&e|ERR_KEYSET(Borrowed(e))),
+ "471"=>msg.elements().last().map(|&e|ERR_CHANNELISFULL(Borrowed(e))),
+ "472"=>msg.elements().last().map(|&e|ERR_UNKNOWNMODE(Borrowed(e))),
+ "473"=>msg.elements().last().map(|&e|ERR_INVITEONLYCHAN(Borrowed(e))),
+ "474"=>msg.elements().last().map(|&e|ERR_BANNEDFROMCHAN(Borrowed(e))),
+ "475"=>msg.elements().last().map(|&e|ERR_BADCHANNELKEY(Borrowed(e))),
+ "476"=>msg.elements().last().map(|&e|ERR_BADCHANMASK(Borrowed(e))),
+ "477"=>msg.elements().last().map(|&e|ERR_NOCHANMODES(Borrowed(e))),
+ "478"=>msg.elements().last().map(|&e|ERR_BANLISTFULL(Borrowed(e))),
+ "481"=>msg.elements().last().map(|&e|ERR_NOPRIVILEGES(Borrowed(e))),
+ "482"=>msg.elements().last().map(|&e|ERR_CHANOPRIVSNEEDED(Borrowed(e))),
+ "483"=>msg.elements().last().map(|&e|ERR_CANTKILLSERVER(Borrowed(e))),
+ "484"=>msg.elements().last().map(|&e|ERR_RESTRICTED(Borrowed(e))),
+ "485"=>msg.elements().last().map(|&e|ERR_UNIQOPPRIVSNEEDED(Borrowed(e))),
+ "491"=>msg.elements().last().map(|&e|ERR_NOOPERHOST(Borrowed(e))),
+ "501"=>msg.elements().last().map(|&e|ERR_UMODEUNKNOWNFLAG(Borrowed(e))),
+ "502"=>msg.elements().last().map(|&e|ERR_USERSDONTMATCH(Borrowed(e))),
+ _ =>None
+ }
+ }
+
+ pubfnto_message(&'aself) ->Message {
+ useself::Reply::*;
+ matchself {
+ &RPL_WELCOME(refs) =>Message::format(None, Borrowed("001"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_YOURHOST(refs) =>Message::format(None, Borrowed("002"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_CREATED(refs) =>Message::format(None, Borrowed("003"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_MYINFO(refs) =>Message::format(None, Borrowed("004"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_BOUNCE(refs) =>Message::format(None, Borrowed("005"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_USERHOST(refs) =>Message::format(None, Borrowed("302"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ISON(refs) =>Message::format(None, Borrowed("303"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_AWAY(refs) =>Message::format(None, Borrowed("301"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_UNAWAY(refs) =>Message::format(None, Borrowed("305"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_NOWAWAY(refs) =>Message::format(None, Borrowed("306"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_WHOISUSER(refs) =>Message::format(None, Borrowed("311"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_WHOISSERVER(refs) =>Message::format(None, Borrowed("312"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_WHOISOPERATOR(refs) =>Message::format(None, Borrowed("313"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_WHOISIDLE(refs) =>Message::format(None, Borrowed("317"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ENDOFWHOIS(refs) =>Message::format(None, Borrowed("318"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_WHOISCHANNELS(refs) =>Message::format(None, Borrowed("319"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_WHOWASUSER(refs) =>Message::format(None, Borrowed("314"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ENDOFWHOWAS(refs) =>Message::format(None, Borrowed("369"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_LISTSTART=>Message::format(None, Borrowed("321"), vec![], None, MsgType::Irc),
+ &RPL_LIST(refs) =>Message::format(None, Borrowed("322"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_LISTEND(refs) =>Message::format(None, Borrowed("323"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_UNIQOPIS(refs) =>Message::format(None, Borrowed("325"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_CHANNELMODEIS(refs) =>Message::format(None, Borrowed("324"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_NOTOPIC(refs) =>Message::format(None, Borrowed("331"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TOPIC(refs) =>Message::format(None, Borrowed("332"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_INVITING(refs) =>Message::format(None, Borrowed("341"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_SUMMONING(refs) =>Message::format(None, Borrowed("342"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_INVITELIST(refs) =>Message::format(None, Borrowed("346"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ENDOFINVITELIST(refs) =>Message::format(None, Borrowed("347"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_EXCEPTLIST(refs) =>Message::format(None, Borrowed("348"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ENDOFEXCEPTLIST(refs) =>Message::format(None, Borrowed("349"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_VERSION(refs) =>Message::format(None, Borrowed("351"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_WHOREPLY(refs) =>Message::format(None, Borrowed("352"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ENDOFWHO(refs) =>Message::format(None, Borrowed("315"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_NAMREPLY(refs) =>Message::format(None, Borrowed("353"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ENDOFNAMES(refs) =>Message::format(None, Borrowed("366"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_LINKS(refs) =>Message::format(None, Borrowed("364"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ENDOFLINKS(refs) =>Message::format(None, Borrowed("365"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_BANLIST(refs) =>Message::format(None, Borrowed("367"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ENDOFBANLIST(refs) =>Message::format(None, Borrowed("368"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_INFO(refs) =>Message::format(None, Borrowed("371"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ENDOFINFO(refs) =>Message::format(None, Borrowed("374"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_MOTDSTART(refs) =>Message::format(None, Borrowed("375"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_MOTD(refs) =>Message::format(None, Borrowed("372"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ENDOFMOTD(refs) =>Message::format(None, Borrowed("376"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_YOUREOPER(refs) =>Message::format(None, Borrowed("381"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_REHASHING(refs) =>Message::format(None, Borrowed("382"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_YOURESERVICE(refs) =>Message::format(None, Borrowed("383"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TIME(refs) =>Message::format(None, Borrowed("391"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_USERSSTART(refs) =>Message::format(None, Borrowed("392"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_USERS(refs) =>Message::format(None, Borrowed("393"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ENDOFUSERS(refs) =>Message::format(None, Borrowed("394"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_NOUSERS(refs) =>Message::format(None, Borrowed("395"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TRACELINK(refs) =>Message::format(None, Borrowed("200"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TRACECONNECTING(refs) =>Message::format(None, Borrowed("201"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TRACEHANDSHAKE(refs) =>Message::format(None, Borrowed("202"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TRACEUNKNOWN(refs) =>Message::format(None, Borrowed("203"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TRACEOPERATOR(refs) =>Message::format(None, Borrowed("204"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TRACEUSER(refs) =>Message::format(None, Borrowed("205"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TRACESERVER(refs) =>Message::format(None, Borrowed("206"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TRACESERVICE(refs) =>Message::format(None, Borrowed("207"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TRACENEWTYPE(refs) =>Message::format(None, Borrowed("208"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TRACECLASS(refs) =>Message::format(None, Borrowed("209"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TRACERECONNECT(refs) =>Message::format(None, Borrowed("210"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TRACELOG(refs) =>Message::format(None, Borrowed("261"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TRACEEND(refs) =>Message::format(None, Borrowed("262"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_STATSLINKINFO(refs) =>Message::format(None, Borrowed("211"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_STATSCOMMANDS(refs) =>Message::format(None, Borrowed("212"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ENDOFSTATS(refs) =>Message::format(None, Borrowed("219"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_STATSUPTIME(refs) =>Message::format(None, Borrowed("242"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_STATSOLINE(refs) =>Message::format(None, Borrowed("243"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_UMODEIS(refs) =>Message::format(None, Borrowed("221"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_SERVLIST(refs) =>Message::format(None, Borrowed("234"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_SERVLISTEND(refs) =>Message::format(None, Borrowed("235"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_LUSERCLIENT(refs) =>Message::format(None, Borrowed("251"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_LUSEROP(refs) =>Message::format(None, Borrowed("252"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_LUSERUNKNOWN(refs) =>Message::format(None, Borrowed("253"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_LUSERCHANNELS(refs) =>Message::format(None, Borrowed("254"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_LUSERME(refs) =>Message::format(None, Borrowed("255"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ADMINME(refs) =>Message::format(None, Borrowed("256"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ADMINLOC1(refs) =>Message::format(None, Borrowed("257"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ADMINLOC2(refs) =>Message::format(None, Borrowed("258"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_ADMINEMAIL(refs) =>Message::format(None, Borrowed("259"), vec![], Some(s.clone()), MsgType::Irc),
+ &RPL_TRYAGAIN(refs) =>Message::format(None, Borrowed("263"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOSUCHNICK(refs) =>Message::format(None, Borrowed("401"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOSUCHSERVER(refs) =>Message::format(None, Borrowed("402"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOSUCHCHANNEL(refs) =>Message::format(None, Borrowed("403"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_CANNOTSENDTOCHAN(refs) =>Message::format(None, Borrowed("404"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_TOOMANYCHANNELS(refs) =>Message::format(None, Borrowed("405"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_WASNOSUCHNICK(refs) =>Message::format(None, Borrowed("406"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_TOOMANYTARGETS(refs) =>Message::format(None, Borrowed("407"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOSUCHSERVICE(refs) =>Message::format(None, Borrowed("408"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOORIGIN(refs) =>Message::format(None, Borrowed("409"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NORECIPIENT(refs) =>Message::format(None, Borrowed("411"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOTEXTTOSEND(refs) =>Message::format(None, Borrowed("412"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOTOPLEVEL(refs) =>Message::format(None, Borrowed("413"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_WILDTOPLEVEL(refs) =>Message::format(None, Borrowed("414"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_BADMASK(refs) =>Message::format(None, Borrowed("415"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_UNKNOWNCOMMAND(refs) =>Message::format(None, Borrowed("421"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOMOTD(refs) =>Message::format(None, Borrowed("422"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOADMININFO(refs) =>Message::format(None, Borrowed("423"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_FILEERROR(refs) =>Message::format(None, Borrowed("424"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NONICKNAMEGIVEN(refs) =>Message::format(None, Borrowed("431"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_ERRONEUSNICKNAME(refs) =>Message::format(None, Borrowed("432"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NICKNAMEINUSE(refs) =>Message::format(None, Borrowed("433"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NICKCOLLISION(refs) =>Message::format(None, Borrowed("436"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_UNAVAILRESOURCE(refs) =>Message::format(None, Borrowed("437"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_USERNOTINCHANNEL(refs) =>Message::format(None, Borrowed("441"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOTONCHANNEL(refs) =>Message::format(None, Borrowed("442"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_USERONCHANNEL(refs) =>Message::format(None, Borrowed("443"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOLOGIN(refs) =>Message::format(None, Borrowed("444"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_SUMMONDISABLED(refs) =>Message::format(None, Borrowed("445"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_USERSDISABLED(refs) =>Message::format(None, Borrowed("446"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOTREGISTERED(refs) =>Message::format(None, Borrowed("451"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NEEDMOREPARAMS(refs) =>Message::format(None, Borrowed("461"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_ALREADYREGISTRED(refs) =>Message::format(None, Borrowed("462"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOPERMFORHOST(refs) =>Message::format(None, Borrowed("463"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_PASSWDMISMATCH(refs) =>Message::format(None, Borrowed("464"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_YOUREBANNEDCREEP(refs) =>Message::format(None, Borrowed("465"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_YOUWILLBEBANNED(refs) =>Message::format(None, Borrowed("466"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_KEYSET(refs) =>Message::format(None, Borrowed("467"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_CHANNELISFULL(refs) =>Message::format(None, Borrowed("471"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_UNKNOWNMODE(refs) =>Message::format(None, Borrowed("472"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_INVITEONLYCHAN(refs) =>Message::format(None, Borrowed("473"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_BANNEDFROMCHAN(refs) =>Message::format(None, Borrowed("474"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_BADCHANNELKEY(refs) =>Message::format(None, Borrowed("475"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_BADCHANMASK(refs) =>Message::format(None, Borrowed("476"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOCHANMODES(refs) =>Message::format(None, Borrowed("477"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_BANLISTFULL(refs) =>Message::format(None, Borrowed("478"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOPRIVILEGES(refs) =>Message::format(None, Borrowed("481"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_CHANOPRIVSNEEDED(refs) =>Message::format(None, Borrowed("482"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_CANTKILLSERVER(refs) =>Message::format(None, Borrowed("483"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_RESTRICTED(refs) =>Message::format(None, Borrowed("484"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_UNIQOPPRIVSNEEDED(refs) =>Message::format(None, Borrowed("485"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_NOOPERHOST(refs) =>Message::format(None, Borrowed("491"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_UMODEUNKNOWNFLAG(refs) =>Message::format(None, Borrowed("501"), vec![], Some(s.clone()), MsgType::Irc),
+ &ERR_USERSDONTMATCH(refs) =>Message::format(None, Borrowed("502"), vec![], Some(s.clone()), MsgType::Irc),
+ }
+ }
+
+ pubfnto_static(&self) ->Reply<'static> {
+ useself::Reply::*;
+ matchself {
+ &RPL_WELCOME(refs) =>RPL_WELCOME(Cow::Owned(s.clone().into_owned())),
+ &RPL_YOURHOST(refs) =>RPL_YOURHOST(Cow::Owned(s.clone().into_owned())),
+ &RPL_CREATED(refs) =>RPL_CREATED(Cow::Owned(s.clone().into_owned())),
+ &RPL_MYINFO(refs) =>RPL_MYINFO(Cow::Owned(s.clone().into_owned())),
+ &RPL_BOUNCE(refs) =>RPL_BOUNCE(Cow::Owned(s.clone().into_owned())),
+ &RPL_USERHOST(refs) =>RPL_USERHOST(Cow::Owned(s.clone().into_owned())),
+ &RPL_ISON(refs) =>RPL_ISON(Cow::Owned(s.clone().into_owned())),
+ &RPL_AWAY(refs) =>RPL_AWAY(Cow::Owned(s.clone().into_owned())),
+ &RPL_UNAWAY(refs) =>RPL_UNAWAY(Cow::Owned(s.clone().into_owned())),
+ &RPL_NOWAWAY(refs) =>RPL_NOWAWAY(Cow::Owned(s.clone().into_owned())),
+ &RPL_WHOISUSER(refs) =>RPL_WHOISUSER(Cow::Owned(s.clone().into_owned())),
+ &RPL_WHOISSERVER(refs) =>RPL_WHOISSERVER(Cow::Owned(s.clone().into_owned())),
+ &RPL_WHOISOPERATOR(refs) =>RPL_WHOISOPERATOR(Cow::Owned(s.clone().into_owned())),
+ &RPL_WHOISIDLE(refs) =>RPL_WHOISIDLE(Cow::Owned(s.clone().into_owned())),
+ &RPL_ENDOFWHOIS(refs) =>RPL_ENDOFWHOIS(Cow::Owned(s.clone().into_owned())),
+ &RPL_WHOISCHANNELS(refs) =>RPL_WHOISCHANNELS(Cow::Owned(s.clone().into_owned())),
+ &RPL_WHOWASUSER(refs) =>RPL_WHOWASUSER(Cow::Owned(s.clone().into_owned())),
+ &RPL_ENDOFWHOWAS(refs) =>RPL_ENDOFWHOWAS(Cow::Owned(s.clone().into_owned())),
+ &RPL_LISTSTART=>RPL_LISTSTART,
+ &RPL_LIST(refs) =>RPL_LIST(Cow::Owned(s.clone().into_owned())),
+ &RPL_LISTEND(refs) =>RPL_LISTEND(Cow::Owned(s.clone().into_owned())),
+ &RPL_UNIQOPIS(refs) =>RPL_UNIQOPIS(Cow::Owned(s.clone().into_owned())),
+ &RPL_CHANNELMODEIS(refs) =>RPL_CHANNELMODEIS(Cow::Owned(s.clone().into_owned())),
+ &RPL_NOTOPIC(refs) =>RPL_NOTOPIC(Cow::Owned(s.clone().into_owned())),
+ &RPL_TOPIC(refs) =>RPL_TOPIC(Cow::Owned(s.clone().into_owned())),
+ &RPL_INVITING(refs) =>RPL_INVITING(Cow::Owned(s.clone().into_owned())),
+ &RPL_SUMMONING(refs) =>RPL_SUMMONING(Cow::Owned(s.clone().into_owned())),
+ &RPL_INVITELIST(refs) =>RPL_INVITELIST(Cow::Owned(s.clone().into_owned())),
+ &RPL_ENDOFINVITELIST(refs) =>RPL_ENDOFINVITELIST(Cow::Owned(s.clone().into_owned())),
+ &RPL_EXCEPTLIST(refs) =>RPL_EXCEPTLIST(Cow::Owned(s.clone().into_owned())),
+ &RPL_ENDOFEXCEPTLIST(refs) =>RPL_ENDOFEXCEPTLIST(Cow::Owned(s.clone().into_owned())),
+ &RPL_VERSION(refs) =>RPL_VERSION(Cow::Owned(s.clone().into_owned())),
+ &RPL_WHOREPLY(refs) =>RPL_WHOREPLY(Cow::Owned(s.clone().into_owned())),
+ &RPL_ENDOFWHO(refs) =>RPL_ENDOFWHO(Cow::Owned(s.clone().into_owned())),
+ &RPL_NAMREPLY(refs) =>RPL_NAMREPLY(Cow::Owned(s.clone().into_owned())),
+ &RPL_ENDOFNAMES(refs) =>RPL_ENDOFNAMES(Cow::Owned(s.clone().into_owned())),
+ &RPL_LINKS(refs) =>RPL_LINKS(Cow::Owned(s.clone().into_owned())),
+ &RPL_ENDOFLINKS(refs) =>RPL_ENDOFLINKS(Cow::Owned(s.clone().into_owned())),
+ &RPL_BANLIST(refs) =>RPL_BANLIST(Cow::Owned(s.clone().into_owned())),
+ &RPL_ENDOFBANLIST(refs) =>RPL_ENDOFBANLIST(Cow::Owned(s.clone().into_owned())),
+ &RPL_INFO(refs) =>RPL_INFO(Cow::Owned(s.clone().into_owned())),
+ &RPL_ENDOFINFO(refs) =>RPL_ENDOFINFO(Cow::Owned(s.clone().into_owned())),
+ &RPL_MOTDSTART(refs) =>RPL_MOTDSTART(Cow::Owned(s.clone().into_owned())),
+ &RPL_MOTD(refs) =>RPL_MOTD(Cow::Owned(s.clone().into_owned())),
+ &RPL_ENDOFMOTD(refs) =>RPL_ENDOFMOTD(Cow::Owned(s.clone().into_owned())),
+ &RPL_YOUREOPER(refs) =>RPL_YOUREOPER(Cow::Owned(s.clone().into_owned())),
+ &RPL_REHASHING(refs) =>RPL_REHASHING(Cow::Owned(s.clone().into_owned())),
+ &RPL_YOURESERVICE(refs) =>RPL_YOURESERVICE(Cow::Owned(s.clone().into_owned())),
+ &RPL_TIME(refs) =>RPL_TIME(Cow::Owned(s.clone().into_owned())),
+ &RPL_USERSSTART(refs) =>RPL_USERSSTART(Cow::Owned(s.clone().into_owned())),
+ &RPL_USERS(refs) =>RPL_USERS(Cow::Owned(s.clone().into_owned())),
+ &RPL_ENDOFUSERS(refs) =>RPL_ENDOFUSERS(Cow::Owned(s.clone().into_owned())),
+ &RPL_NOUSERS(refs) =>RPL_NOUSERS(Cow::Owned(s.clone().into_owned())),
+ &RPL_TRACELINK(refs) =>RPL_TRACELINK(Cow::Owned(s.clone().into_owned())),
+ &RPL_TRACECONNECTING(refs) =>RPL_TRACECONNECTING(Cow::Owned(s.clone().into_owned())),
+ &RPL_TRACEHANDSHAKE(refs) =>RPL_TRACEHANDSHAKE(Cow::Owned(s.clone().into_owned())),
+ &RPL_TRACEUNKNOWN(refs) =>RPL_TRACEUNKNOWN(Cow::Owned(s.clone().into_owned())),
+ &RPL_TRACEOPERATOR(refs) =>RPL_TRACEOPERATOR(Cow::Owned(s.clone().into_owned())),
+ &RPL_TRACEUSER(refs) =>RPL_TRACEUSER(Cow::Owned(s.clone().into_owned())),
+ &RPL_TRACESERVER(refs) =>RPL_TRACESERVER(Cow::Owned(s.clone().into_owned())),
+ &RPL_TRACESERVICE(refs) =>RPL_TRACESERVICE(Cow::Owned(s.clone().into_owned())),
+ &RPL_TRACENEWTYPE(refs) =>RPL_TRACENEWTYPE(Cow::Owned(s.clone().into_owned())),
+ &RPL_TRACECLASS(refs) =>RPL_TRACECLASS(Cow::Owned(s.clone().into_owned())),
+ &RPL_TRACERECONNECT(refs) =>RPL_TRACERECONNECT(Cow::Owned(s.clone().into_owned())),
+ &RPL_TRACELOG(refs) =>RPL_TRACELOG(Cow::Owned(s.clone().into_owned())),
+ &RPL_TRACEEND(refs) =>RPL_TRACEEND(Cow::Owned(s.clone().into_owned())),
+ &RPL_STATSLINKINFO(refs) =>RPL_STATSLINKINFO(Cow::Owned(s.clone().into_owned())),
+ &RPL_STATSCOMMANDS(refs) =>RPL_STATSCOMMANDS(Cow::Owned(s.clone().into_owned())),
+ &RPL_ENDOFSTATS(refs) =>RPL_ENDOFSTATS(Cow::Owned(s.clone().into_owned())),
+ &RPL_STATSUPTIME(refs) =>RPL_STATSUPTIME(Cow::Owned(s.clone().into_owned())),
+ &RPL_STATSOLINE(refs) =>RPL_STATSOLINE(Cow::Owned(s.clone().into_owned())),
+ &RPL_UMODEIS(refs) =>RPL_UMODEIS(Cow::Owned(s.clone().into_owned())),
+ &RPL_SERVLIST(refs) =>RPL_SERVLIST(Cow::Owned(s.clone().into_owned())),
+ &RPL_SERVLISTEND(refs) =>RPL_SERVLISTEND(Cow::Owned(s.clone().into_owned())),
+ &RPL_LUSERCLIENT(refs) =>RPL_LUSERCLIENT(Cow::Owned(s.clone().into_owned())),
+ &RPL_LUSEROP(refs) =>RPL_LUSEROP(Cow::Owned(s.clone().into_owned())),
+ &RPL_LUSERUNKNOWN(refs) =>RPL_LUSERUNKNOWN(Cow::Owned(s.clone().into_owned())),
+ &RPL_LUSERCHANNELS(refs) =>RPL_LUSERCHANNELS(Cow::Owned(s.clone().into_owned())),
+ &RPL_LUSERME(refs) =>RPL_LUSERME(Cow::Owned(s.clone().into_owned())),
+ &RPL_ADMINME(refs) =>RPL_ADMINME(Cow::Owned(s.clone().into_owned())),
+ &RPL_ADMINLOC1(refs) =>RPL_ADMINLOC1(Cow::Owned(s.clone().into_owned())),
+ &RPL_ADMINLOC2(refs) =>RPL_ADMINLOC2(Cow::Owned(s.clone().into_owned())),
+ &RPL_ADMINEMAIL(refs) =>RPL_ADMINEMAIL(Cow::Owned(s.clone().into_owned())),
+ &RPL_TRYAGAIN(refs) =>RPL_TRYAGAIN(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOSUCHNICK(refs) =>ERR_NOSUCHNICK(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOSUCHSERVER(refs) =>ERR_NOSUCHSERVER(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOSUCHCHANNEL(refs) =>ERR_NOSUCHCHANNEL(Cow::Owned(s.clone().into_owned())),
+ &ERR_CANNOTSENDTOCHAN(refs) =>ERR_CANNOTSENDTOCHAN(Cow::Owned(s.clone().into_owned())),
+ &ERR_TOOMANYCHANNELS(refs) =>ERR_TOOMANYCHANNELS(Cow::Owned(s.clone().into_owned())),
+ &ERR_WASNOSUCHNICK(refs) =>ERR_WASNOSUCHNICK(Cow::Owned(s.clone().into_owned())),
+ &ERR_TOOMANYTARGETS(refs) =>ERR_TOOMANYTARGETS(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOSUCHSERVICE(refs) =>ERR_NOSUCHSERVICE(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOORIGIN(refs) =>ERR_NOORIGIN(Cow::Owned(s.clone().into_owned())),
+ &ERR_NORECIPIENT(refs) =>ERR_NORECIPIENT(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOTEXTTOSEND(refs) =>ERR_NOTEXTTOSEND(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOTOPLEVEL(refs) =>ERR_NOTOPLEVEL(Cow::Owned(s.clone().into_owned())),
+ &ERR_WILDTOPLEVEL(refs) =>ERR_WILDTOPLEVEL(Cow::Owned(s.clone().into_owned())),
+ &ERR_BADMASK(refs) =>ERR_BADMASK(Cow::Owned(s.clone().into_owned())),
+ &ERR_UNKNOWNCOMMAND(refs) =>ERR_UNKNOWNCOMMAND(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOMOTD(refs) =>ERR_NOMOTD(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOADMININFO(refs) =>ERR_NOADMININFO(Cow::Owned(s.clone().into_owned())),
+ &ERR_FILEERROR(refs) =>ERR_FILEERROR(Cow::Owned(s.clone().into_owned())),
+ &ERR_NONICKNAMEGIVEN(refs) =>ERR_NONICKNAMEGIVEN(Cow::Owned(s.clone().into_owned())),
+ &ERR_ERRONEUSNICKNAME(refs) =>ERR_ERRONEUSNICKNAME(Cow::Owned(s.clone().into_owned())),
+ &ERR_NICKNAMEINUSE(refs) =>ERR_NICKNAMEINUSE(Cow::Owned(s.clone().into_owned())),
+ &ERR_NICKCOLLISION(refs) =>ERR_NICKCOLLISION(Cow::Owned(s.clone().into_owned())),
+ &ERR_UNAVAILRESOURCE(refs) =>ERR_UNAVAILRESOURCE(Cow::Owned(s.clone().into_owned())),
+ &ERR_USERNOTINCHANNEL(refs) =>ERR_USERNOTINCHANNEL(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOTONCHANNEL(refs) =>ERR_NOTONCHANNEL(Cow::Owned(s.clone().into_owned())),
+ &ERR_USERONCHANNEL(refs) =>ERR_USERONCHANNEL(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOLOGIN(refs) =>ERR_NOLOGIN(Cow::Owned(s.clone().into_owned())),
+ &ERR_SUMMONDISABLED(refs) =>ERR_SUMMONDISABLED(Cow::Owned(s.clone().into_owned())),
+ &ERR_USERSDISABLED(refs) =>ERR_USERSDISABLED(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOTREGISTERED(refs) =>ERR_NOTREGISTERED(Cow::Owned(s.clone().into_owned())),
+ &ERR_NEEDMOREPARAMS(refs) =>ERR_NEEDMOREPARAMS(Cow::Owned(s.clone().into_owned())),
+ &ERR_ALREADYREGISTRED(refs) =>ERR_ALREADYREGISTRED(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOPERMFORHOST(refs) =>ERR_NOPERMFORHOST(Cow::Owned(s.clone().into_owned())),
+ &ERR_PASSWDMISMATCH(refs) =>ERR_PASSWDMISMATCH(Cow::Owned(s.clone().into_owned())),
+ &ERR_YOUREBANNEDCREEP(refs) =>ERR_YOUREBANNEDCREEP(Cow::Owned(s.clone().into_owned())),
+ &ERR_YOUWILLBEBANNED(refs) =>ERR_YOUWILLBEBANNED(Cow::Owned(s.clone().into_owned())),
+ &ERR_KEYSET(refs) =>ERR_KEYSET(Cow::Owned(s.clone().into_owned())),
+ &ERR_CHANNELISFULL(refs) =>ERR_CHANNELISFULL(Cow::Owned(s.clone().into_owned())),
+ &ERR_UNKNOWNMODE(refs) =>ERR_UNKNOWNMODE(Cow::Owned(s.clone().into_owned())),
+ &ERR_INVITEONLYCHAN(refs) =>ERR_INVITEONLYCHAN(Cow::Owned(s.clone().into_owned())),
+ &ERR_BANNEDFROMCHAN(refs) =>ERR_BANNEDFROMCHAN(Cow::Owned(s.clone().into_owned())),
+ &ERR_BADCHANNELKEY(refs) =>ERR_BADCHANNELKEY(Cow::Owned(s.clone().into_owned())),
+ &ERR_BADCHANMASK(refs) =>ERR_BADCHANMASK(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOCHANMODES(refs) =>ERR_NOCHANMODES(Cow::Owned(s.clone().into_owned())),
+ &ERR_BANLISTFULL(refs) =>ERR_BANLISTFULL(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOPRIVILEGES(refs) =>ERR_NOPRIVILEGES(Cow::Owned(s.clone().into_owned())),
+ &ERR_CHANOPRIVSNEEDED(refs) =>ERR_CHANOPRIVSNEEDED(Cow::Owned(s.clone().into_owned())),
+ &ERR_CANTKILLSERVER(refs) =>ERR_CANTKILLSERVER(Cow::Owned(s.clone().into_owned())),
+ &ERR_RESTRICTED(refs) =>ERR_RESTRICTED(Cow::Owned(s.clone().into_owned())),
+ &ERR_UNIQOPPRIVSNEEDED(refs) =>ERR_UNIQOPPRIVSNEEDED(Cow::Owned(s.clone().into_owned())),
+ &ERR_NOOPERHOST(refs) =>ERR_NOOPERHOST(Cow::Owned(s.clone().into_owned())),
+ &ERR_UMODEUNKNOWNFLAG(refs) =>ERR_UMODEUNKNOWNFLAG(Cow::Owned(s.clone().into_owned())),
+ &ERR_USERSDONTMATCH(refs) =>ERR_USERSDONTMATCH(Cow::Owned(s.clone().into_owned())),
+ }
+
+ }
+}
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! A lightweight logging facade.
+//!
+//! A logging facade provides a single logging API that abstracts over the
+//! actual logging implementation. Libraries can use the logging API provided
+//! by this crate, and the consumer of those libraries can choose the logging
+//! framework that is most suitable for its use case.
+//!
+//! If no logging implementation is selected, the facade falls back to a "noop"
+//! implementation that ignores all log messages. The overhead in this case
+//! is very small - just an integer load, comparison and jump.
+//!
+//! A log request consists of a target, a level, and a body. A target is a
+//! string which defaults to the module path of the location of the log
+//! request, though that default may be overridden. Logger implementations
+//! typically use the target to filter requests based on some user
+//! configuration.
+//!
+//! # Use
+//!
+//! ## In libraries
+//!
+//! Libraries should link only to the `log` crate, and use the provided
+//! macros to log whatever information will be useful to downstream consumers.
+//!
+//! ### Examples
+//!
+//! ```rust
+//! # #![allow(unstable)]
+//! #[macro_use]
+//! extern crate log;
+//!
+//! # #[derive(Debug)] pub struct Yak(String);
+//! # impl Yak { fn shave(&self, _: u32) {} }
+//! # fn find_a_razor() -> Result<u32, u32> { Ok(1) }
+//! pub fn shave_the_yak(yak: &Yak) {
+//! info!(target: "yak_events", "Commencing yak shaving for {:?}", yak);
+//!
+//! loop {
+//! match find_a_razor() {
+//! Ok(razor) => {
+//! info!("Razor located: {}", razor);
+//! yak.shave(razor);
+//! break;
+//! }
+//! Err(err) => {
+//! warn!("Unable to locate a razor: {}, retrying", err);
+//! }
+//! }
+//! }
+//! }
+//! # fn main() {}
+//! ```
+//!
+//! ## In executables
+//!
+//! Executables should chose a logging framework and initialize it early in the
+//! runtime of the program. Logging frameworks will typically include a
+//! function to do this. Any log messages generated before the framework is
+//! initialized will be ignored.
+//!
+//! The executable itself may use the `log` crate to log as well.
+//!
+//! ### Warning
+//!
+//! The logging system may only be initialized once.
+//!
+//! ### Examples
+//!
+//! ```rust,ignore
+//! #[macro_use]
+//! extern crate log;
+//! extern crate my_logger;
+//!
+//! fn main() {
+//! my_logger::init();
+//!
+//! info!("starting up");
+//!
+//! // ...
+//! }
+//! ```
+//!
+//! # Logger implementations
+//!
+//! Loggers implement the `Log` trait. Here's a very basic example that simply
+//! logs all messages at the `Error`, `Warn` or `Info` levels to stdout:
+//!
+//! ```rust
+//! extern crate log;
+//!
+//! use log::{LogRecord, LogLevel, LogMetadata};
+//!
+//! struct SimpleLogger;
+//!
+//! impl log::Log for SimpleLogger {
+//! fn enabled(&self, metadata: &LogMetadata) -> bool {
+//! metadata.level() <= LogLevel::Info
+//! }
+//!
+//! fn log(&self, record: &LogRecord) {
+//! if self.enabled(record.metadata()) {
+//! println!("{} - {}", record.level(), record.args());
+//! }
+//! }
+//! }
+//!
+//! # fn main() {}
+//! ```
+//!
+//! Loggers are installed by calling the `set_logger` function. It takes a
+//! closure which is provided a `MaxLogLevel` token and returns a `Log` trait
+//! object. The `MaxLogLevel` token controls the global maximum log level. The
+//! logging facade uses this as an optimization to improve performance of log
+//! messages at levels that are disabled. In the case of our example logger,
+//! we'll want to set the maximum log level to `Info`, since we ignore any
+//! `Debug` or `Trace` level log messages. A logging framework should provide a
+//! function that wraps a call to `set_logger`, handling initialization of the
+//! logger:
+//!
+//! ```rust
+//! # extern crate log;
+//! # use log::{LogLevel, LogLevelFilter, SetLoggerError, LogMetadata};
+//! # struct SimpleLogger;
+//! # impl log::Log for SimpleLogger {
+//! # fn enabled(&self, _: &LogMetadata) -> bool { false }
+//! # fn log(&self, _: &log::LogRecord) {}
+//! # }
+//! # fn main() {}
+//! pub fn init() -> Result<(), SetLoggerError> {
+//! log::set_logger(|max_log_level| {
+//! max_log_level.set(LogLevelFilter::Info);
+//! Box::new(SimpleLogger)
+//! })
+//! }
+//! ```
+
+#![doc(html_logo_url="http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
+ html_favicon_url="http://www.rust-lang.org/favicon.ico",
+ html_root_url="http://doc.rust-lang.org/log/")]
+#![warn(missing_docs)]
+
+externcratelibc;
+
+usestd::ascii::AsciiExt;
+usestd::cmp;
+usestd::error;
+usestd::fmt;
+usestd::mem;
+usestd::ops::Deref;
+usestd::str::FromStr;
+usestd::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
+modmacros;
+
+// The setup here is a bit weird to make at_exit work.
+//
+// There are four different states that we care about: the logger's
+// uninitialized, the logger's initializing (set_logger's been called but
+// LOGGER hasn't actually been set yet), the logger's active, or the logger's
+// shutting down inside of at_exit.
+//
+// The LOGGER static is normally a Box<Box<Log>> with some special possible
+// values as well. The uninitialized and initializing states are represented by
+// the values 0 and 1 respectively. The shutting down state is also represented
+// by 1. Any other value is a valid pointer to the logger.
+//
+// The at_exit routine needs to make sure that no threads are actively logging
+// when it deallocates the logger. The number of actively logging threads is
+// tracked in the REFCOUNT static. The routine first sets LOGGER back to 1.
+// All logging calls past that point will immediately return without accessing
+// the logger. At that point, the at_exit routine just waits for the refcount
+// to reach 0 before deallocating the logger. Note that the refcount does not
+// necessarily monotonically decrease at this point, as new log calls still
+// increment and decrement it, but the interval in between is small enough that
+// the wait is really just for the active log calls to finish.
+staticLOGGER: AtomicUsize=ATOMIC_USIZE_INIT;
+staticREFCOUNT: AtomicUsize=ATOMIC_USIZE_INIT;
+
+constUNINITIALIZED: usize=0;
+constINITIALIZING: usize=1;
+
+staticMAX_LOG_LEVEL_FILTER: AtomicUsize=ATOMIC_USIZE_INIT;
+
+staticLOG_LEVEL_NAMES: [&'staticstr; 6] = ["OFF", "ERROR", "WARN", "INFO",
+ "DEBUG", "TRACE"];
+
+/// An enum representing the available verbosity levels of the logging framework
+///
+/// A `LogLevel` may be compared directly to a `LogLevelFilter`.
+#[repr(usize)]
+#[derive(Copy, Eq, Debug)]
+pubenumLogLevel {
+ /// The "error" level.
+ ///
+ /// Designates very serious errors.
+ Error=1, // This way these line up with the discriminants for LogLevelFilter below
+ /// The "warn" level.
+ ///
+ /// Designates hazardous situations.
+ Warn,
+ /// The "info" level.
+ ///
+ /// Designates useful information.
+ Info,
+ /// The "debug" level.
+ ///
+ /// Designates lower priority information.
+ Debug,
+ /// The "trace" level.
+ ///
+ /// Designates very low priority, often extremely verbose, information.
+ Trace,
+}
+
+implCloneforLogLevel {
+ #[inline]
+ fnclone(&self) ->LogLevel {
+ *self
+ }
+}
+
+implPartialEqforLogLevel {
+ #[inline]
+ fneq(&self, other: &LogLevel) ->bool {
+ *selfasusize==*otherasusize
+ }
+}
+
+implPartialEq<LogLevelFilter>forLogLevel {
+ #[inline]
+ fneq(&self, other: &LogLevelFilter) ->bool {
+ *selfasusize==*otherasusize
+ }
+}
+
+implPartialOrdforLogLevel {
+ #[inline]
+ fnpartial_cmp(&self, other: &LogLevel) ->Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+implPartialOrd<LogLevelFilter>forLogLevel {
+ #[inline]
+ fnpartial_cmp(&self, other: &LogLevelFilter) ->Option<cmp::Ordering> {
+ Some((*selfasusize).cmp(&(*otherasusize)))
+ }
+}
+
+implOrdforLogLevel {
+ #[inline]
+ fncmp(&self, other: &LogLevel) ->cmp::Ordering {
+ (*selfasusize).cmp(&(*otherasusize))
+ }
+}
+
+fnok_or<T, E>(t: Option<T>, e: E) ->Result<T, E> {
+ matcht {
+ Some(t) =>Ok(t),
+ None=>Err(e),
+ }
+}
+
+implFromStrforLogLevel {
+ typeErr= ();
+ fnfrom_str(level: &str) ->Result<LogLevel, ()> {
+ ok_or(LOG_LEVEL_NAMES.iter()
+ .position(|&name|name.eq_ignore_ascii_case(level))
+ .into_iter()
+ .filter(|&idx|idx!=0)
+ .map(|idx|LogLevel::from_usize(idx).unwrap())
+ .next(), ())
+ }
+}
+
+implfmt::DisplayforLogLevel {
+ fnfmt(&self, fmt: &mutfmt::Formatter) ->fmt::Result {
+ fmt.pad(LOG_LEVEL_NAMES[*selfasusize])
+ }
+}
+
+implLogLevel {
+ fnfrom_usize(u: usize) ->Option<LogLevel> {
+ matchu {
+ 1=>Some(LogLevel::Error),
+ 2=>Some(LogLevel::Warn),
+ 3=>Some(LogLevel::Info),
+ 4=>Some(LogLevel::Debug),
+ 5=>Some(LogLevel::Trace),
+ _ =>None
+ }
+ }
+
+ /// Returns the most verbose logging level.
+ #[inline]
+ pubfnmax() ->LogLevel {
+ LogLevel::Trace
+ }
+
+ /// Converts the `LogLevel` to the equivalent `LogLevelFilter`.
+ #[inline]
+ pubfnto_log_level_filter(&self) ->LogLevelFilter {
+ LogLevelFilter::from_usize(*selfasusize).unwrap()
+ }
+}
+
+/// An enum representing the available verbosity level filters of the logging
+/// framework.
+///
+/// A `LogLevelFilter` may be compared directly to a `LogLevel`.
+#[repr(usize)]
+#[derive(Copy, Eq, Debug)]
+pubenumLogLevelFilter {
+ /// A level lower than all log levels.
+ Off,
+ /// Corresponds to the `Error` log level.
+ Error,
+ /// Corresponds to the `Warn` log level.
+ Warn,
+ /// Corresponds to the `Trace` log level.
+ Info,
+ /// Corresponds to the `Debug` log level.
+ Debug,
+ /// Corresponds to the `Trace` log level.
+ Trace,
+}
+
+// Deriving generates terrible impls of these traits
+
+implCloneforLogLevelFilter {
+ #[inline]
+ fnclone(&self) ->LogLevelFilter {
+ *self
+ }
+}
+
+implPartialEqforLogLevelFilter {
+ #[inline]
+ fneq(&self, other: &LogLevelFilter) ->bool {
+ *selfasusize==*otherasusize
+ }
+}
+
+implPartialEq<LogLevel>forLogLevelFilter {
+ #[inline]
+ fneq(&self, other: &LogLevel) ->bool {
+ other.eq(self)
+ }
+}
+
+implPartialOrdforLogLevelFilter {
+ #[inline]
+ fnpartial_cmp(&self, other: &LogLevelFilter) ->Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+implPartialOrd<LogLevel>forLogLevelFilter {
+ #[inline]
+ fnpartial_cmp(&self, other: &LogLevel) ->Option<cmp::Ordering> {
+ other.partial_cmp(self).map(|x|x.reverse())
+ }
+}
+
+implOrdforLogLevelFilter {
+ #[inline]
+ fncmp(&self, other: &LogLevelFilter) ->cmp::Ordering {
+ (*selfasusize).cmp(&(*otherasusize))
+ }
+}
+
+implFromStrforLogLevelFilter {
+ typeErr= ();
+ fnfrom_str(level: &str) ->Result<LogLevelFilter, ()> {
+ ok_or(LOG_LEVEL_NAMES.iter()
+ .position(|&name|name.eq_ignore_ascii_case(level))
+ .map(|p|LogLevelFilter::from_usize(p).unwrap()), ())
+ }
+}
+
+implfmt::DisplayforLogLevelFilter {
+ fnfmt(&self, fmt: &mutfmt::Formatter) ->fmt::Result {
+ write!(fmt, "{}", LOG_LEVEL_NAMES[*selfasusize])
+ }
+}
+
+implLogLevelFilter {
+ fnfrom_usize(u: usize) ->Option<LogLevelFilter> {
+ matchu {
+ 0=>Some(LogLevelFilter::Off),
+ 1=>Some(LogLevelFilter::Error),
+ 2=>Some(LogLevelFilter::Warn),
+ 3=>Some(LogLevelFilter::Info),
+ 4=>Some(LogLevelFilter::Debug),
+ 5=>Some(LogLevelFilter::Trace),
+ _ =>None
+ }
+ }
+ /// Returns the most verbose logging level filter.
+ #[inline]
+ pubfnmax() ->LogLevelFilter {
+ LogLevelFilter::Trace
+ }
+
+ /// Converts `self` to the equivalent `LogLevel`.
+ ///
+ /// Returns `None` if `self` is `LogLevelFilter::Off`.
+ #[inline]
+ pubfnto_log_level(&self) ->Option<LogLevel> {
+ LogLevel::from_usize(*selfasusize)
+ }
+}
+
+/// The "payload" of a log message.
+pubstructLogRecord<'a> {
+ metadata: LogMetadata<'a>,
+ location: &'aLogLocation,
+ args: fmt::Arguments<'a>,
+}
+
+impl<'a>LogRecord<'a> {
+ /// The message body.
+ pubfnargs(&self) ->&fmt::Arguments<'a> {
+ &self.args
+ }
+
+ /// Metadata about the log directive.
+ pubfnmetadata(&self) ->&LogMetadata {
+ &self.metadata
+ }
+
+ /// The location of the log directive.
+ pubfnlocation(&self) ->&LogLocation {
+ self.location
+ }
+
+ /// The verbosity level of the message.
+ pubfnlevel(&self) ->LogLevel {
+ self.metadata.level()
+ }
+
+ /// The name of the target of the directive.
+ pubfntarget(&self) ->&str {
+ self.metadata.target()
+ }
+}
+
+/// Metadata about a log message.
+pubstructLogMetadata<'a> {
+ level: LogLevel,
+ target: &'astr,
+}
+
+impl<'a>LogMetadata<'a> {
+ /// The verbosity level of the message.
+ pubfnlevel(&self) ->LogLevel {
+ self.level
+ }
+
+ /// The name of the target of the directive.
+ pubfntarget(&self) ->&str {
+ self.target
+ }
+}
+
+/// A trait encapsulating the operations required of a logger
+pubtraitLog: Sync+Send {
+ /// Determines if a log message with the specified metadata would be
+ /// logged.
+ ///
+ /// This is used by the `log_enabled!` macro to allow callers to avoid
+ /// expensive computation of log message arguments if the message would be
+ /// discarded anyway.
+ fnenabled(&self, metadata: &LogMetadata) ->bool;
+
+ /// Logs the `LogRecord`.
+ ///
+ /// Note that `enabled` is *not* necessarily called before this method.
+ /// Implementations of `log` should perform all necessary filtering
+ /// internally.
+ fnlog(&self, record: &LogRecord);
+}
+
+/// The location of a log message.
+///
+/// # Warning
+///
+/// The fields of this struct are public so that they may be initialized by the
+/// `log!` macro. They are subject to change at any time and should never be
+/// accessed directly.
+#[derive(Copy, Clone, Debug)]
+pubstructLogLocation {
+ #[doc(hidden)]
+ pub__module_path: &'staticstr,
+ #[doc(hidden)]
+ pub__file: &'staticstr,
+ #[doc(hidden)]
+ pub__line: u32,
+}
+
+implLogLocation {
+ /// The module path of the message.
+ pubfnmodule_path(&self) ->&str {
+ self.__module_path
+ }
+
+ /// The source file containing the message.
+ pubfnfile(&self) ->&str {
+ self.__file
+ }
+
+ /// The line containing the message.
+ pubfnline(&self) ->u32 {
+ self.__line
+ }
+}
+
+/// A token providing read and write access to the global maximum log level
+/// filter.
+///
+/// The maximum log level is used as an optimization to avoid evaluating log
+/// messages that will be ignored by the logger. Any message with a level
+/// higher than the maximum log level filter will be ignored. A logger should
+/// make sure to keep the maximum log level filter in sync with its current
+/// configuration.
+#[allow(missing_copy_implementations)]
+pubstructMaxLogLevelFilter(());
+
+implfmt::DebugforMaxLogLevelFilter {
+ fnfmt(&self, fmt: &mutfmt::Formatter) ->fmt::Result {
+ write!(fmt, "MaxLogLevelFilter")
+ }
+}
+
+implMaxLogLevelFilter {
+ /// Gets the current maximum log level filter.
+ pubfnget(&self) ->LogLevelFilter {
+ max_log_level()
+ }
+
+ /// Sets the maximum log level.
+ pubfnset(&self, level: LogLevelFilter) {
+ MAX_LOG_LEVEL_FILTER.store(levelasusize, Ordering::SeqCst)
+ }
+}
+
+/// Returns the current maximum log level.
+///
+/// The `log!`, `error!`, `warn!`, `info!`, `debug!`, and `trace!` macros check
+/// this value and discard any message logged at a higher level. The maximum
+/// log level is set by the `MaxLogLevel` token passed to loggers.
+#[inline(always)]
+pubfnmax_log_level() ->LogLevelFilter {
+ unsafe { mem::transmute(MAX_LOG_LEVEL_FILTER.load(Ordering::Relaxed)) }
+}
+
+/// Sets the global logger.
+///
+/// The `make_logger` closure is passed a `MaxLogLevel` object, which the
+/// logger should use to keep the global maximum log level in sync with the
+/// highest log level that the logger will not ignore.
+///
+/// This function may only be called once in the lifetime of a program. Any log
+/// events that occur before the call to `set_logger` completes will be
+/// ignored.
+///
+/// This function does not typically need to be called manually. Logger
+/// implementations should provide an initialization method that calls
+/// `set_logger` internally.
+pubfnset_logger<M>(make_logger: M) ->Result<(), SetLoggerError>
+ whereM: FnOnce(MaxLogLevelFilter) ->Box<Log> {
+ ifLOGGER.compare_and_swap(UNINITIALIZED, INITIALIZING,
+ Ordering::SeqCst) !=UNINITIALIZED {
+ returnErr(SetLoggerError(()));
+ }
+
+ letlogger=Box::new(make_logger(MaxLogLevelFilter(())));
+ letlogger=unsafe { mem::transmute::<Box<Box<Log>>, usize>(logger) };
+ LOGGER.store(logger, Ordering::SeqCst);
+
+ unsafe {
+ assert_eq!(libc::atexit(shutdown), 0);
+ }
+ returnOk(());
+
+ externfnshutdown() {
+ // Set to INITIALIZING to prevent re-initialization after
+ letlogger=LOGGER.swap(INITIALIZING, Ordering::SeqCst);
+
+ whileREFCOUNT.load(Ordering::SeqCst) !=0 {
+ // FIXME add a sleep here when it doesn't involve timers
+ }
+
+ unsafe { mem::transmute::<usize, Box<Box<Log>>>(logger); }
+ }
+}
+
+/// The type returned by `set_logger` if `set_logger` has already been called.
+#[allow(missing_copy_implementations)]
+#[derive(Debug)]
+pubstructSetLoggerError(());
+
+implfmt::DisplayforSetLoggerError {
+ fnfmt(&self, fmt: &mutfmt::Formatter) ->fmt::Result {
+ write!(fmt, "attempted to set a logger after the logging system \
+ was already initialized")
+ }
+}
+
+implerror::ErrorforSetLoggerError {
+ fndescription(&self) ->&str { "set_logger() called multiple times" }
+}
+
+structLoggerGuard(usize);
+
+implDropforLoggerGuard {
+ fndrop(&mutself) {
+ REFCOUNT.fetch_sub(1, Ordering::SeqCst);
+ }
+}
+
+implDerefforLoggerGuard {
+ typeTarget=Box<Log>;
+
+ fnderef(&self) ->&Box<Log+'static> {
+ unsafe { mem::transmute(self.0) }
+ }
+}
+
+fnlogger() ->Option<LoggerGuard> {
+ REFCOUNT.fetch_add(1, Ordering::SeqCst);
+ letlogger=LOGGER.load(Ordering::SeqCst);
+ iflogger==UNINITIALIZED||logger==INITIALIZING {
+ REFCOUNT.fetch_sub(1, Ordering::SeqCst);
+ None
+ } else {
+ Some(LoggerGuard(logger))
+ }
+}
+
+// WARNING
+// This is not considered part of the crate's public API. It is subject to
+// change at any time.
+#[doc(hidden)]
+pubfn__enabled(level: LogLevel, target: &str) ->bool {
+ ifletSome(logger) =logger() {
+ logger.enabled(&LogMetadata { level: level, target: target })
+ } else {
+ false
+ }
+}
+
+// WARNING
+// This is not considered part of the crate's public API. It is subject to
+// change at any time.
+#[doc(hidden)]
+pubfn__log(level: LogLevel, target: &str, loc: &LogLocation,
+ args: fmt::Arguments) {
+ ifletSome(logger) =logger() {
+ letrecord=LogRecord {
+ metadata: LogMetadata {
+ level: level,
+ target: target,
+ },
+ location: loc,
+ args: args
+ };
+ logger.log(&record)
+ }
+}
+
+#[cfg(test)]
+modtests {
+ usestd::error::Error;
+ usesuper::{LogLevel, LogLevelFilter, SetLoggerError};
+
+ #[test]
+ fntest_loglevelfilter_from_str() {
+ lettests= [
+ ("off", Ok(LogLevelFilter::Off)),
+ ("error", Ok(LogLevelFilter::Error)),
+ ("warn", Ok(LogLevelFilter::Warn)),
+ ("info", Ok(LogLevelFilter::Info)),
+ ("debug", Ok(LogLevelFilter::Debug)),
+ ("trace", Ok(LogLevelFilter::Trace)),
+ ("OFF", Ok(LogLevelFilter::Off)),
+ ("ERROR", Ok(LogLevelFilter::Error)),
+ ("WARN", Ok(LogLevelFilter::Warn)),
+ ("INFO", Ok(LogLevelFilter::Info)),
+ ("DEBUG", Ok(LogLevelFilter::Debug)),
+ ("TRACE", Ok(LogLevelFilter::Trace)),
+ ("asdf", Err(())),
+ ];
+ for&(s, refexpected) in&tests {
+ assert_eq!(expected, &s.parse());
+ }
+ }
+
+ #[test]
+ fntest_loglevel_from_str() {
+ lettests= [
+ ("OFF", Err(())),
+ ("error", Ok(LogLevel::Error)),
+ ("warn", Ok(LogLevel::Warn)),
+ ("info", Ok(LogLevel::Info)),
+ ("debug", Ok(LogLevel::Debug)),
+ ("trace", Ok(LogLevel::Trace)),
+ ("ERROR", Ok(LogLevel::Error)),
+ ("WARN", Ok(LogLevel::Warn)),
+ ("INFO", Ok(LogLevel::Info)),
+ ("DEBUG", Ok(LogLevel::Debug)),
+ ("TRACE", Ok(LogLevel::Trace)),
+ ("asdf", Err(())),
+ ];
+ for&(s, refexpected) in&tests {
+ assert_eq!(expected, &s.parse());
+ }
+ }
+
+ #[test]
+ fntest_loglevel_show() {
+ assert_eq!("INFO", LogLevel::Info.to_string());
+ assert_eq!("ERROR", LogLevel::Error.to_string());
+ }
+
+ #[test]
+ fntest_loglevelfilter_show() {
+ assert_eq!("OFF", LogLevelFilter::Off.to_string());
+ assert_eq!("ERROR", LogLevelFilter::Error.to_string());
+ }
+
+ #[test]
+ fntest_cross_cmp() {
+ assert!(LogLevel::Debug>LogLevelFilter::Error);
+ assert!(LogLevelFilter::Warn<LogLevel::Trace);
+ assert!(LogLevelFilter::Off<LogLevel::Error);
+ }
+
+ #[test]
+ fntest_cross_eq() {
+ assert!(LogLevel::Error==LogLevelFilter::Error);
+ assert!(LogLevelFilter::Off!=LogLevel::Error);
+ assert!(LogLevel::Trace==LogLevelFilter::Trace);
+ }
+
+ #[test]
+ fntest_to_log_level() {
+ assert_eq!(Some(LogLevel::Error), LogLevelFilter::Error.to_log_level());
+ assert_eq!(None, LogLevelFilter::Off.to_log_level());
+ assert_eq!(Some(LogLevel::Debug), LogLevelFilter::Debug.to_log_level());
+ }
+
+ #[test]
+ fntest_to_log_level_filter() {
+ assert_eq!(LogLevelFilter::Error, LogLevel::Error.to_log_level_filter());
+ assert_eq!(LogLevelFilter::Trace, LogLevel::Trace.to_log_level_filter());
+ }
+
+ #[test]
+ fntest_error_trait() {
+ lete=SetLoggerError(());
+ assert_eq!(e.description(), "set_logger() called multiple times");
+ }
+}
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+/// The standard logging macro.
+///
+/// This macro will generically log with the specified `LogLevel` and `format!`
+/// based argument list.
+///
+/// The `log_level` cfg can be used to statically disable logging at various
+/// levels.
+#[macro_export]
+macro_rules!log {
+ (target: $target:expr, $lvl:expr, $($arg:tt)+) => ({
+ staticLOC: $crate::LogLocation=$crate::LogLocation {
+ __line: line!(),
+ __file: file!(),
+ __module_path: module_path!(),
+ };
+ letlvl=$lvl;
+ if!cfg!(log_level="off") &&
+ (lvl<=$crate::LogLevel::Error||!cfg!(log_level="error")) &&
+ (lvl<=$crate::LogLevel::Warn||!cfg!(log_level="warn")) &&
+ (lvl<=$crate::LogLevel::Debug||!cfg!(log_level="debug")) &&
+ (lvl<=$crate::LogLevel::Info||!cfg!(log_level="info")) &&
+ lvl<=$crate::max_log_level() {
+ $crate::__log(lvl, $target, &LOC, format_args!($($arg)+))
+ }
+ });
+ ($lvl:expr, $($arg:tt)+) => (log!(target: module_path!(), $lvl, $($arg)+))
+}
+
+/// Logs a message at the error level.
+///
+/// Logging at this level is disabled if the `log_level = "off"` cfg is
+/// present.
+#[macro_export]
+macro_rules!error {
+ (target: $target:expr, $($arg:tt)*) => (
+ log!(target: $target, $crate::LogLevel::Error, $($arg)*);
+ );
+ ($($arg:tt)*) => (
+ log!($crate::LogLevel::Error, $($arg)*);
+ )
+}
+
+/// Logs a message at the warn level.
+///
+/// Logging at this level is disabled if any of the following cfgs are present:
+/// `log_level = "off"` or `log_level = "error"`.
+#[macro_export]
+macro_rules!warn {
+ (target: $target:expr, $($arg:tt)*) => (
+ log!(target: $target, $crate::LogLevel::Warn, $($arg)*);
+ );
+ ($($arg:tt)*) => (
+ log!($crate::LogLevel::Warn, $($arg)*);
+ )
+}
+
+/// Logs a message at the info level.
+///
+/// Logging at this level is disabled if any of the following cfgs are present:
+/// `log_level = "off"`, `log_level = "error"`, or
+/// `log_level = "warn"`.
+#[macro_export]
+macro_rules!info {
+ (target: $target:expr, $($arg:tt)*) => (
+ log!(target: $target, $crate::LogLevel::Info, $($arg)*);
+ );
+ ($($arg:tt)*) => (
+ log!($crate::LogLevel::Info, $($arg)*);
+ )
+}
+
+/// Logs a message at the debug level.
+///
+/// Logging at this level is disabled if any of the following cfgs are present:
+/// `log_level = "off"`, `log_level = "error"`, `log_level = "warn"`,
+/// or `log_level = "info"`.
+#[macro_export]
+macro_rules!debug {
+ (target: $target:expr, $($arg:tt)*) => (
+ log!(target: $target, $crate::LogLevel::Debug, $($arg)*);
+ );
+ ($($arg:tt)*) => (
+ log!($crate::LogLevel::Debug, $($arg)*);
+ )
+}
+
+/// Logs a message at the trace level.
+///
+/// Logging at this level is disabled if any of the following cfgs are present:
+/// `log_level = "off"`, `log_level = "error"`, `log_level = "warn"`,
+/// `log_level = "info"`, or `log_level = "debug"`.
+#[macro_export]
+macro_rules!trace {
+ (target: $target:expr, $($arg:tt)*) => (
+ log!(target: $target, $crate::LogLevel::Trace, $($arg)*);
+ );
+ ($($arg:tt)*) => (
+ log!($crate::LogLevel::Trace, $($arg)*);
+ )
+}
+
+/// Determines if a message logged at the specified level in that module will
+/// be logged.
+///
+/// This can be used to avoid expensive computation of log message arguments if
+/// the message would be ignored anyway.
+///
+/// # Examples
+///
+/// ```rust
+/// # #[macro_use]
+/// # extern crate log;
+/// use log::LogLevel::Debug;
+///
+/// # fn foo() {
+/// if log_enabled!(Debug) {
+/// debug!("expensive debug data: {}", expensive_call());
+/// }
+/// # }
+/// # fn expensive_call() -> u32 { 0 }
+/// # fn main() {}
+/// ```
+#[macro_export]
+macro_rules!log_enabled {
+ (target: $target:expr, $lvl:expr) => ({
+ letlvl=$lvl;
+ !cfg!(log_level="off") &&
+ (lvl<=$crate::LogLevel::Error||!cfg!(log_level="error")) &&
+ (lvl<=$crate::LogLevel::Warn||!cfg!(log_level="warn")) &&
+ (lvl<=$crate::LogLevel::Debug||!cfg!(log_level="debug")) &&
+ (lvl<=$crate::LogLevel::Info||!cfg!(log_level="info")) &&
+ lvl<=$crate::max_log_level() &&
+ $crate::__enabled(lvl, $target)
+ });
+ ($lvl:expr) => (log_enabled!(target: module_path!(), $lvl))
+}
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+
+uselibc::size_t;
+useffi;
+
+/// Returns `true` iff `a` and `b` contain the same bytes.
+///
+/// This operation takes an amount of time dependent on the length of the two
+/// arrays given, but is independent of the contents of a and b.
+///
+/// # Failure
+///
+/// This function will panic the current task if `a` and `b` do not have the same
+/// length.
+pubfneq(a: &[u8], b: &[u8]) ->bool {
+ assert!(a.len() ==b.len());
+ letret=unsafe {
+ ffi::CRYPTO_memcmp(a.as_ptr() as*const _,
+ b.as_ptr() as*const _,
+ a.len() assize_t)
+ };
+ ret==0
+}
+
+#[cfg(test)]
+modtests {
+ usesuper::eq;
+
+ #[test]
+ fntest_eq() {
+ assert!(eq(&[], &[]));
+ assert!(eq(&[1], &[1]));
+ assert!(!eq(&[1, 2, 3], &[1, 2, 4]));
+ }
+
+ #[test]
+ #[should_panic]
+ fntest_diff_lens() {
+ eq(&[], &[1]);
+ }
+}
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+
+/*
+ * Copyright 2011 Google Inc.
+ * 2013 Jack Lloyd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pubmodhash;
+pubmodhmac;
+pubmodpkcs5;
+pubmodpkey;
+pubmodrand;
+pubmodsymm;
+pubmodmemcmp;
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+usestd::char;
+usestd::cmp::Ordering;
+usestd::fmt;
+usestd::u32;
+
+usesyntax;
+
+/// An inline representation of `Option<char>`.
+///
+/// This eliminates the need to do case analysis on `Option<char>` to determine
+/// ordinality with other characters.
+///
+/// (The `Option<char>` is not related to encoding. Instead, it is used in the
+/// matching engines to represent the beginning and ending boundaries of the
+/// search text.)
+#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pubstructChar(u32);
+
+implfmt::DebugforChar {
+ fnfmt(&self, f: &mutfmt::Formatter) ->fmt::Result {
+ matchchar::from_u32(self.0) {
+ None=>write!(f, "Empty"),
+ Some(c) =>write!(f, "{:?}", c),
+ }
+ }
+}
+
+implChar {
+ /// Returns true iff the character is absent.
+ #[inline]
+ pubfnis_none(self) ->bool { self.0==u32::MAX }
+
+ /// Returns the length of the character's UTF-8 encoding.
+ ///
+ /// If the character is absent, then `0` is returned.
+ #[inline]
+ pubfnlen_utf8(self) ->usize {
+ char::from_u32(self.0).map(|c|c.len_utf8()).unwrap_or(0)
+ }
+
+ /// Returns the simple case folding of this character.
+ ///
+ /// If the character is absent, then absence is returned.
+ pubfncase_fold(self) ->Char {
+ char::from_u32(self.0).map(syntax::simple_case_fold).into()
+ }
+
+ /// Returns true iff the character is a word character.
+ ///
+ /// If the character is absent, then false is returned.
+ pubfnis_word_char(self) ->bool {
+ char::from_u32(self.0).map(syntax::is_word_char).unwrap_or(false)
+ }
+
+ /// Converts the character to a real primitive `char`.
+ ///
+ /// If the character is absent, then `None` is returned.
+ pubfnas_char(self) ->Option<char> {
+ // This is only used in the `regex!` macro because it expands char
+ // classes into `match` expressions (instead of binary search).
+ char::from_u32(self.0)
+ }
+}
+
+implFrom<char>forChar {
+ fnfrom(c: char) ->Char { Char(casu32) }
+}
+
+implFrom<Option<char>>forChar {
+ fnfrom(c: Option<char>) ->Char {
+ c.map(|c|c.into()).unwrap_or(Char(u32::MAX))
+ }
+}
+
+implPartialEq<char>forChar {
+ #[inline]
+ fneq(&self, other: &char) ->bool { self.0==*otherasu32 }
+}
+
+implPartialEq<Char>forchar {
+ #[inline]
+ fneq(&self, other: &Char) ->bool { *selfasu32==other.0 }
+}
+
+implPartialOrd<char>forChar {
+ #[inline]
+ fnpartial_cmp(&self, other: &char) ->Option<Ordering> {
+ self.0.partial_cmp(&(*otherasu32))
+ }
+}
+
+implPartialOrd<Char>forchar {
+ #[inline]
+ fnpartial_cmp(&self, other: &Char) ->Option<Ordering> {
+ (*selfasu32).partial_cmp(&other.0)
+ }
+}
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+usestd::ops;
+
+usechar::Char;
+useprefix::Prefix;
+
+/// Represents a location in the input.
+#[derive(Clone, Copy, Debug)]
+pubstructInputAt {
+ pos: usize,
+ c: Char,
+ len: usize,
+}
+
+implInputAt {
+ /// Returns true iff this position is at the beginning of the input.
+ pubfnis_beginning(&self) ->bool {
+ self.pos==0
+ }
+
+ /// Returns the character at this position.
+ ///
+ /// If this position is just before or after the input, then an absent
+ /// character is returned.
+ pubfnchar(&self) ->Char {
+ self.c
+ }
+
+ /// Returns the UTF-8 width of the character at this position.
+ pubfnlen(&self) ->usize {
+ self.len
+ }
+
+ /// Returns the byte offset of this position.
+ pubfnpos(&self) ->usize {
+ self.pos
+ }
+
+ /// Returns the byte offset of the next position in the input.
+ pubfnnext_pos(&self) ->usize {
+ self.pos+self.len
+ }
+}
+
+/// An abstraction over input used in the matching engines.
+pubtraitInput {
+ /// Return an encoding of the position at byte offset `i`.
+ fnat(&self, i: usize) ->InputAt;
+ /// Return an encoding of the char position just prior to byte offset `i`.
+ fnprevious_at(&self, i: usize) ->InputAt;
+ /// Scan the input for a matching prefix.
+ fnprefix_at(&self, prefixes: &Prefix, at: InputAt) ->Option<InputAt>;
+}
+
+/// An input reader over characters.
+///
+/// (This is the only implementation of `Input` at the moment.)
+#[derive(Debug)]
+pubstructCharInput<'t>(&'tstr);
+
+impl<'t>CharInput<'t> {
+ /// Return a new character input reader for the given string.
+ pubfnnew(s: &'tstr) ->CharInput<'t> {
+ CharInput(s)
+ }
+}
+
+impl<'t>ops::DerefforCharInput<'t> {
+ typeTarget=str;
+
+ fnderef(&self) ->&str {
+ self.0
+ }
+}
+
+impl<'t>InputforCharInput<'t> {
+ // This `inline(always)` increases throughput by almost 25% on the `hard`
+ // benchmarks over a normal `inline` annotation.
+ //
+ // I'm not sure why `#[inline]` isn't enough to convince LLVM, but it is
+ // used *a lot* in the guts of the matching engines.
+ #[inline(always)]
+ fnat(&self, i: usize) ->InputAt {
+ letc=self[i..].chars().next().into();
+ InputAt {
+ pos: i,
+ c: c,
+ len: c.len_utf8(),
+ }
+ }
+
+ fnprevious_at(&self, i: usize) ->InputAt {
+ letc: Char=self[..i].chars().rev().next().into();
+ letlen=c.len_utf8();
+ InputAt {
+ pos: i-len,
+ c: c,
+ len: len,
+ }
+ }
+
+ fnprefix_at(&self, prefixes: &Prefix, at: InputAt) ->Option<InputAt> {
+ prefixes.find(&self[at.pos()..]).map(|(s, _)|self.at(at.pos() +s))
+ }
+}
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This crate provides a native implementation of regular expressions that is
+//! heavily based on RE2 both in syntax and in implementation. Notably,
+//! backreferences and arbitrary lookahead/lookbehind assertions are not
+//! provided. In return, regular expression searching provided by this package
+//! has excellent worst-case performance. The specific syntax supported is
+//! documented further down.
+//!
+//! This crate's documentation provides some simple examples, describes Unicode
+//! support and exhaustively lists the supported syntax. For more specific
+//! details on the API, please see the documentation for the `Regex` type.
+//!
+//! # Usage
+//!
+//! This crate is [on crates.io](https://crates.io/crates/regex) and can be
+//! used by adding `regex` to your dependencies in your project's `Cargo.toml`.
+//!
+//! ```toml
+//! [dependencies]
+//! regex = "0.1.8"
+//! ```
+//!
+//! and this to your crate root:
+//!
+//! ```rust
+//! extern crate regex;
+//! ```
+//!
+//! # First example: find a date
+//!
+//! General use of regular expressions in this package involves compiling an
+//! expression and then using it to search, split or replace text. For example,
+//! to confirm that some text resembles a date:
+//!
+//! ```rust
+//! use regex::Regex;
+//! let re = Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
+//! assert!(re.is_match("2014-01-01"));
+//! ```
+//!
+//! Notice the use of the `^` and `$` anchors. In this crate, every expression
+//! is executed with an implicit `.*?` at the beginning and end, which allows
+//! it to match anywhere in the text. Anchors can be used to ensure that the
+//! full text matches an expression.
+//!
+//! This example also demonstrates the utility of
+//! [raw strings](http://doc.rust-lang.org/stable/reference.html#raw-byte-string-literals)
+//! in Rust, which
+//! are just like regular strings except they are prefixed with an `r` and do
+//! not process any escape sequences. For example, `"\\d"` is the same
+//! expression as `r"\d"`.
+//!
+//! # The `regex!` macro
+//!
+//! Rust's compile-time meta-programming facilities provide a way to write a
+//! `regex!` macro which compiles regular expressions *when your program
+//! compiles*. Said differently, if you only use `regex!` to build regular
+//! expressions in your program, then your program cannot compile with an
+//! invalid regular expression. Moreover, the `regex!` macro compiles the
+//! given expression to native Rust code, which ideally makes it faster.
+//! Unfortunately (or fortunately), the dynamic implementation has had a lot
+//! more optimization work put it into it currently, so it is faster than
+//! the `regex!` macro in most cases.
+//!
+//! To use the `regex!` macro, you must enable the `plugin` feature and import
+//! the `regex_macros` crate as a syntax extension:
+//!
+//! ```ignore
+//! #![feature(plugin)]
+//! #![plugin(regex_macros)]
+//! extern crate regex;
+//!
+//! fn main() {
+//! let re = regex!(r"^\d{4}-\d{2}-\d{2}$");
+//! assert!(re.is_match("2014-01-01"));
+//! }
+//! ```
+//!
+//! There are a few things worth mentioning about using the `regex!` macro.
+//! Firstly, the `regex!` macro *only* accepts string *literals*.
+//! Secondly, the `regex` crate *must* be linked with the name `regex` since
+//! the generated code depends on finding symbols in the `regex` crate.
+//!
+//! One downside of using the `regex!` macro is that it can increase the
+//! size of your program's binary since it generates specialized Rust code.
+//! The extra size probably won't be significant for a small number of
+//! expressions, but 100+ calls to `regex!` will probably result in a
+//! noticeably bigger binary.
+//!
+//! **NOTE**: This is implemented using a compiler plugin, which is not
+//! available on the Rust 1.0 beta/stable channels. Therefore, you'll only
+//! be able to use `regex!` on the nightlies.
+//!
+//! # Example: iterating over capture groups
+//!
+//! This crate provides convenient iterators for matching an expression
+//! repeatedly against a search string to find successive non-overlapping
+//! matches. For example, to find all dates in a string and be able to access
+//! them by their component pieces:
+//!
+//! ```rust
+//! # extern crate regex; use regex::Regex;
+//! # fn main() {
+//! let re = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap();
+//! let text = "2012-03-14, 2013-01-01 and 2014-07-05";
+//! for cap in re.captures_iter(text) {
+//! println!("Month: {} Day: {} Year: {}",
+//! cap.at(2).unwrap_or(""), cap.at(3).unwrap_or(""),
+//! cap.at(1).unwrap_or(""));
+//! }
+//! // Output:
+//! // Month: 03 Day: 14 Year: 2012
+//! // Month: 01 Day: 01 Year: 2013
+//! // Month: 07 Day: 05 Year: 2014
+//! # }
+//! ```
+//!
+//! Notice that the year is in the capture group indexed at `1`. This is
+//! because the *entire match* is stored in the capture group at index `0`.
+//!
+//! # Example: replacement with named capture groups
+//!
+//! Building on the previous example, perhaps we'd like to rearrange the date
+//! formats. This can be done with text replacement. But to make the code
+//! clearer, we can *name* our capture groups and use those names as variables
+//! in our replacement text:
+//!
+//! ```rust
+//! # extern crate regex; use regex::Regex;
+//! # fn main() {
+//! let re = Regex::new(r"(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})").unwrap();
+//! let before = "2012-03-14, 2013-01-01 and 2014-07-05";
+//! let after = re.replace_all(before, "$m/$d/$y");
+//! assert_eq!(after, "03/14/2012, 01/01/2013 and 07/05/2014");
+//! # }
+//! ```
+//!
+//! The `replace` methods are actually polymorphic in the replacement, which
+//! provides more flexibility than is seen here. (See the documentation for
+//! `Regex::replace` for more details.)
+//!
+//! Note that if your regex gets complicated, you can use the `x` flag to
+//! enable insigificant whitespace mode, which also lets you write comments:
+//!
+//! ```rust
+//! # extern crate regex; use regex::Regex;
+//! # fn main() {
+//! let re = Regex::new(r"(?x)
+//! (?P<y>\d{4}) # the year
+//! -
+//! (?P<m>\d{2}) # the month
+//! -
+//! (?P<d>\d{2}) # the day
+//! ").unwrap();
+//! let before = "2012-03-14, 2013-01-01 and 2014-07-05";
+//! let after = re.replace_all(before, "$m/$d/$y");
+//! assert_eq!(after, "03/14/2012, 01/01/2013 and 07/05/2014");
+//! # }
+//! ```
+//!
+//! # Pay for what you use
+//!
+//! With respect to searching text with a regular expression, there are three
+//! questions that can be asked:
+//!
+//! 1. Does the text match this expression?
+//! 2. If so, where does it match?
+//! 3. Where are the submatches?
+//!
+//! Generally speaking, this crate could provide a function to answer only #3,
+//! which would subsume #1 and #2 automatically. However, it can be
+//! significantly more expensive to compute the location of submatches, so it's
+//! best not to do it if you don't need to.
+//!
+//! Therefore, only use what you need. For example, don't use `find` if you
+//! only need to test if an expression matches a string. (Use `is_match`
+//! instead.)
+//!
+//! # Unicode
+//!
+//! This implementation executes regular expressions **only** on sequences of
+//! Unicode scalar values while exposing match locations as byte indices into
+//! the search string.
+//!
+//! Currently, only simple case folding is supported. Namely, when matching
+//! case-insensitively, the characters are first mapped using the
+//! [simple case folding](ftp://ftp.unicode.org/Public/UNIDATA/CaseFolding.txt)
+//! mapping.
+//!
+//! Regular expressions themselves are also **only** interpreted as a sequence
+//! of Unicode scalar values. This means you can use Unicode characters
+//! directly in your expression:
+//!
+//! ```rust
+//! # extern crate regex; use regex::Regex;
+//! # fn main() {
+//! let re = Regex::new(r"(?i)Δ+").unwrap();
+//! assert_eq!(re.find("ΔδΔ"), Some((0, 6)));
+//! # }
+//! ```
+//!
+//! Finally, Unicode general categories and scripts are available as character
+//! classes. For example, you can match a sequence of numerals, Greek or
+//! Cherokee letters:
+//!
+//! ```rust
+//! # extern crate regex; use regex::Regex;
+//! # fn main() {
+//! let re = Regex::new(r"[\pN\p{Greek}\p{Cherokee}]+").unwrap();
+//! assert_eq!(re.find("abcΔᎠβⅠᏴγδⅡxyz"), Some((3, 23)));
+//! # }
+//! ```
+//!
+//! # Syntax
+//!
+//! The syntax supported in this crate is almost in an exact correspondence
+//! with the syntax supported by RE2. It is documented below.
+//!
+//! Note that the regular expression parser and abstract syntax are exposed in
+//! a separate crate,
+//! [`regex-syntax`](../regex_syntax/index.html).
+//!
+//! ## Matching one character
+//!
+//! <pre class="rust">
+//! . any character except new line (includes new line with s flag)
+//! [xyz] A character class matching either x, y or z.
+//! [^xyz] A character class matching any character except x, y and z.
+//! [a-z] A character class matching any character in range a-z.
+//! \d digit (\p{Nd})
+//! \D not digit
+//! [:alpha:] ASCII character class ([A-Za-z])
+//! [:^alpha:] Negated ASCII character class ([^A-Za-z])
+//! \pN One-letter name Unicode character class
+//! \p{Greek} Unicode character class (general category or script)
+//! \PN Negated one-letter name Unicode character class
+//! \P{Greek} negated Unicode character class (general category or script)
+//! </pre>
+//!
+//! Any named character class may appear inside a bracketed `[...]` character
+//! class. For example, `[\p{Greek}\pN]` matches any Greek or numeral
+//! character.
+//!
+//! ## Composites
+//!
+//! <pre class="rust">
+//! xy concatenation (x followed by y)
+//! x|y alternation (x or y, prefer x)
+//! </pre>
+//!
+//! ## Repetitions
+//!
+//! <pre class="rust">
+//! x* zero or more of x (greedy)
+//! x+ one or more of x (greedy)
+//! x? zero or one of x (greedy)
+//! x*? zero or more of x (ungreedy)
+//! x+? one or more of x (ungreedy)
+//! x?? zero or one of x (ungreedy)
+//! x{n,m} at least n x and at most m x (greedy)
+//! x{n,} at least n x (greedy)
+//! x{n} exactly n x
+//! x{n,m}? at least n x and at most m x (ungreedy)
+//! x{n,}? at least n x (ungreedy)
+//! x{n}? exactly n x
+//! </pre>
+//!
+//! ## Empty matches
+//!
+//! <pre class="rust">
+//! ^ the beginning of text (or start-of-line with multi-line mode)
+//! $ the end of text (or end-of-line with multi-line mode)
+//! \A only the beginning of text (even with multi-line mode enabled)
+//! \z only the end of text (even with multi-line mode enabled)
+//! \b a Unicode word boundary (\w on one side and \W, \A, or \z on other)
+//! \B not a Unicode word boundary
+//! </pre>
+//!
+//! ## Grouping and flags
+//!
+//! <pre class="rust">
+//! (exp) numbered capture group (indexed by opening parenthesis)
+//! (?P<name>exp) named (also numbered) capture group (allowed chars: [_0-9a-zA-Z])
+//! (?:exp) non-capturing group
+//! (?flags) set flags within current group
+//! (?flags:exp) set flags for exp (non-capturing)
+//! </pre>
+//!
+//! Flags are each a single character. For example, `(?x)` sets the flag `x`
+//! and `(?-x)` clears the flag `x`. Multiple flags can be set or cleared at
+//! the same time: `(?xy)` sets both the `x` and `y` flags and `(?x-y)` sets
+//! the `x` flag and clears the `y` flag.
+//!
+//! All flags are by default disabled. They are:
+//!
+//! <pre class="rust">
+//! i case-insensitive
+//! m multi-line mode: ^ and $ match begin/end of line
+//! s allow . to match \n
+//! U swap the meaning of x* and x*?
+//! x ignore whitespace and allow line comments (starting with `#`)
+//! </pre>
+//!
+//! Here's an example that matches case-insensitively for only part of the
+//! expression:
+//!
+//! ```rust
+//! # extern crate regex; use regex::Regex;
+//! # fn main() {
+//! let re = Regex::new(r"(?i)a+(?-i)b+").unwrap();
+//! let cap = re.captures("AaAaAbbBBBb").unwrap();
+//! assert_eq!(cap.at(0), Some("AaAaAbb"));
+//! # }
+//! ```
+//!
+//! Notice that the `a+` matches either `a` or `A`, but the `b+` only matches
+//! `b`.
+//!
+//! ## Escape sequences
+//!
+//! <pre class="rust">
+//! \* literal *, works for any punctuation character: \.+*?()|[]{}^$
+//! \a bell (\x07)
+//! \f form feed (\x0C)
+//! \t horizontal tab
+//! \n new line
+//! \r carriage return
+//! \v vertical tab (\x0B)
+//! \123 octal character code (up to three digits)
+//! \x7F hex character code (exactly two digits)
+//! \x{10FFFF} any hex character code corresponding to a Unicode code point
+//! </pre>
+//!
+//! ## Perl character classes (Unicode friendly)
+//!
+//! These classes are based on the definitions provided in
+//! [UTS#18](http://www.unicode.org/reports/tr18/#Compatibility_Properties):
+//!
+//! <pre class="rust">
+//! \d digit (\p{Nd})
+//! \D not digit
+//! \s whitespace (\p{White_Space})
+//! \S not whitespace
+//! \w word character (\p{Alphabetic} + \p{M} + \d + \p{Pc} + \p{Join_Control})
+//! \W not word character
+//! </pre>
+//!
+//! ## ASCII character classes
+//!
+//! <pre class="rust">
+//! [:alnum:] alphanumeric ([0-9A-Za-z])
+//! [:alpha:] alphabetic ([A-Za-z])
+//! [:ascii:] ASCII ([\x00-\x7F])
+//! [:blank:] blank ([\t ])
+//! [:cntrl:] control ([\x00-\x1F\x7F])
+//! [:digit:] digits ([0-9])
+//! [:graph:] graphical ([!-~])
+//! [:lower:] lower case ([a-z])
+//! [:print:] printable ([ -~])
+//! [:punct:] punctuation ([!-/:-@[-`{-~])
+//! [:space:] whitespace ([\t\n\v\f\r ])
+//! [:upper:] upper case ([A-Z])
+//! [:word:] word characters ([0-9A-Za-z_])
+//! [:xdigit:] hex digit ([0-9A-Fa-f])
+//! </pre>
+//!
+//! # Untrusted input
+//!
+//! This crate can handle both untrusted regular expressions and untrusted
+//! search text.
+//!
+//! Untrusted regular expressions are handled by capping the size of a compiled
+//! regular expression. (See `Regex::with_size_limit`.) Without this, it would
+//! be trivial for an attacker to exhaust your system's memory with expressions
+//! like `a{100}{100}{100}`.
+//!
+//! Untrusted search text is allowed because the matching engine(s) in this
+//! crate have time complexity `O(mn)` (with `m ~ regex` and `n ~ search
+//! text`), which means there's no way to cause exponential blow-up like with
+//! some other regular expression engines. (We pay for this by disallowing
+//! features like arbitrary look-ahead and back-references.)
+
+#![deny(missing_docs)]
+#![cfg_attr(test, deny(warnings))]
+#![cfg_attr(feature="pattern", feature(pattern))]
+#![doc(html_logo_url="http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
+ html_favicon_url="http://www.rust-lang.org/favicon.ico",
+ html_root_url="http://doc.rust-lang.org/regex/")]
+
+externcrateaho_corasick;
+externcratememchr;
+externcrateregex_syntaxassyntax;
+
+pubusere::{
+ Regex, Error, Captures, SubCaptures, SubCapturesPos, SubCapturesNamed,
+ FindCaptures, FindMatches,
+ Replacer, NoExpand, RegexSplits, RegexSplitsN,
+ quote, is_match,
+};
+
+modbacktrack;
+modchar;
+modcompile;
+modinput;
+modpool;
+modprefix;
+modprogram;
+modnfa;
+modre;
+
+/// The `internal` module exists to support the `regex!` macro and other
+/// suspicious activity, such as testing different matching engines.
+#[doc(hidden)]
+pubmodinternal {
+ pubusechar::Char;
+ pubuseinput::{Input, CharInput, InputAt};
+ pubuseprogram::{
+ Program, MatchEngine, CharRanges, Inst, LookInst, OneChar,
+ };
+ pubusere::ExNative;
+ pubusere::Regex::{Dynamic, Native};
+}
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+usestd::cmp::{self, Ordering};
+
+usesyntax;
+
+useError;
+usebacktrack::{Backtrack, BackMachine};
+usechar::Char;
+usecompile::Compiler;
+usenfa::{Nfa, NfaThreads};
+usepool::Pool;
+useprefix::Prefix;
+usere::CaptureIdxs;
+
+constNUM_PREFIX_LIMIT: usize=30;
+constPREFIX_LENGTH_LIMIT: usize=15;
+
+pubtypeInstIdx=usize;
+
+/// An instruction, the underlying unit of a compiled regular expression
+#[derive(Clone, Debug)]
+pubenumInst {
+ /// A match has occurred.
+ /// This is always the last instruction and only occurs in a single spot.
+ /// We could special case this in the code, but it is much clearer to
+ /// handle it as a proper instruction.
+ Match,
+ /// Save the current location in the input into the given capture location.
+ Save(usize),
+ /// Jump to the instruction given.
+ Jump(InstIdx),
+ /// Match either instruction, preferring the first.
+ Split(InstIdx, InstIdx),
+ /// A zero-width instruction. When this instruction matches, the input
+ /// is not advanced.
+ EmptyLook(LookInst),
+ /// Match a single possibly case insensitive character.
+ Char(OneChar),
+ /// Match one or more possibly case insensitive character ranges.
+ Ranges(CharRanges),
+}
+
+/// A single character instruction.
+#[derive(Clone, Debug)]
+pubstructOneChar {
+ /// The character.
+ pubc: char,
+ /// True if the character should be matched case insensitively.
+ /// (i.e., The input character will need to be case folded.)
+ pubcasei: bool,
+}
+
+/// A multi-range character class instruction.
+#[derive(Clone, Debug)]
+pubstructCharRanges {
+ /// Sorted sequence of non-overlapping ranges.
+ pubranges: Vec<(char, char)>,
+ /// Whether to match case insensitively.
+ pubcasei: bool,
+}
+
+/// The set of zero-width match instructions.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pubenumLookInst {
+ /// Start of line or input.
+ StartLine,
+ /// End of line or input.
+ EndLine,
+ /// Start of input.
+ StartText,
+ /// End of input.
+ EndText,
+ /// Word character on one side and non-word character on other.
+ WordBoundary,
+ /// Word character on both sides or non-word character on both sides.
+ NotWordBoundary,
+}
+
+implOneChar {
+ /// Tests whether the given input character matches this instruction.
+ #[inline(always)]// About ~5-15% more throughput then `#[inline]`
+ pubfnmatches(&self, c: Char) ->bool {
+ self.c==c|| (self.casei&&self.c==c.case_fold())
+ }
+}
+
+implCharRanges {
+ /// Emits a range specifically for the `.` expression.
+ pubfnany() ->CharRanges {
+ CharRanges {
+ ranges: vec![('\x00', '\u{10ffff}')],
+ casei: false,
+ }
+ }
+
+ /// Emits a range specifically for the `(?s).` expression.
+ pubfnany_nonl() ->CharRanges {
+ CharRanges {
+ ranges: vec![('\x00', '\x09'), ('\x0B', '\u{10ffff}')],
+ casei: false,
+ }
+ }
+
+ /// Emits a range from the AST character class.
+ pubfnfrom_class(cls: syntax::CharClass) ->CharRanges {
+ letcasei=cls.is_case_insensitive();
+ CharRanges {
+ ranges: cls.into_iter().map(|r| (r.start, r.end)).collect(),
+ casei: casei,
+ }
+ }
+
+ /// Tests whether the given input character matches this instruction.
+ #[inline(always)]// About ~5-15% more throughput then `#[inline]`
+ pubfnmatches(&self, mutc: Char) ->Option<usize> {
+ ifself.casei {
+ c=c.case_fold();
+ }
+ // This speeds up the `match_class_unicode` benchmark by checking
+ // some common cases quickly without binary search. e.g., Matching
+ // a Unicode class on predominantly ASCII text.
+ foriin0..cmp::min(self.ranges.len(), 4) {
+ letr=self.ranges[i];
+ ifc<r.0 {
+ returnNone;
+ }
+ ifc<=r.1 {
+ returnSome(i);
+ }
+ }
+ self.ranges.binary_search_by(|r| {
+ ifr.1<c {
+ Ordering::Less
+ } elseifr.0>c {
+ Ordering::Greater
+ } else {
+ Ordering::Equal
+ }
+ }).ok()
+ }
+}
+
+implLookInst {
+ /// Tests whether the pair of characters matches this zero-width
+ /// instruction.
+ pubfnmatches(&self, c1: Char, c2: Char) ->bool {
+ useself::LookInst::*;
+ match*self {
+ StartLine=>c1.is_none() ||c1=='\n',
+ EndLine=>c2.is_none() ||c2=='\n',
+ StartText=>c1.is_none(),
+ EndText=>c2.is_none(),
+ refwbty=> {
+ let (w1, w2) = (c1.is_word_char(), c2.is_word_char());
+ (*wbty==WordBoundary&&w1^w2)
+ || (*wbty==NotWordBoundary&&!(w1^w2))
+ }
+ }
+ }
+}
+
+/// The matching engines offered by this regex implementation.
+///
+/// N.B. This is exported for use in testing.
+#[doc(hidden)]
+#[derive(Clone, Copy, Debug)]
+pubenumMatchEngine {
+ /// A bounded backtracking implementation. About twice as fast as the
+ /// NFA, but can only work on small regexes and small input.
+ Backtrack,
+ /// A full NFA simulation. Can always be employed but almost always the
+ /// slowest choice.
+ Nfa,
+ /// If the entire regex is a literal and no capture groups have been
+ /// requested, then we can degrade to a simple substring match.
+ Literals,
+}
+
+/// Program represents a compiled regular expression. Once an expression is
+/// compiled, its representation is immutable and will never change.
+/// (Well, almost. In fact, the matching engines cache state that can be
+/// reused on subsequent searches. But this is interior mutability that
+/// shouldn't be observable by the caller.)
+#[derive(Debug)]
+pubstructProgram {
+ /// The original regular expression string.
+ puboriginal: String,
+ /// A sequence of instructions.
+ pubinsts: Vec<Inst>,
+ /// The sequence of capture group names. There is an entry for each capture
+ /// group index and a name exists only if the capture group is named.
+ pubcap_names: Vec<Option<String>>,
+ /// If the regular expression requires a literal prefix in order to have a
+ /// match, that prefix is stored here as a DFA.
+ pubprefixes: Prefix,
+ /// True iff matching any literal prefix indicates a match.
+ pubprefixes_complete: bool,
+ /// True iff program is anchored at the beginning.
+ pubanchored_begin: bool,
+ /// True iff program is anchored at the end.
+ pubanchored_end: bool,
+ /// The type of matching engine to use.
+ /// When `None` (the default), pick an engine automatically.
+ pubengine: Option<MatchEngine>,
+ /// Cached NFA threads.
+ pubnfa_threads: Pool<NfaThreads>,
+ /// Cached backtracking memory.
+ pubbacktrack: Pool<BackMachine>,
+}
+
+implProgram {
+ /// Compiles a Regex.
+ pubfnnew(
+ engine: Option<MatchEngine>,
+ size_limit: usize,
+ re: &str,
+ ) ->Result<Program, Error> {
+ letexpr=try!(syntax::Expr::parse(re));
+ let (insts, cap_names) =try!(Compiler::new(size_limit).compile(expr));
+ let (insts_len, ncaps) = (insts.len(), num_captures(&insts));
+ letcreate_threads=move||NfaThreads::new(insts_len, ncaps);
+ letcreate_backtrack=move||BackMachine::new();
+ letmutprog=Program {
+ original: re.into(),
+ insts: insts,
+ cap_names: cap_names,
+ prefixes: Prefix::Empty,
+ prefixes_complete: false,
+ anchored_begin: false,
+ anchored_end: false,
+ engine: engine,
+ nfa_threads: Pool::new(Box::new(create_threads)),
+ backtrack: Pool::new(Box::new(create_backtrack)),
+ };
+
+ prog.find_prefixes();
+ prog.anchored_begin=matchprog.insts[1] {
+ Inst::EmptyLook(LookInst::StartText) =>true,
+ _ =>false,
+ };
+ prog.anchored_end=matchprog.insts[prog.insts.len() -3] {
+ Inst::EmptyLook(LookInst::EndText) =>true,
+ _ =>false,
+ };
+ Ok(prog)
+ }
+
+ /// Executes a compiled regex program.
+ pubfnexec(
+ &self,
+ caps: &mutCaptureIdxs,
+ text: &str,
+ start: usize,
+ ) ->bool {
+ matchself.choose_engine(caps.len(), text) {
+ MatchEngine::Backtrack=>Backtrack::exec(self, caps, text, start),
+ MatchEngine::Nfa=>Nfa::exec(self, caps, text, start),
+ MatchEngine::Literals=> {
+ matchself.prefixes.find(&text[start..]) {
+ None=>false,
+ Some((s, e)) => {
+ ifcaps.len() ==2 {
+ caps[0] =Some(start+s);
+ caps[1] =Some(start+e);
+ }
+ true
+ }
+ }
+ }
+ }
+ }
+
+ fnchoose_engine(&self, cap_len: usize, text: &str) ->MatchEngine {
+ // If the engine is already chosen, then we use it.
+ // But that might not be a good idea. e.g., What if `Literals` is
+ // chosen and it can't work? I guess we should probably check whether
+ // the chosen engine is appropriate or not.
+ self.engine.unwrap_or_else(|| {
+ ifcap_len<=2
+ &&self.prefixes.preserves_priority()
+ &&self.prefixes_complete {
+ MatchEngine::Literals
+ } elseifBacktrack::should_exec(self, text) {
+ // We're only here if the input and regex combined are small.
+ MatchEngine::Backtrack
+ } else {
+ MatchEngine::Nfa
+ }
+ })
+ }
+
+ /// Returns the total number of capture groups in the regular expression.
+ /// This includes the zeroth capture.
+ pubfnnum_captures(&self) ->usize {
+ num_captures(&self.insts)
+ }
+
+ /// Allocate new capture groups.
+ pubfnalloc_captures(&self) ->Vec<Option<usize>> {
+ vec![None; 2*self.num_captures()]
+ }
+
+ /// Find and store a prefix machine for the current program.
+ pubfnfind_prefixes(&mutself) {
+ useself::Inst::*;
+
+ let (ps, complete) =self.prefixes_from_insts(1);
+ ifps.len() >0 {
+ self.prefixes=Prefix::new(ps);
+ self.prefixes_complete=complete;
+ return;
+ }
+ letmutpc=1;
+ letmutprefixes=vec![];
+ letmutpcomplete=true;
+ whileletSplit(x, y) =self.insts[pc] {
+ let (xps, xcomplete) =self.prefixes_from_insts(x);
+ let (yps, ycomplete) =self.prefixes_from_insts(y);
+ letmutdone=false;
+ match (&self.insts[x], &self.insts[y]) {
+ // We should be able to support this. Add explicit stack. ---AG
+ (&Split(_, _), &Split(_, _)) =>return,
+ (_, &Split(_, _)) ifxps.len() ==0=>return,
+ (_, &Split(_, _)) => {
+ pcomplete=pcomplete&&xcomplete;
+ prefixes.extend(xps);
+ pc=y;
+ }
+ (&Split(_, _), _) ifyps.len() ==0=>return,
+ (&Split(_, _), _) => {
+ pcomplete=pcomplete&&ycomplete;
+ prefixes.extend(yps);
+ pc=x;
+ }
+ _ ifxps.len() ==0||yps.len() ==0=>return,
+ // This is our base case. We've followed splits the whole
+ // way, which means both instructions lead to a match.
+ _ => {
+ pcomplete=pcomplete&&xcomplete&&ycomplete;
+ prefixes.extend(xps);
+ prefixes.extend(yps);
+ done=true;
+ }
+ }
+ // Arg. We've over-extended ourselves, quit with nothing to
+ // show for it.
+ ifprefixes.len() >NUM_PREFIX_LIMIT {
+ return;
+ }
+ ifdone { break; }
+ }
+ self.prefixes=Prefix::new(prefixes);
+ self.prefixes_complete=pcomplete&&self.prefixes.len() >0;
+ }
+
+ /// Find a prefix starting at the given instruction.
+ ///
+ /// Returns `true` in the tuple if the end of the prefix leads trivially
+ /// to a match. (This may report false negatives, but being conservative
+ /// is OK.)
+ fnprefixes_from_insts(&self, mutpc: usize) -> (Vec<String>, bool) {
+ useself::Inst::*;
+
+ letmutcomplete=true;
+ letmutalts=vec![String::new()];
+ whilepc<self.insts.len() {
+ letinst=&self.insts[pc];
+
+ // Each iteration adds one character to every alternate prefix *or*
+ // it stops. Thus, the prefix alternates grow in lock step, and it
+ // suffices to check one of them to see if the prefix limit has been
+ // exceeded.
+ ifalts[0].len() >PREFIX_LENGTH_LIMIT {
+ complete=false;
+ break;
+ }
+ match*inst {
+ Save(_) => { pc+=1; continue } // completely ignore it
+ Char(OneChar { c, casei: false }) => {
+ foraltin&mutalts {
+ alt.push(c);
+ }
+ pc+=1;
+ }
+ Ranges(CharRanges { refranges, casei: false }) => {
+ letnchars=num_chars_in_ranges(ranges);
+ ifalts.len() *nchars>NUM_PREFIX_LIMIT {
+ complete=false;
+ break;
+ }
+
+ letorig=alts;
+ alts=Vec::with_capacity(orig.len());
+ for&(s, e) inranges {
+ forcin (sasu32)..(easu32+1){
+ foraltin&orig {
+ letmutalt=alt.clone();
+ alt.push(::std::char::from_u32(c).unwrap());
+ alts.push(alt);
+ }
+ }
+ }
+ pc+=1;
+ }
+ Jump(pc2) =>pc=pc2,
+ _ => { complete=self.leads_to_match(pc); break }
+ }
+ }
+ ifalts[0].len() ==0 {
+ (vec![], false)
+ } else {
+ (alts, complete)
+ }
+ }
+
+ fnleads_to_match(&self, mutpc: usize) ->bool {
+ // I'm pretty sure this is conservative, so it might have some
+ // false negatives.
+ loop {
+ matchself.insts[pc] {
+ Inst::Match=>returntrue,
+ Inst::Save(_) =>pc+=1,
+ Inst::Jump(pc2) =>pc=pc2,
+ _ =>returnfalse,
+ }
+ }
+ }
+}
+
+implCloneforProgram {
+ fnclone(&self) ->Program {
+ let (insts_len, ncaps) = (self.insts.len(), self.num_captures());
+ letcreate_threads=move||NfaThreads::new(insts_len, ncaps);
+ letcreate_backtrack=move||BackMachine::new();
+ Program {
+ original: self.original.clone(),
+ insts: self.insts.clone(),
+ cap_names: self.cap_names.clone(),
+ prefixes: self.prefixes.clone(),
+ prefixes_complete: self.prefixes_complete,
+ anchored_begin: self.anchored_begin,
+ anchored_end: self.anchored_end,
+ engine: self.engine,
+ nfa_threads: Pool::new(Box::new(create_threads)),
+ backtrack: Pool::new(Box::new(create_backtrack)),
+ }
+ }
+}
+
+/// Return the number of captures in the given sequence of instructions.
+fnnum_captures(insts: &[Inst]) ->usize {
+ letmutn=0;
+ forinstininsts {
+ match*inst {
+ Inst::Save(c) =>n=cmp::max(n, c+1),
+ _ => {}
+ }
+ }
+ // There's exactly 2 Save slots for every capture.
+ n/2
+}
+
+/// Count the number of characters in the given range.
+///
+/// This is useful for pre-emptively limiting the number of prefix literals
+/// we extract from a regex program.
+fnnum_chars_in_ranges(ranges: &[(char, char)]) ->usize {
+ ranges.iter()
+ .map(|&(s, e)| (easu32) - (sasu32))
+ .fold(0, |acc, len|acc+len) asusize
+}
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+usestd::borrow::Cow;
+usestd::collections::HashMap;
+usestd::collections::hash_map::Iter;
+usestd::fmt;
+#[cfg(feature="pattern")]
+usestd::str::pattern::{Pattern, Searcher, SearchStep};
+usestd::str::FromStr;
+
+useprogram::{Program, MatchEngine};
+usesyntax;
+
+constREPLACE_EXPAND: &'staticstr=r"(?x)
+ (?P<before>^|\b|[^$]) # Ignore `$$name`.
+ \$
+ (?P<name> # Match the actual capture name. Can be...
+ [0-9]+ # A sequence of digits (for indexed captures), or...
+ |
+ [_a-zA-Z][_0-9a-zA-Z]* # A name for named captures.
+ )
+";
+
+/// Type alias for representing capture indices.
+pubtypeCaptureIdxs= [Option<usize>];
+
+/// Escapes all regular expression meta characters in `text`.
+///
+/// The string returned may be safely used as a literal in a regular
+/// expression.
+pubfnquote(text: &str) ->String {
+ letmutquoted=String::with_capacity(text.len());
+ forcintext.chars() {
+ ifsyntax::is_punct(c) {
+ quoted.push('\\')
+ }
+ quoted.push(c);
+ }
+ quoted
+}
+
+/// Tests if the given regular expression matches somewhere in the text given.
+///
+/// If there was a problem compiling the regular expression, an error is
+/// returned.
+///
+/// To find submatches, split or replace text, you'll need to compile an
+/// expression first.
+pubfnis_match(regex: &str, text: &str) ->Result<bool, Error> {
+ Regex::new(regex).map(|r|r.is_match(text))
+}
+
+/// An error that occurred during parsing or compiling a regular expression.
+#[derive(Debug)]
+pubenumError {
+ /// A syntax error.
+ Syntax(syntax::Error),
+ /// The compiled program exceeded the set size limit.
+ /// The argument is the size limit imposed.
+ CompiledTooBig(usize),
+ /// Hints that destructuring should not be exhaustive.
+ ///
+ /// This enum may grow additional variants, so this makes sure clients
+ /// don't count on exhaustive matching. (Otherwise, adding a new variant
+ /// could break existing code.)
+ #[doc(hidden)]
+ __Nonexhaustive,
+}
+
+impl ::std::error::ErrorforError {
+ fndescription(&self) ->&str {
+ match*self {
+ Error::Syntax(referr) =>err.description(),
+ Error::CompiledTooBig(_) =>"compiled program too big",
+ Error::__Nonexhaustive=>unreachable!(),
+ }
+ }
+
+ fncause(&self) ->Option<&::std::error::Error> {
+ match*self {
+ Error::Syntax(referr) =>Some(err),
+ _ =>None,
+ }
+ }
+}
+
+implfmt::DisplayforError {
+ fnfmt(&self, f: &mutfmt::Formatter) ->fmt::Result {
+ match*self {
+ Error::Syntax(referr) =>err.fmt(f),
+ Error::CompiledTooBig(limit) => {
+ write!(f, "Compiled regex exceeds size limit of {} bytes.",
+ limit)
+ }
+ Error::__Nonexhaustive=>unreachable!(),
+ }
+ }
+}
+
+implFrom<syntax::Error>forError {
+ fnfrom(err: syntax::Error) ->Error {
+ Error::Syntax(err)
+ }
+}
+
+/// A compiled regular expression
+///
+/// It is represented as either a sequence of bytecode instructions (dynamic)
+/// or as a specialized Rust function (native). It can be used to search, split
+/// or replace text. All searching is done with an implicit `.*?` at the
+/// beginning and end of an expression. To force an expression to match the
+/// whole string (or a prefix or a suffix), you must use an anchor like `^` or
+/// `$` (or `\A` and `\z`).
+///
+/// While this crate will handle Unicode strings (whether in the regular
+/// expression or in the search text), all positions returned are **byte
+/// indices**. Every byte index is guaranteed to be at a Unicode code point
+/// boundary.
+///
+/// The lifetimes `'r` and `'t` in this crate correspond to the lifetime of a
+/// compiled regular expression and text to search, respectively.
+///
+/// The only methods that allocate new strings are the string replacement
+/// methods. All other methods (searching and splitting) return borrowed
+/// pointers into the string given.
+///
+/// # Examples
+///
+/// Find the location of a US phone number:
+///
+/// ```rust
+/// # use regex::Regex;
+/// let re = Regex::new("[0-9]{3}-[0-9]{3}-[0-9]{4}").unwrap();
+/// assert_eq!(re.find("phone: 111-222-3333"), Some((7, 19)));
+/// ```
+///
+/// # Using the `std::str::StrExt` methods with `Regex`
+///
+/// > **Note**: This section requires that this crate is currently compiled with
+/// > the `pattern` Cargo feature enabled.
+///
+/// Since `Regex` implements `Pattern`, you can use regexes with methods
+/// defined on `std::str::StrExt`. For example, `is_match`, `find`, `find_iter`
+/// and `split` can be replaced with `StrExt::contains`, `StrExt::find`,
+/// `StrExt::match_indices` and `StrExt::split`.
+///
+/// Here are some examples:
+///
+/// ```rust,ignore
+/// # use regex::Regex;
+/// let re = Regex::new(r"\d+").unwrap();
+/// let haystack = "a111b222c";
+///
+/// assert!(haystack.contains(&re));
+/// assert_eq!(haystack.find(&re), Some(1));
+/// assert_eq!(haystack.match_indices(&re).collect::<Vec<_>>(),
+/// vec![(1, 4), (5, 8)]);
+/// assert_eq!(haystack.split(&re).collect::<Vec<_>>(), vec!["a", "b", "c"]);
+/// ```
+#[derive(Clone)]
+pubenumRegex {
+ // The representation of `Regex` is exported to support the `regex!`
+ // syntax extension. Do not rely on it.
+ //
+ // See the comments for the `program` module in `lib.rs` for a more
+ // detailed explanation for what `regex!` requires.
+ #[doc(hidden)]
+ Dynamic(Program),
+ #[doc(hidden)]
+ Native(ExNative),
+}
+
+#[doc(hidden)]
+pubstructExNative {
+ #[doc(hidden)]
+ puboriginal: &'staticstr,
+ #[doc(hidden)]
+ pubnames: &'static&'static [Option<&'staticstr>],
+ #[doc(hidden)]
+ pubprog: fn(&mutCaptureIdxs, &str, usize) ->bool,
+}
+
+implCopyforExNative {}
+
+implCloneforExNative {
+ fnclone(&self) ->ExNative {
+ *self
+ }
+}
+
+implfmt::DisplayforRegex {
+ /// Shows the original regular expression.
+ fnfmt(&self, f: &mutfmt::Formatter) ->fmt::Result {
+ write!(f, "{}", self.as_str())
+ }
+}
+
+implfmt::DebugforRegex {
+ /// Shows the original regular expression.
+ fnfmt(&self, f: &mutfmt::Formatter) ->fmt::Result {
+ fmt::Display::fmt(self, f)
+ }
+}
+
+/// Equality comparison is based on the original string. It is possible that
+/// different regular expressions have the same matching behavior, but are
+/// still compared unequal. For example, `\d+` and `\d\d*` match the same set
+/// of strings, but are not considered equal.
+implPartialEqforRegex {
+ fneq(&self, other: &Regex) ->bool {
+ self.as_str() ==other.as_str()
+ }
+}
+
+implEqforRegex {}
+
+implFromStrforRegex {
+ typeErr=Error;
+
+ /// Attempts to parse a string into a regular expression
+ fnfrom_str(s: &str) ->Result<Regex, Error> {
+ Regex::new(s)
+ }
+}
+
+implRegex {
+ /// Compiles a dynamic regular expression. Once compiled, it can be
+ /// used repeatedly to search, split or replace text in a string.
+ ///
+ /// If an invalid expression is given, then an error is returned.
+ pubfnnew(re: &str) ->Result<Regex, Error> {
+ Regex::with_size_limit(10* (1<<20), re)
+ }
+
+ /// Compiles a dynamic regular expression with the given size limit.
+ ///
+ /// The size limit is applied to the size of the *compiled* data structure.
+ /// If the data structure exceeds the size given, then an error is
+ /// returned.
+ ///
+ /// The default size limit used in `new` is 10MB.
+ pubfnwith_size_limit(size: usize, re: &str) ->Result<Regex, Error> {
+ Regex::with_engine(None, size, re)
+ }
+
+ /// Compiles a dynamic regular expression and uses given matching engine.
+ ///
+ /// This is exposed for use in testing and shouldn't be used by clients.
+ /// Instead, the regex program should choose the correct matching engine
+ /// to use automatically. (Based on the regex, the size of the input and
+ /// the type of search.)
+ ///
+ /// A value of `None` means that the engine is automatically selected,
+ /// which is the default behavior.
+ ///
+ /// **WARNING**: Passing an unsuitable engine for the given regex/input
+ /// could lead to bad things. (Not unsafe things, but panics, incorrect
+ /// matches and large memory use are all things that could happen.)
+ #[doc(hidden)]
+ pubfnwith_engine(
+ engine: Option<MatchEngine>,
+ size: usize,
+ re: &str,
+ ) ->Result<Regex, Error> {
+ Program::new(engine, size, re).map(Regex::Dynamic)
+ }
+
+
+ /// Returns true if and only if the regex matches the string given.
+ ///
+ /// # Example
+ ///
+ /// Test if some text contains at least one word with exactly 13
+ /// characters:
+ ///
+ /// ```rust
+ /// # extern crate regex; use regex::Regex;
+ /// # fn main() {
+ /// let text = "I categorically deny having triskaidekaphobia.";
+ /// assert!(Regex::new(r"\b\w{13}\b").unwrap().is_match(text));
+ /// # }
+ /// ```
+ pubfnis_match(&self, text: &str) ->bool {
+ exec(self, &mut [], text, 0)
+ }
+
+ /// Returns the start and end byte range of the leftmost-first match in
+ /// `text`. If no match exists, then `None` is returned.
+ ///
+ /// Note that this should only be used if you want to discover the position
+ /// of the match. Testing the existence of a match is faster if you use
+ /// `is_match`.
+ ///
+ /// # Example
+ ///
+ /// Find the start and end location of the first word with exactly 13
+ /// characters:
+ ///
+ /// ```rust
+ /// # extern crate regex; use regex::Regex;
+ /// # fn main() {
+ /// let text = "I categorically deny having triskaidekaphobia.";
+ /// let pos = Regex::new(r"\b\w{13}\b").unwrap().find(text);
+ /// assert_eq!(pos, Some((2, 15)));
+ /// # }
+ /// ```
+ pubfnfind(&self, text: &str) ->Option<(usize, usize)> {
+ letmutcaps= [None, None];
+ ifexec(self, &mutcaps, text, 0) {
+ Some((caps[0].unwrap(), caps[1].unwrap()))
+ } else {
+ None
+ }
+ }
+
+ /// Returns an iterator for each successive non-overlapping match in
+ /// `text`, returning the start and end byte indices with respect to
+ /// `text`.
+ ///
+ /// # Example
+ ///
+ /// Find the start and end location of every word with exactly 13
+ /// characters:
+ ///
+ /// ```rust
+ /// # extern crate regex; use regex::Regex;
+ /// # fn main() {
+ /// let text = "Retroactively relinquishing remunerations is reprehensible.";
+ /// for pos in Regex::new(r"\b\w{13}\b").unwrap().find_iter(text) {
+ /// println!("{:?}", pos);
+ /// }
+ /// // Output:
+ /// // (0, 13)
+ /// // (14, 27)
+ /// // (28, 41)
+ /// // (45, 58)
+ /// # }
+ /// ```
+ pubfnfind_iter<'r, 't>(&'rself, text: &'tstr) ->FindMatches<'r, 't> {
+ FindMatches {
+ re: self,
+ search: text,
+ last_end: 0,
+ last_match: None,
+ }
+ }
+
+ /// Returns the capture groups corresponding to the leftmost-first
+ /// match in `text`. Capture group `0` always corresponds to the entire
+ /// match. If no match is found, then `None` is returned.
+ ///
+ /// You should only use `captures` if you need access to submatches.
+ /// Otherwise, `find` is faster for discovering the location of the overall
+ /// match.
+ ///
+ /// # Examples
+ ///
+ /// Say you have some text with movie names and their release years,
+ /// like "'Citizen Kane' (1941)". It'd be nice if we could search for text
+ /// looking like that, while also extracting the movie name and its release
+ /// year separately.
+ ///
+ /// ```rust
+ /// # extern crate regex; use regex::Regex;
+ /// # fn main() {
+ /// let re = Regex::new(r"'([^']+)'\s+\((\d{4})\)").unwrap();
+ /// let text = "Not my favorite movie: 'Citizen Kane' (1941).";
+ /// let caps = re.captures(text).unwrap();
+ /// assert_eq!(caps.at(1), Some("Citizen Kane"));
+ /// assert_eq!(caps.at(2), Some("1941"));
+ /// assert_eq!(caps.at(0), Some("'Citizen Kane' (1941)"));
+ /// # }
+ /// ```
+ ///
+ /// Note that the full match is at capture group `0`. Each subsequent
+ /// capture group is indexed by the order of its opening `(`.
+ ///
+ /// We can make this example a bit clearer by using *named* capture groups:
+ ///
+ /// ```rust
+ /// # extern crate regex; use regex::Regex;
+ /// # fn main() {
+ /// let re = Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)")
+ /// .unwrap();
+ /// let text = "Not my favorite movie: 'Citizen Kane' (1941).";
+ /// let caps = re.captures(text).unwrap();
+ /// assert_eq!(caps.name("title"), Some("Citizen Kane"));
+ /// assert_eq!(caps.name("year"), Some("1941"));
+ /// assert_eq!(caps.at(0), Some("'Citizen Kane' (1941)"));
+ /// # }
+ /// ```
+ ///
+ /// Here we name the capture groups, which we can access with the `name`
+ /// method. Note that the named capture groups are still accessible with
+ /// `at`.
+ ///
+ /// The `0`th capture group is always unnamed, so it must always be
+ /// accessed with `at(0)`.
+ pubfncaptures<'t>(&self, text: &'tstr) ->Option<Captures<'t>> {
+ letmutcaps=self.alloc_captures();
+ ifexec(self, &mutcaps, text, 0) {
+ Some(Captures::new(self, text, caps))
+ } else {
+ None
+ }
+ }
+
+ /// Returns an iterator over all the non-overlapping capture groups matched
+ /// in `text`. This is operationally the same as `find_iter` (except it
+ /// yields information about submatches).
+ ///
+ /// # Example
+ ///
+ /// We can use this to find all movie titles and their release years in
+ /// some text, where the movie is formatted like "'Title' (xxxx)":
+ ///
+ /// ```rust
+ /// # extern crate regex; use regex::Regex;
+ /// # fn main() {
+ /// let re = Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)")
+ /// .unwrap();
+ /// let text = "'Citizen Kane' (1941), 'The Wizard of Oz' (1939), 'M' (1931).";
+ /// for caps in re.captures_iter(text) {
+ /// println!("Movie: {:?}, Released: {:?}", caps.name("title"), caps.name("year"));
+ /// }
+ /// // Output:
+ /// // Movie: Citizen Kane, Released: 1941
+ /// // Movie: The Wizard of Oz, Released: 1939
+ /// // Movie: M, Released: 1931
+ /// # }
+ /// ```
+ pubfncaptures_iter<'r, 't>(&'rself, text: &'tstr)
+ ->FindCaptures<'r, 't> {
+ FindCaptures {
+ re: self,
+ search: text,
+ last_match: None,
+ last_end: 0,
+ }
+ }
+
+ /// Returns an iterator of substrings of `text` delimited by a match
+ /// of the regular expression.
+ /// Namely, each element of the iterator corresponds to text that *isn't*
+ /// matched by the regular expression.
+ ///
+ /// This method will *not* copy the text given.
+ ///
+ /// # Example
+ ///
+ /// To split a string delimited by arbitrary amounts of spaces or tabs:
+ ///
+ /// ```rust
+ /// # extern crate regex; use regex::Regex;
+ /// # fn main() {
+ /// let re = Regex::new(r"[ \t]+").unwrap();
+ /// let fields: Vec<&str> = re.split("a b \t c\td e").collect();
+ /// assert_eq!(fields, vec!("a", "b", "c", "d", "e"));
+ /// # }
+ /// ```
+ pubfnsplit<'r, 't>(&'rself, text: &'tstr) ->RegexSplits<'r, 't> {
+ RegexSplits {
+ finder: self.find_iter(text),
+ last: 0,
+ }
+ }
+
+ /// Returns an iterator of at most `limit` substrings of `text` delimited
+ /// by a match of the regular expression. (A `limit` of `0` will return no
+ /// substrings.)
+ /// Namely, each element of the iterator corresponds to text that *isn't*
+ /// matched by the regular expression.
+ /// The remainder of the string that is not split will be the last element
+ /// in the iterator.
+ ///
+ /// This method will *not* copy the text given.
+ ///
+ /// # Example
+ ///
+ /// Get the first two words in some text:
+ ///
+ /// ```rust
+ /// # extern crate regex; use regex::Regex;
+ /// # fn main() {
+ /// let re = Regex::new(r"\W+").unwrap();
+ /// let fields: Vec<&str> = re.splitn("Hey! How are you?", 3).collect();
+ /// assert_eq!(fields, vec!("Hey", "How", "are you?"));
+ /// # }
+ /// ```
+ pubfnsplitn<'r, 't>(&'rself, text: &'tstr, limit: usize)
+ ->RegexSplitsN<'r, 't> {
+ RegexSplitsN {
+ splits: self.split(text),
+ cur: 0,
+ limit: limit,
+ }
+ }
+
+ /// Replaces the leftmost-first match with the replacement provided.
+ /// The replacement can be a regular string (where `$N` and `$name` are
+ /// expanded to match capture groups) or a function that takes the matches'
+ /// `Captures` and returns the replaced string.
+ ///
+ /// If no match is found, then a copy of the string is returned unchanged.
+ ///
+ /// # Examples
+ ///
+ /// Note that this function is polymorphic with respect to the replacement.
+ /// In typical usage, this can just be a normal string:
+ ///
+ /// ```rust
+ /// # extern crate regex; use regex::Regex;
+ /// # fn main() {
+ /// let re = Regex::new("[^01]+").unwrap();
+ /// assert_eq!(re.replace("1078910", ""), "1010");
+ /// # }
+ /// ```
+ ///
+ /// But anything satisfying the `Replacer` trait will work. For example,
+ /// a closure of type `|&Captures| -> String` provides direct access to the
+ /// captures corresponding to a match. This allows one to access
+ /// submatches easily:
+ ///
+ /// ```rust
+ /// # extern crate regex; use regex::Regex;
+ /// # use regex::Captures; fn main() {
+ /// let re = Regex::new(r"([^,\s]+),\s+(\S+)").unwrap();
+ /// let result = re.replace("Springsteen, Bruce", |caps: &Captures| {
+ /// format!("{} {}", caps.at(2).unwrap_or(""), caps.at(1).unwrap_or(""))
+ /// });
+ /// assert_eq!(result, "Bruce Springsteen");
+ /// # }
+ /// ```
+ ///
+ /// But this is a bit cumbersome to use all the time. Instead, a simple
+ /// syntax is supported that expands `$name` into the corresponding capture
+ /// group. Here's the last example, but using this expansion technique
+ /// with named capture groups:
+ ///
+ /// ```rust
+ /// # extern crate regex; use regex::Regex;
+ /// # fn main() {
+ /// let re = Regex::new(r"(?P<last>[^,\s]+),\s+(?P<first>\S+)").unwrap();
+ /// let result = re.replace("Springsteen, Bruce", "$first $last");
+ /// assert_eq!(result, "Bruce Springsteen");
+ /// # }
+ /// ```
+ ///
+ /// Note that using `$2` instead of `$first` or `$1` instead of `$last`
+ /// would produce the same result. To write a literal `$` use `$$`.
+ ///
+ /// Finally, sometimes you just want to replace a literal string with no
+ /// submatch expansion. This can be done by wrapping a string with
+ /// `NoExpand`:
+ ///
+ /// ```rust
+ /// # extern crate regex; use regex::Regex;
+ /// # fn main() {
+ /// use regex::NoExpand;
+ ///
+ /// let re = Regex::new(r"(?P<last>[^,\s]+),\s+(\S+)").unwrap();
+ /// let result = re.replace("Springsteen, Bruce", NoExpand("$2 $last"));
+ /// assert_eq!(result, "$2 $last");
+ /// # }
+ /// ```
+ pubfnreplace<R: Replacer>(&self, text: &str, rep: R) ->String {
+ self.replacen(text, 1, rep)
+ }
+
+ /// Replaces all non-overlapping matches in `text` with the
+ /// replacement provided. This is the same as calling `replacen` with
+ /// `limit` set to `0`.
+ ///
+ /// See the documentation for `replace` for details on how to access
+ /// submatches in the replacement string.
+ pubfnreplace_all<R: Replacer>(&self, text: &str, rep: R) ->String {
+ self.replacen(text, 0, rep)
+ }
+
+ /// Replaces at most `limit` non-overlapping matches in `text` with the
+ /// replacement provided. If `limit` is 0, then all non-overlapping matches
+ /// are replaced.
+ ///
+ /// See the documentation for `replace` for details on how to access
+ /// submatches in the replacement string.
+ pubfnreplacen<R: Replacer>
+ (&self, text: &str, limit: usize, mutrep: R) ->String {
+ letmutnew=String::with_capacity(text.len());
+ letmutlast_match=0;
+
+ ifrep.no_expand().is_some() {
+ // borrow checker pains. `rep` is borrowed mutably in the `else`
+ // branch below.
+ letrep=rep.no_expand().unwrap();
+ for (i, (s, e)) inself.find_iter(text).enumerate() {
+ iflimit>0&&i>=limit {
+ break
+ }
+ new.push_str(&text[last_match..s]);
+ new.push_str(&rep);
+ last_match=e;
+ }
+ } else {
+ for (i, cap) inself.captures_iter(text).enumerate() {
+ iflimit>0&&i>=limit {
+ break
+ }
+ // unwrap on 0 is OK because captures only reports matches
+ let (s, e) =cap.pos(0).unwrap();
+ new.push_str(&text[last_match..s]);
+ new.push_str(&rep.reg_replace(&cap));
+ last_match=e;
+ }
+ }
+ new.push_str(&text[last_match..]);
+ new
+ }
+
+ /// Returns the original string of this regex.
+ pubfnas_str<'a>(&'aself) ->&'astr {
+ match*self {
+ Regex::Dynamic(Program { reforiginal, .. }) =>original,
+ Regex::Native(ExNative { reforiginal, .. }) =>original,
+ }
+ }
+
+ #[doc(hidden)]
+ pubfnnames_iter<'a>(&'aself) ->NamesIter<'a> {
+ match*self {
+ Regex::Native(refn) =>NamesIter::Native(n.names.iter()),
+ Regex::Dynamic(refd) =>NamesIter::Dynamic(d.cap_names.iter())
+ }
+ }
+
+ fnnames_len(&self) ->usize {
+ match*self {
+ Regex::Native(refn) =>n.names.len(),
+ Regex::Dynamic(refd) =>d.cap_names.len()
+ }
+ }
+
+ fnalloc_captures(&self) ->Vec<Option<usize>> {
+ match*self {
+ Regex::Native(refn) =>vec![None; 2*n.names.len()],
+ Regex::Dynamic(refd) =>d.alloc_captures(),
+ }
+ }
+}
+
+pubenumNamesIter<'a> {
+ Native(::std::slice::Iter<'a, Option<&'staticstr>>),
+ Dynamic(::std::slice::Iter<'a, Option<String>>)
+}
+
+impl<'a>IteratorforNamesIter<'a> {
+ typeItem=Option<String>;
+
+ fnnext(&mutself) ->Option<Option<String>> {
+ match*self {
+ NamesIter::Native(refmuti) =>
+ i.next().map(|x|x.map(|s|s.to_owned())),
+ NamesIter::Dynamic(refmuti) =>
+ i.next().map(|x|x.as_ref().map(|s|s.to_owned())),
+ }
+ }
+}
+
+/// NoExpand indicates literal string replacement.
+///
+/// It can be used with `replace` and `replace_all` to do a literal
+/// string replacement without expanding `$name` to their corresponding
+/// capture groups.
+///
+/// `'r` is the lifetime of the literal text.
+pubstructNoExpand<'t>(pub&'tstr);
+
+/// Replacer describes types that can be used to replace matches in a string.
+pubtraitReplacer {
+ /// Returns a possibly owned string that is used to replace the match
+ /// corresponding to the `caps` capture group.
+ ///
+ /// The `'a` lifetime refers to the lifetime of a borrowed string when
+ /// a new owned string isn't needed (e.g., for `NoExpand`).
+ fnreg_replace<'a>(&'amutself, caps: &Captures) ->Cow<'a, str>;
+
+ /// Returns a possibly owned string that never needs expansion.
+ fnno_expand<'a>(&'amutself) ->Option<Cow<'a, str>> { None }
+}
+
+impl<'t>ReplacerforNoExpand<'t> {
+ fnreg_replace<'a>(&'amutself, _: &Captures) ->Cow<'a, str> {
+ self.0.into()
+ }
+
+ fnno_expand<'a>(&'amutself) ->Option<Cow<'a, str>> {
+ Some(self.0.into())
+ }
+}
+
+impl<'t>Replacerfor&'tstr {
+ fnreg_replace<'a>(&'amutself, caps: &Captures) ->Cow<'a, str> {
+ caps.expand(*self).into()
+ }
+
+ fnno_expand<'a>(&'amutself) ->Option<Cow<'a, str>> {
+ letre=Regex::new(REPLACE_EXPAND).unwrap();
+ if!re.is_match(self) {
+ Some((*self).into())
+ } else {
+ None
+ }
+ }
+}
+
+impl<F>ReplacerforFwhereF: FnMut(&Captures) ->String {
+ fnreg_replace<'a>(&'amutself, caps: &Captures) ->Cow<'a, str> {
+ (*self)(caps).into()
+ }
+}
+
+/// Yields all substrings delimited by a regular expression match.
+///
+/// `'r` is the lifetime of the compiled expression and `'t` is the lifetime
+/// of the string being split.
+pubstructRegexSplits<'r, 't> {
+ finder: FindMatches<'r, 't>,
+ last: usize,
+}
+
+impl<'r, 't>IteratorforRegexSplits<'r, 't> {
+ typeItem=&'tstr;
+
+ fnnext(&mutself) ->Option<&'tstr> {
+ lettext=self.finder.search;
+ matchself.finder.next() {
+ None=> {
+ ifself.last>=text.len() {
+ None
+ } else {
+ lets=&text[self.last..];
+ self.last=text.len();
+ Some(s)
+ }
+ }
+ Some((s, e)) => {
+ letmatched=&text[self.last..s];
+ self.last=e;
+ Some(matched)
+ }
+ }
+ }
+}
+
+/// Yields at most `N` substrings delimited by a regular expression match.
+///
+/// The last substring will be whatever remains after splitting.
+///
+/// `'r` is the lifetime of the compiled expression and `'t` is the lifetime
+/// of the string being split.
+pubstructRegexSplitsN<'r, 't> {
+ splits: RegexSplits<'r, 't>,
+ cur: usize,
+ limit: usize,
+}
+
+impl<'r, 't>IteratorforRegexSplitsN<'r, 't> {
+ typeItem=&'tstr;
+
+ fnnext(&mutself) ->Option<&'tstr> {
+ lettext=self.splits.finder.search;
+ ifself.cur>=self.limit {
+ None
+ } else {
+ self.cur+=1;
+ ifself.cur>=self.limit {
+ Some(&text[self.splits.last..])
+ } else {
+ self.splits.next()
+ }
+ }
+ }
+}
+
+/// Captures represents a group of captured strings for a single match.
+///
+/// The 0th capture always corresponds to the entire match. Each subsequent
+/// index corresponds to the next capture group in the regex.
+/// If a capture group is named, then the matched string is *also* available
+/// via the `name` method. (Note that the 0th capture is always unnamed and so
+/// must be accessed with the `at` method.)
+///
+/// Positions returned from a capture group are always byte indices.
+///
+/// `'t` is the lifetime of the matched text.
+pubstructCaptures<'t> {
+ text: &'tstr,
+ locs: Vec<Option<usize>>,
+ named: Option<HashMap<String, usize>>,
+}
+
+impl<'t>Captures<'t> {
+ fnnew(
+ re: &Regex,
+ search: &'tstr,
+ locs: Vec<Option<usize>>,
+ ) ->Captures<'t> {
+ letnamed=
+ ifre.names_len() ==0 {
+ None
+ } else {
+ letmutnamed=HashMap::new();
+ for (i, name) inre.names_iter().enumerate() {
+ ifletSome(name) =name {
+ named.insert(name, i);
+ }
+ }
+ Some(named)
+ };
+ Captures {
+ text: search,
+ locs: locs,
+ named: named,
+ }
+ }
+
+ /// Returns the start and end positions of the Nth capture group.
+ /// Returns `None` if `i` is not a valid capture group or if the capture
+ /// group did not match anything.
+ /// The positions returned are *always* byte indices with respect to the
+ /// original string matched.
+ pubfnpos(&self, i: usize) ->Option<(usize, usize)> {
+ let (s, e) = (i*2, i*2+1);
+ ife>=self.locs.len() ||self.locs[s].is_none() {
+ // VM guarantees that each pair of locations are both Some or None.
+ returnNone
+ }
+ Some((self.locs[s].unwrap(), self.locs[e].unwrap()))
+ }
+
+ /// Returns the matched string for the capture group `i`. If `i` isn't
+ /// a valid capture group or didn't match anything, then `None` is
+ /// returned.
+ pubfnat(&self, i: usize) ->Option<&'tstr> {
+ matchself.pos(i) {
+ None=>None,
+ Some((s, e)) =>Some(&self.text[s..e])
+ }
+ }
+
+ /// Returns the matched string for the capture group named `name`. If
+ /// `name` isn't a valid capture group or didn't match anything, then
+ /// `None` is returned.
+ pubfnname(&self, name: &str) ->Option<&'tstr> {
+ matchself.named {
+ None=>None,
+ Some(refh) => {
+ matchh.get(name) {
+ None=>None,
+ Some(i) =>self.at(*i),
+ }
+ }
+ }
+ }
+
+ /// Creates an iterator of all the capture groups in order of appearance
+ /// in the regular expression.
+ pubfniter(&'tself) ->SubCaptures<'t> {
+ SubCaptures { idx: 0, caps: self, }
+ }
+
+ /// Creates an iterator of all the capture group positions in order of
+ /// appearance in the regular expression. Positions are byte indices
+ /// in terms of the original string matched.
+ pubfniter_pos(&'tself) ->SubCapturesPos<'t> {
+ SubCapturesPos { idx: 0, caps: self, }
+ }
+
+ /// Creates an iterator of all named groups as an tuple with the group
+ /// name and the value. The iterator returns these values in arbitrary
+ /// order.
+ pubfniter_named(&'tself) ->SubCapturesNamed<'t> {
+ SubCapturesNamed { caps: self, inner: self.named.as_ref().map(|n|n.iter()) }
+ }
+
+ /// Expands all instances of `$name` in `text` to the corresponding capture
+ /// group `name`.
+ ///
+ /// `name` may be an integer corresponding to the index of the
+ /// capture group (counted by order of opening parenthesis where `0` is the
+ /// entire match) or it can be a name (consisting of letters, digits or
+ /// underscores) corresponding to a named capture group.
+ ///
+ /// If `name` isn't a valid capture group (whether the name doesn't exist or
+ /// isn't a valid index), then it is replaced with the empty string.
+ ///
+ /// To write a literal `$` use `$$`.
+ pubfnexpand(&self, text: &str) ->String {
+ // How evil can you get?
+ letre=Regex::new(REPLACE_EXPAND).unwrap();
+ lettext=re.replace_all(text, |refs: &Captures|->String {
+ letbefore=refs.name("before").unwrap_or("");
+ letname=refs.name("name").unwrap_or("");
+ format!("{}{}", before, matchname.parse::<usize>() {
+ Err(_) =>self.name(name).unwrap_or("").to_string(),
+ Ok(i) =>self.at(i).unwrap_or("").to_string(),
+ })
+ });
+ letre=Regex::new(r"\$\$").unwrap();
+ re.replace_all(&text, NoExpand("$"))
+ }
+
+ /// Returns the number of captured groups.
+ #[inline]
+ pubfnlen(&self) ->usize { self.locs.len() /2 }
+
+ /// Returns true if and only if there are no captured groups.
+ #[inline]
+ pubfnis_empty(&self) ->bool { self.len() ==0 }
+}
+
+/// An iterator over capture groups for a particular match of a regular
+/// expression.
+///
+/// `'t` is the lifetime of the matched text.
+pubstructSubCaptures<'t> {
+ idx: usize,
+ caps: &'tCaptures<'t>,
+}
+
+impl<'t>IteratorforSubCaptures<'t> {
+ typeItem=Option<&'tstr>;
+
+ fnnext(&mutself) ->Option<Option<&'tstr>> {
+ ifself.idx<self.caps.len() {
+ self.idx+=1;
+ Some(self.caps.at(self.idx-1))
+ } else {
+ None
+ }
+ }
+}
+
+/// An iterator over capture group positions for a particular match of a
+/// regular expression.
+///
+/// Positions are byte indices in terms of the original string matched.
+///
+/// `'t` is the lifetime of the matched text.
+pubstructSubCapturesPos<'t> {
+ idx: usize,
+ caps: &'tCaptures<'t>,
+}
+
+impl<'t>IteratorforSubCapturesPos<'t> {
+ typeItem=Option<(usize, usize)>;
+
+ fnnext(&mutself) ->Option<Option<(usize, usize)>> {
+ ifself.idx<self.caps.len() {
+ self.idx+=1;
+ Some(self.caps.pos(self.idx-1))
+ } else {
+ None
+ }
+ }
+}
+
+/// An Iterator over named capture groups as a tuple with the group
+/// name and the value.
+///
+/// `'t` is the lifetime of the matched text.
+pubstructSubCapturesNamed<'t>{
+ caps: &'tCaptures<'t>,
+ inner: Option<Iter<'t, String, usize>>,
+}
+
+impl<'t>IteratorforSubCapturesNamed<'t> {
+ typeItem= (&'tstr, Option<&'tstr>);
+
+ fnnext(&mutself) ->Option<(&'tstr, Option<&'tstr>)> {
+ matchself.inner.as_mut().map(|it|it.next()).unwrap_or(None) {
+ Some((name, pos)) =>Some((name, self.caps.at(*pos))),
+ None=>None
+ }
+ }
+}
+
+/// An iterator that yields all non-overlapping capture groups matching a
+/// particular regular expression.
+///
+/// The iterator stops when no more matches can be found.
+///
+/// `'r` is the lifetime of the compiled expression and `'t` is the lifetime
+/// of the matched string.
+pubstructFindCaptures<'r, 't> {
+ re: &'rRegex,
+ search: &'tstr,
+ last_match: Option<usize>,
+ last_end: usize,
+}
+
+impl<'r, 't>IteratorforFindCaptures<'r, 't> {
+ typeItem=Captures<'t>;
+
+ fnnext(&mutself) ->Option<Captures<'t>> {
+ ifself.last_end>self.search.len() {
+ returnNone
+ }
+
+ letmutcaps=self.re.alloc_captures();
+ if!exec(self.re, &mutcaps, self.search, self.last_end) {
+ returnNone
+ }
+ let (s, e) = (caps[0].unwrap(), caps[1].unwrap());
+
+ // Don't accept empty matches immediately following a match.
+ // i.e., no infinite loops please.
+ ife==s&&Some(self.last_end) ==self.last_match {
+ ifself.last_end>=self.search.len() {
+ returnNone;
+ }
+ self.last_end+=self.search[self.last_end..].chars()
+ .next().unwrap().len_utf8();
+ returnself.next()
+ }
+ self.last_end=e;
+ self.last_match=Some(self.last_end);
+ Some(Captures::new(self.re, self.search, caps))
+ }
+}
+
+/// An iterator over all non-overlapping matches for a particular string.
+///
+/// The iterator yields a tuple of integers corresponding to the start and end
+/// of the match. The indices are byte offsets. The iterator stops when no more
+/// matches can be found.
+///
+/// `'r` is the lifetime of the compiled expression and `'t` is the lifetime
+/// of the matched string.
+pubstructFindMatches<'r, 't> {
+ re: &'rRegex,
+ search: &'tstr,
+ last_match: Option<usize>,
+ last_end: usize,
+}
+
+impl<'r, 't>IteratorforFindMatches<'r, 't> {
+ typeItem= (usize, usize);
+
+ fnnext(&mutself) ->Option<(usize, usize)> {
+ ifself.last_end>self.search.len() {
+ returnNone
+ }
+
+ letmutcaps= [None, None];
+ if!exec(self.re, &mutcaps, self.search, self.last_end) {
+ returnNone;
+ }
+ let (s, e) = (caps[0].unwrap(), caps[1].unwrap());
+
+ // Don't accept empty matches immediately following a match.
+ // i.e., no infinite loops please.
+ ife==s&&Some(self.last_end) ==self.last_match {
+ ifself.last_end>=self.search.len() {
+ returnNone;
+ }
+ self.last_end+=self.search[self.last_end..].chars()
+ .next().unwrap().len_utf8();
+ returnself.next()
+ }
+ self.last_end=e;
+ self.last_match=Some(self.last_end);
+ Some((s, e))
+ }
+}
+
+#[cfg(feature="pattern")]
+pubstructRegexSearcher<'r, 't> {
+ it: FindMatches<'r, 't>,
+ last_step_end: usize,
+ next_match: Option<(usize, usize)>,
+}
+
+#[cfg(feature="pattern")]
+impl<'r, 't>Pattern<'t>for&'rRegex {
+ typeSearcher=RegexSearcher<'r, 't>;
+
+ fninto_searcher(self, haystack: &'tstr) ->RegexSearcher<'r, 't> {
+ RegexSearcher {
+ it: self.find_iter(haystack),
+ last_step_end: 0,
+ next_match: None,
+ }
+ }
+}
+
+#[cfg(feature="pattern")]
+unsafeimpl<'r, 't>Searcher<'t>forRegexSearcher<'r, 't> {
+ #[inline]
+ fnhaystack(&self) ->&'tstr {
+ self.it.search
+ }
+
+ #[inline]
+ fnnext(&mutself) ->SearchStep {
+ ifletSome((s, e)) =self.next_match {
+ self.next_match=None;
+ self.last_step_end=e;
+ returnSearchStep::Match(s, e);
+ }
+ matchself.it.next() {
+ None=> {
+ ifself.last_step_end<self.haystack().len() {
+ letlast=self.last_step_end;
+ self.last_step_end=self.haystack().len();
+ SearchStep::Reject(last, self.haystack().len())
+ } else {
+ SearchStep::Done
+ }
+ }
+ Some((s, e)) => {
+ ifs==self.last_step_end {
+ self.last_step_end=e;
+ SearchStep::Match(s, e)
+ } else {
+ self.next_match=Some((s, e));
+ letlast=self.last_step_end;
+ self.last_step_end=s;
+ SearchStep::Reject(last, s)
+ }
+ }
+ }
+ }
+}
+
+fnexec(re: &Regex, caps: &mutCaptureIdxs, text: &str, start: usize) ->bool {
+ match*re {
+ Regex::Native(ExNative { refprog, .. }) => (*prog)(caps, text, start),
+ Regex::Dynamic(refprog) =>prog.exec(caps, text, start),
+ }
+}
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*!
+This crate provides a regular expression parser and an abstract syntax for
+regular expressions. The abstract syntax is defined by the `Expr` type. The
+concrete syntax is enumerated in the
+[`regex`](../regex/index.html#syntax)
+crate documentation.
+
+Note that since this crate is first and foremost an implementation detail for
+the `regex` crate, it may experience more frequent breaking changes. It is
+exposed as a separate crate so that others may use it to do analysis on regular
+expressions or even build their own matching engine.
+
+# Example: parsing an expression
+
+Parsing a regular expression can be done with the `Expr::parse` function.
+
+```rust
+use regex_syntax::Expr;
+
+assert_eq!(Expr::parse(r"ab|yz").unwrap(), Expr::Alternate(vec![
+ Expr::Literal { chars: vec!['a', 'b'], casei: false },
+ Expr::Literal { chars: vec!['y', 'z'], casei: false },
+]));
+```
+
+# Example: inspecting an error
+
+The parser in this crate provides very detailed error values. For example,
+if an invalid character class range is given:
+
+```rust
+use regex_syntax::{Expr, ErrorKind};
+
+let err = Expr::parse(r"[z-a]").unwrap_err();
+assert_eq!(err.position(), 4);
+assert_eq!(err.kind(), &ErrorKind::InvalidClassRange {
+ start: 'z',
+ end: 'a',
+});
+```
+
+Or unbalanced parentheses:
+
+```rust
+use regex_syntax::{Expr, ErrorKind};
+
+let err = Expr::parse(r"ab(cd").unwrap_err();
+assert_eq!(err.position(), 2);
+assert_eq!(err.kind(), &ErrorKind::UnclosedParen);
+```
+*/
+
+#![deny(missing_docs)]
+
+#[cfg(test)]externcratequickcheck;
+#[cfg(test)]externcraterand;
+
+modparser;
+modunicode;
+
+usestd::char;
+usestd::cmp::{Ordering, max, min};
+usestd::fmt;
+usestd::iter::IntoIterator;
+usestd::ops::Deref;
+usestd::slice;
+usestd::vec;
+
+useunicode::case_folding;
+
+useself::Expr::*;
+useself::Repeater::*;
+
+pubuseparser::is_punct;
+
+/// A regular expression abstract syntax tree.
+///
+/// An `Expr` represents the abstract syntax of a regular expression.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pubenumExpr {
+ /// An empty regex (which never matches any text).
+ Empty,
+ /// A sequence of one or more literal characters to be matched.
+ Literal {
+ /// The characters.
+ chars: Vec<char>,
+ /// Whether to match case insensitively.
+ casei: bool,
+ },
+ /// Match any character, excluding new line.
+ AnyChar,
+ /// Match any character.
+ AnyCharNoNL,
+ /// A character class.
+ Class(CharClass),
+ /// Match the start of a line or beginning of input.
+ StartLine,
+ /// Match the end of a line or end of input.
+ EndLine,
+ /// Match the beginning of input.
+ StartText,
+ /// Match the end of input.
+ EndText,
+ /// Match a word boundary (word character on one side and a non-word
+ /// character on the other).
+ WordBoundary,
+ /// Match a position that is not a word boundary (word or non-word
+ /// characters on both sides).
+ NotWordBoundary,
+ /// A group, possibly non-capturing.
+ Group {
+ /// The expression inside the group.
+ e: Box<Expr>,
+ /// The capture index (starting at `1`) only for capturing groups.
+ i: Option<usize>,
+ /// The capture name, only for capturing named groups.
+ name: Option<String>,
+ },
+ /// A repeat operator (`?`, `*`, `+` or `{m,n}`).
+ Repeat {
+ /// The expression to be repeated. Limited to literals, `.`, classes
+ /// or grouped expressions.
+ e: Box<Expr>,
+ /// The type of repeat operator used.
+ r: Repeater,
+ /// Whether the repeat is greedy (match the most) or not (match the
+ /// least).
+ greedy: bool,
+ },
+ /// A concatenation of expressions. Must be matched one after the other.
+ ///
+ /// N.B. A concat expression can only appear at the top-level or
+ /// immediately inside a group expression.
+ Concat(Vec<Expr>),
+ /// An alternation of expressions. Only one must match.
+ ///
+ /// N.B. An alternate expression can only appear at the top-level or
+ /// immediately inside a group expression.
+ Alternate(Vec<Expr>),
+}
+
+typeCaptureIndex=Option<usize>;
+
+typeCaptureName=Option<String>;
+
+/// The type of a repeat operator expression.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pubenumRepeater {
+ /// Match zero or one (`?`).
+ ZeroOrOne,
+ /// Match zero or more (`*`).
+ ZeroOrMore,
+ /// Match one or more (`+`).
+ OneOrMore,
+ /// Match for at least `min` and at most `max` (`{m,n}`).
+ ///
+ /// When `max` is `None`, there is no upper bound on the number of matches.
+ Range {
+ /// Lower bound on the number of matches.
+ min: u32,
+ /// Optional upper bound on the number of matches.
+ max: Option<u32>,
+ },
+}
+
+/// A character class.
+///
+/// A character class has a canonical format that the parser guarantees. Its
+/// canonical format is defined by the following invariants:
+///
+/// 1. Given any Unicode scalar value, it is matched by *at most* one character
+/// range in a canonical character class.
+/// 2. Every adjacent character range is separated by at least one Unicode
+/// scalar value.
+/// 3. Given any pair of character ranges `r1` and `r2`, if
+/// `r1.end < r2.start`, then `r1` comes before `r2` in a canonical
+/// character class.
+///
+/// In sum, any `CharClass` produced by this crate's parser is a sorted
+/// sequence of non-overlapping ranges. This makes it possible to test whether
+/// a character is matched by a class with a binary search.
+///
+/// Additionally, a character class may be marked *case insensitive*. If it's
+/// case insensitive, then:
+///
+/// 1. Simple case folding has been applied to all ranges.
+/// 2. Simple case folding must be applied to a character before testing
+/// whether it matches the character class.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pubstructCharClass {
+ ranges: Vec<ClassRange>,
+ casei: bool,
+}
+
+/// A single inclusive range in a character class.
+///
+/// Since range boundaries are defined by Unicode scalar values, the boundaries
+/// can never be in the open interval `(0xD7FF, 0xE000)`. However, a range may
+/// *cover* codepoints that are not scalar values.
+///
+/// Note that this has a few convenient impls on `PartialEq` and `PartialOrd`
+/// for testing whether a character is contained inside a given range.
+#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
+pubstructClassRange {
+ /// The start character of the range.
+ ///
+ /// This must be less than or equal to `end`.
+ pubstart: char,
+
+ /// The end character of the range.
+ ///
+ /// This must be greater than or equal to `end`.
+ pubend: char,
+}
+
+implExpr {
+ /// Parses a string in a regular expression syntax tree.
+ pubfnparse(s: &str) ->Result<Expr> {
+ parser::Parser::parse(s).map(|e|e.simplify())
+ }
+
+ /// Returns true iff the expression can be repeated by a quantifier.
+ fncan_repeat(&self) ->bool {
+ match*self {
+ Literal{..}
+ |AnyChar
+ |AnyCharNoNL
+ |Class(_)
+ |StartLine|EndLine|StartText|EndText
+ |WordBoundary|NotWordBoundary
+ |Group{..}
+ =>true,
+ _ =>false,
+ }
+ }
+
+ fnsimplify(self) ->Expr {
+ fncombine_literals(es: &mutVec<Expr>, e: Expr) {
+ match (es.pop(), e) {
+ (None, e) =>es.push(e),
+ (Some(Literal { chars: mutchars1, casei: casei1 }),
+ Literal { chars: chars2, casei: casei2 }) => {
+ ifcasei1==casei2 {
+ chars1.extend(chars2);
+ es.push(Literal { chars: chars1, casei: casei1 });
+ } else {
+ es.push(Literal { chars: chars1, casei: casei1 });
+ es.push(Literal { chars: chars2, casei: casei2 });
+ }
+ }
+ (Some(e1), e2) => {
+ es.push(e1);
+ es.push(e2);
+ }
+ }
+ }
+ matchself {
+ Repeat { e, r, greedy } =>Repeat {
+ e: Box::new(e.simplify()),
+ r: r,
+ greedy: greedy,
+ },
+ Group { e, i, name } => {
+ lete=e.simplify();
+ ifi.is_none() &&name.is_none() &&e.can_repeat() {
+ e
+ } else {
+ Group { e: Box::new(e), i: i, name: name }
+ }
+ }
+ Concat(es) => {
+ letmutnew_es=Vec::with_capacity(es.len());
+ foreines {
+ combine_literals(&mutnew_es, e.simplify());
+ }
+ ifnew_es.len() ==1 {
+ new_es.pop().unwrap()
+ } else {
+ Concat(new_es)
+ }
+ }
+ Alternate(es) =>Alternate(es.into_iter()
+ .map(|e|e.simplify())
+ .collect()),
+ e=>e,
+ }
+ }
+}
+
+implDerefforCharClass {
+ typeTarget=Vec<ClassRange>;
+ fnderef(&self) ->&Vec<ClassRange> { &self.ranges }
+}
+
+implIntoIteratorforCharClass {
+ typeItem=ClassRange;
+ typeIntoIter=vec::IntoIter<ClassRange>;
+ fninto_iter(self) ->vec::IntoIter<ClassRange> { self.ranges.into_iter() }
+}
+
+impl<'a>IntoIteratorfor&'aCharClass {
+ typeItem=&'aClassRange;
+ typeIntoIter=slice::Iter<'a, ClassRange>;
+ fninto_iter(self) ->slice::Iter<'a, ClassRange> { self.iter() }
+}
+
+implCharClass {
+ /// Create a new class from an existing set of ranges.
+ fnnew(ranges: Vec<ClassRange>) ->CharClass {
+ CharClass { ranges: ranges, casei: false }
+ }
+
+ /// Create an empty class.
+ fnempty() ->CharClass {
+ CharClass::new(Vec::new())
+ }
+
+ /// Returns true if `c` is matched by this character class.
+ ///
+ /// If this character class is case insensitive, then simple case folding
+ /// is applied to `c` before checking for a match.
+ pubfnmatches(&self, mutc: char) ->bool {
+ ifself.is_case_insensitive() {
+ c=simple_case_fold(c)
+ }
+ self.binary_search_by(|range|c.partial_cmp(range).unwrap()).is_ok()
+ }
+
+ /// Returns true if this character class should be matched case
+ /// insensitively.
+ ///
+ /// When `true`, simple case folding has already been applied to the
+ /// class.
+ pubfnis_case_insensitive(&self) ->bool {
+ self.casei
+ }
+
+ /// Create a new empty class from this one.
+ ///
+ /// Namely, its capacity and case insensitive setting will be the same.
+ fnto_empty(&self) ->CharClass {
+ CharClass { ranges: Vec::with_capacity(self.len()), casei: self.casei }
+ }
+
+ /// Merge two classes and canonicalize them.
+ #[cfg(test)]
+ fnmerge(mutself, other: CharClass) ->CharClass {
+ self.ranges.extend(other);
+ self.canonicalize()
+ }
+
+ /// Canonicalze any sequence of ranges.
+ ///
+ /// This is responsible for enforcing the canonical format invariants
+ /// as described on the docs for the `CharClass` type.
+ fncanonicalize(mutself) ->CharClass {
+ // TODO: Save some cycles here by checking if already canonicalized.
+ self.ranges.sort();
+ letmutordered=self.to_empty(); // TODO: Do this in place?
+ forcandidateinself {
+ // If the candidate overlaps with an existing range, then it must
+ // be the most recent range added because we process the candidates
+ // in order.
+ ifletSome(or) =ordered.ranges.last_mut() {
+ ifor.overlapping(candidate) {
+ *or=or.merge(candidate);
+ continue;
+ }
+ }
+ ordered.ranges.push(candidate);
+ }
+ ordered
+ }
+
+ /// Negates the character class.
+ ///
+ /// For all `c` where `c` is a Unicode scalar value, `c` matches `self`
+ /// if and only if `c` does not match `self.negate()`.
+ ///
+ /// Note that this cannot be called on a character class that has had
+ /// case folding applied to it. (Because case folding turns on a flag
+ /// and doesn't store every possible matching character. Therefore,
+ /// its negation is tricky to get right. Turns out, we don't need it
+ /// anyway!)
+ fnnegate(mutself) ->CharClass {
+ fnrange(s: char, e: char) ->ClassRange { ClassRange::new(s, e) }
+
+ // Never allow negating of a class that has been case folded!
+ assert!(!self.casei);
+
+ ifself.is_empty() { returnself; }
+ self=self.canonicalize();
+ letmutinv=self.to_empty();
+ ifself[0].start>'\x00' {
+ inv.ranges.push(range('\x00', dec_char(self[0].start)));
+ }
+ forwininself.windows(2) {
+ inv.ranges.push(range(inc_char(win[0].end),
+ dec_char(win[1].start)));
+ }
+ ifself[self.len() -1].end<char::MAX {
+ inv.ranges.push(range(inc_char(self[self.len() -1].end),
+ char::MAX));
+ }
+ inv
+ }
+
+ /// Apply case folding to this character class.
+ ///
+ /// One a class had been case folded, it cannot be negated.
+ fncase_fold(self) ->CharClass {
+ letmutfolded=self.to_empty();
+ folded.casei=true;
+ forrinself {
+ // Applying case folding to a range is expensive because *every*
+ // character needed to be examined. Thus, we avoid that drudgery
+ // if no character in the current range is in our case folding
+ // table.
+ ifr.needs_case_folding() {
+ folded.ranges.extend(r.case_fold());
+ } else {
+ folded.ranges.push(r);
+ }
+ }
+ folded.canonicalize()
+ }
+}
+
+implClassRange {
+ /// Create a new class range.
+ ///
+ /// If `end < start`, then the two values are swapped so that
+ /// the invariant `start <= end` is preserved.
+ fnnew(start: char, end: char) ->ClassRange {
+ ifstart<=end {
+ ClassRange { start: start, end: end }
+ } else {
+ ClassRange { start: end, end: start }
+ }
+ }
+
+ /// Create a range of one character.
+ fnone(c: char) ->ClassRange {
+ ClassRange { start: c, end: c }
+ }
+
+ /// Returns true if and only if the two ranges are overlapping. Note that
+ /// since ranges are inclusive, `a-c` and `d-f` are overlapping!
+ fnoverlapping(self, other: ClassRange) ->bool {
+ max(self.start, other.start) <=inc_char(min(self.end, other.end))
+ }
+
+ /// Creates a new range representing the union of `self` and `other.
+ fnmerge(self, other: ClassRange) ->ClassRange {
+ ClassRange {
+ start: min(self.start, other.start),
+ end: max(self.end, other.end),
+ }
+ }
+
+ /// Returns true if and only if this range contains a character that is
+ /// in the case folding table.
+ fnneeds_case_folding(self) ->bool {
+ case_folding::C_plus_S_table
+ .binary_search_by(|&(c, _)|self.partial_cmp(&c).unwrap()).is_ok()
+ }
+
+ /// Apply case folding to this range.
+ ///
+ /// Since case folding might add characters such that the range is no
+ /// longer contiguous, this returns multiple class ranges. They are in
+ /// canonical order.
+ fncase_fold(self) ->Vec<ClassRange> {
+ let (s, e) = (self.startasu32, self.endasu32+1);
+ letmutstart=simple_case_fold(self.start);
+ letmutend=start;
+ letmutnext_case_fold=self.start;
+ letmutranges=Vec::with_capacity(100);
+ formutcin (s+1..e).filter_map(char::from_u32) {
+ ifc>=next_case_fold {
+ c=matchsimple_case_fold_result(c) {
+ Ok(i) =>case_folding::C_plus_S_table[i].1,
+ Err(i) => {
+ ifi<case_folding::C_plus_S_table.len() {
+ next_case_fold=case_folding::C_plus_S_table[i].0;
+ } else {
+ next_case_fold='\u{10FFFF}'
+ }
+ c
+ }
+ };
+ }
+ ifc!=inc_char(end) {
+ ranges.push(ClassRange::new(start, end));
+ start=c;
+ }
+ end=c;
+ }
+ ranges.push(ClassRange::new(start, end));
+ ranges
+ }
+}
+
+implPartialEq<char>forClassRange {
+ #[inline]
+ fneq(&self, other: &char) ->bool {
+ self.start<=*other&&*other<=self.end
+ }
+}
+
+implPartialEq<ClassRange>forchar {
+ #[inline]
+ fneq(&self, other: &ClassRange) ->bool {
+ other.eq(self)
+ }
+}
+
+implPartialOrd<char>forClassRange {
+ #[inline]
+ fnpartial_cmp(&self, other: &char) ->Option<Ordering> {
+ Some(ifself==other {
+ Ordering::Equal
+ } elseif*other>self.end {
+ Ordering::Greater
+ } else {
+ Ordering::Less
+ })
+ }
+}
+
+implPartialOrd<ClassRange>forchar {
+ #[inline]
+ fnpartial_cmp(&self, other: &ClassRange) ->Option<Ordering> {
+ other.partial_cmp(self).map(|o|o.reverse())
+ }
+}
+
+/// This implementation of `Display` will write a regular expression from the
+/// syntax tree. It does not write the original string parsed.
+implfmt::DisplayforExpr {
+ fnfmt(&self, f: &mutfmt::Formatter) ->fmt::Result {
+ match*self {
+ Empty=>write!(f, ""),
+ Literal { refchars, casei } => {
+ ifcasei { try!(write!(f, "(?i:")); }
+ for&cinchars {
+ try!(write!(f, "{}", quote_char(c)));
+ }
+ ifcasei { try!(write!(f, ")")); }
+ Ok(())
+ }
+ AnyChar=>write!(f, "(?s:.)"),
+ AnyCharNoNL=>write!(f, "."),
+ Class(refcls) =>write!(f, "{}", cls),
+ StartLine=>write!(f, "(?m:^)"),
+ EndLine=>write!(f, "(?m:$)"),
+ StartText=>write!(f, r"^"),
+ EndText=>write!(f, r"$"),
+ WordBoundary=>write!(f, r"\b"),
+ NotWordBoundary=>write!(f, r"\B"),
+ Group { refe, i: None, name: None } =>write!(f, "(?:{})", e),
+ Group { refe, name: None, .. } =>write!(f, "({})", e),
+ Group { refe, name: Some(refn), .. } => {
+ write!(f, "(?P<{}>{})", n, e)
+ }
+ Repeat { refe, r, greedy } => {
+ match&**e {
+ &Literal { refchars, .. } ifchars.len() >1=> {
+ try!(write!(f, "(?:{}){}", e, r))
+ }
+ _ =>try!(write!(f, "{}{}", e, r)),
+ }
+ if!greedy { try!(write!(f, "?")); }
+ Ok(())
+ }
+ Concat(refes) => {
+ foreines {
+ try!(write!(f, "{}", e));
+ }
+ Ok(())
+ }
+ Alternate(refes) => {
+ for (i, e) ines.iter().enumerate() {
+ ifi>0 { try!(write!(f, "|")); }
+ try!(write!(f, "{}", e));
+ }
+ Ok(())
+ }
+ }
+ }
+}
+
+implfmt::DisplayforRepeater {
+ fnfmt(&self, f: &mutfmt::Formatter) ->fmt::Result {
+ match*self {
+ ZeroOrOne=>write!(f, "?"),
+ ZeroOrMore=>write!(f, "*"),
+ OneOrMore=>write!(f, "+"),
+ Range { min: s, max: None } =>write!(f, "{{{},}}", s),
+ Range { min: s, max: Some(e) } ifs==e=>write!(f, "{{{}}}", s),
+ Range { min: s, max: Some(e) } =>write!(f, "{{{}, {}}}", s, e),
+ }
+ }
+}
+
+implfmt::DisplayforCharClass {
+ fnfmt(&self, f: &mutfmt::Formatter) ->fmt::Result {
+ ifself.casei {
+ try!(write!(f, "(?i:"));
+ }
+ try!(write!(f, "["));
+ forrangeinself.iter() {
+ try!(write!(f, "{}", range));
+ }
+ try!(write!(f, "]"));
+ ifself.casei {
+ try!(write!(f, ")"));
+ }
+ Ok(())
+ }
+}
+
+implfmt::DisplayforClassRange {
+ fnfmt(&self, f: &mutfmt::Formatter) ->fmt::Result {
+ write!(f, "{}-{}", quote_char(self.start), quote_char(self.end))
+ }
+}
+
+/// An alias for computations that can return a `Error`.
+pubtypeResult<T>= ::std::result::Result<T, Error>;
+
+/// A parse error.
+///
+/// This includes details about the specific type of error and a rough
+/// approximation of where it occurred.
+#[derive(Clone, Debug, PartialEq)]
+pubstructError {
+ pos: usize,
+ surround: String,
+ kind: ErrorKind,
+}
+
+/// The specific type of parse error that can occur.
+#[derive(Clone, Debug, PartialEq)]
+pubenumErrorKind {
+ /// A negation symbol is used twice in flag settings.
+ /// e.g., `(?-i-s)`.
+ DoubleFlagNegation,
+ /// The same capture name was used more than once.
+ /// e.g., `(?P<a>.)(?P<a>.)`.
+ DuplicateCaptureName(String),
+ /// An alternate is empty. e.g., `(|a)`.
+ EmptyAlternate,
+ /// A capture group name is empty. e.g., `(?P<>a)`.
+ EmptyCaptureName,
+ /// A negation symbol was not proceded by any flags. e.g., `(?i-)`.
+ EmptyFlagNegation,
+ /// A group is empty. e.g., `()`.
+ EmptyGroup,
+ /// An invalid number was used in a counted repetition. e.g., `a{b}`.
+ InvalidBase10(String),
+ /// An invalid hexadecimal number was used in an escape sequence.
+ /// e.g., `\xAG`.
+ InvalidBase16(String),
+ /// An invalid capture name was used. e.g., `(?P<0a>b)`.
+ InvalidCaptureName(String),
+ /// An invalid class range was givien. Specifically, when the start of the
+ /// range is greater than the end. e.g., `[z-a]`.
+ InvalidClassRange {
+ /// The first character specified in the range.
+ start: char,
+ /// The second character specified in the range.
+ end: char,
+ },
+ /// An escape sequence was used in a character class where it is not
+ /// allowed. e.g., `[a-\pN]` or `[\A]`.
+ InvalidClassEscape(Expr),
+ /// An invalid counted repetition min/max was given. e.g., `a{2,1}`.
+ InvalidRepeatRange {
+ /// The first number specified in the repetition.
+ min: u32,
+ /// The second number specified in the repetition.
+ max: u32,
+ },
+ /// An invalid Unicode scalar value was used in a long hexadecimal
+ /// sequence. e.g., `\x{D800}`.
+ InvalidScalarValue(u32),
+ /// An empty counted repetition operator. e.g., `a{}`.
+ MissingBase10,
+ /// A repetition operator was not applied to an expression. e.g., `*`.
+ RepeaterExpectsExpr,
+ /// A repetition operator was applied to an expression that cannot be
+ /// repeated. e.g., `a+*` or `a|*`.
+ RepeaterUnexpectedExpr(Expr),
+ /// A capture group name that is never closed. e.g., `(?P<a`.
+ UnclosedCaptureName(String),
+ /// An unclosed hexadecimal literal. e.g., `\x{a`.
+ UnclosedHex,
+ /// An unclosed parenthesis. e.g., `(a`.
+ UnclosedParen,
+ /// An unclosed counted repetition operator. e.g., `a{2`.
+ UnclosedRepeat,
+ /// An unclosed named Unicode class. e.g., `\p{Yi`.
+ UnclosedUnicodeName,
+ /// Saw end of regex before class was closed. e.g., `[a`.
+ UnexpectedClassEof,
+ /// Saw end of regex before escape sequence was closed. e.g., `\`.
+ UnexpectedEscapeEof,
+ /// Saw end of regex before flags were closed. e.g., `(?i`.
+ UnexpectedFlagEof,
+ /// Saw end of regex before two hexadecimal digits were seen. e.g., `\xA`.
+ UnexpectedTwoDigitHexEof,
+ /// Unopened parenthesis. e.g., `)`.
+ UnopenedParen,
+ /// Unrecognized escape sequence. e.g., `\q`.
+ UnrecognizedEscape(char),
+ /// Unrecognized flag. e.g., `(?a)`.
+ UnrecognizedFlag(char),
+ /// Unrecognized named Unicode class. e.g., `\p{Foo}`.
+ UnrecognizedUnicodeClass(String),
+ /// Hints that destructuring should not be exhaustive.
+ ///
+ /// This enum may grow additional variants, so this makes sure clients
+ /// don't count on exhaustive matching. (Otherwise, adding a new variant
+ /// could break existing code.)
+ #[doc(hidden)]
+ __Nonexhaustive,
+}
+
+implError {
+ /// Returns an approximate *character* offset at which the error occurred.
+ ///
+ /// The character offset may be equal to the number of characters in the
+ /// string, in which case it should be interpreted as pointing to the end
+ /// of the regex.
+ pubfnposition(&self) ->usize {
+ self.pos
+ }
+
+ /// Returns the type of the regex parse error.
+ pubfnkind(&self) ->&ErrorKind {
+ &self.kind
+ }
+}
+
+implErrorKind {
+ fndescription(&self) ->&str {
+ useErrorKind::*;
+ match*self {
+ DoubleFlagNegation=>"double flag negation",
+ DuplicateCaptureName(_) =>"duplicate capture name",
+ EmptyAlternate=>"empty alternate",
+ EmptyCaptureName=>"empty capture name",
+ EmptyFlagNegation=>"flag negation without any flags",
+ EmptyGroup=>"empty group (e.g., '()')",
+ InvalidBase10(_) =>"invalid base 10 number",
+ InvalidBase16(_) =>"invalid base 16 number",
+ InvalidCaptureName(_) =>"invalid capture name",
+ InvalidClassRange{..} =>"invalid character class range",
+ InvalidClassEscape(_) =>"invalid escape sequence in class",
+ InvalidRepeatRange{..} =>"invalid counted repetition range",
+ InvalidScalarValue(_) =>"invalid Unicode scalar value",
+ MissingBase10=>"missing count in repetition operator",
+ RepeaterExpectsExpr=>"repetition operator missing expression",
+ RepeaterUnexpectedExpr(_) =>"expression cannot be repeated",
+ UnclosedCaptureName(_) =>"unclosed capture group name",
+ UnclosedHex=>"unclosed hexadecimal literal",
+ UnclosedParen=>"unclosed parenthesis",
+ UnclosedRepeat=>"unclosed counted repetition operator",
+ UnclosedUnicodeName=>"unclosed Unicode class literal",
+ UnexpectedClassEof=>"unexpected EOF in character class",
+ UnexpectedEscapeEof=>"unexpected EOF in escape sequence",
+ UnexpectedFlagEof=>"unexpected EOF in flags",
+ UnexpectedTwoDigitHexEof=>"unexpected EOF in hex literal",
+ UnopenedParen=>"unopened parenthesis",
+ UnrecognizedEscape(_) =>"unrecognized escape sequence",
+ UnrecognizedFlag(_) =>"unrecognized flag",
+ UnrecognizedUnicodeClass(_) =>"unrecognized Unicode class name",
+ __Nonexhaustive=>unreachable!(),
+ }
+ }
+}
+
+impl ::std::error::ErrorforError {
+ fndescription(&self) ->&str {
+ self.kind.description()
+ }
+}
+
+implfmt::DisplayforError {
+ fnfmt(&self, f: &mutfmt::Formatter) ->fmt::Result {
+ write!(f, "Error parsing regex near '{}' at character offset {}: {}",
+ self.surround, self.pos, self.kind)
+ }
+}
+
+implfmt::DisplayforErrorKind {
+ fnfmt(&self, f: &mutfmt::Formatter) ->fmt::Result {
+ useErrorKind::*;
+ match*self {
+ DoubleFlagNegation=>
+ write!(f, "Only one negation symbol is allowed in flags."),
+ DuplicateCaptureName(refs) =>
+ write!(f, "Capture name '{}' is used more than once.", s),
+ EmptyAlternate=>
+ write!(f, "Alternations cannot be empty."),
+ EmptyCaptureName=>
+ write!(f, "Capture names cannot be empty."),
+ EmptyFlagNegation=>
+ write!(f, "Flag negation requires setting at least one flag."),
+ EmptyGroup=>
+ write!(f, "Empty regex groups (e.g., '()') are not allowed."),
+ InvalidBase10(refs) =>
+ write!(f, "Not a valid base 10 number: '{}'", s),
+ InvalidBase16(refs) =>
+ write!(f, "Not a valid base 16 number: '{}'", s),
+ InvalidCaptureName(refs) =>
+ write!(f, "Invalid capture name: '{}'. Capture names must \
+ consist of [_a-zA-Z0-9] and are not allowed to \
+ start with with a number.", s),
+ InvalidClassRange { start, end } =>
+ write!(f, "Invalid character class range '{}-{}'. \
+ Character class ranges must start with the smaller \
+ character, but {} > {}", start, end, start, end),
+ InvalidClassEscape(refe) =>
+ write!(f, "Invalid escape sequence in character \
+ class: '{}'.", e),
+ InvalidRepeatRange { min, max } =>
+ write!(f, "Invalid counted repetition range: {{{}, {}}}. \
+ Counted repetition ranges must start with the \
+ minimum, but {} > {}", min, max, min, max),
+ InvalidScalarValue(c) =>
+ write!(f, "Number does not correspond to a Unicode scalar \
+ value: '{}'.", c),
+ MissingBase10=>
+ write!(f, "Missing maximum in counted reptition operator."),
+ RepeaterExpectsExpr=>
+ write!(f, "Missing expression for reptition operator."),
+ RepeaterUnexpectedExpr(refe) =>
+ write!(f, "Invalid application of reptition operator to: \
+ '{}'.", e),
+ UnclosedCaptureName(refs) =>
+ write!(f, "Capture name group for '{}' is not closed. \
+ (Missing a '>'.)", s),
+ UnclosedHex=>
+ write!(f, "Unclosed hexadecimal literal (missing a '}}')."),
+ UnclosedParen=>
+ write!(f, "Unclosed parenthesis."),
+ UnclosedRepeat=>
+ write!(f, "Unclosed counted repetition (missing a '}}')."),
+ UnclosedUnicodeName=>
+ write!(f, "Unclosed Unicode literal (missing a '}}')."),
+ UnexpectedClassEof=>
+ write!(f, "Character class was not closed before the end of \
+ the regex (missing a ']')."),
+ UnexpectedEscapeEof=>
+ write!(f, "Started an escape sequence that didn't finish \
+ before the end of the regex."),
+ UnexpectedFlagEof=>
+ write!(f, "Inline flag settings was not closed before the end \
+ of the regex (missing a ')' or ':')."),
+ UnexpectedTwoDigitHexEof=>
+ write!(f, "Unexpected end of two digit hexadecimal literal."),
+ UnopenedParen=>
+ write!(f, "Unopened parenthesis."),
+ UnrecognizedEscape(c) =>
+ write!(f, "Unrecognized escape sequence: '\\{}'.", c),
+ UnrecognizedFlag(c) =>
+ write!(f, "Unrecognized flag: '{}'. \
+ (Allowed flags: i, s, m, U, x.)", c),
+ UnrecognizedUnicodeClass(refs) =>
+ write!(f, "Unrecognized Unicode class name: '{}'.", s),
+ __Nonexhaustive=>unreachable!(),
+ }
+ }
+}
+
+/// Returns the Unicode *simple* case folding of `c`.
+///
+/// N.B. This is hidden because it really isn't the responsibility of this
+/// crate to do simple case folding. One hopes that either another crate or
+/// the standard library will be able to do this for us. In any case, we still
+/// expose it because it is used inside the various Regex engines.
+#[doc(hidden)]
+pubfnsimple_case_fold(c: char) ->char {
+ simple_case_fold_result(c)
+ .map(|i|case_folding::C_plus_S_table[i].1)
+ .unwrap_or(c)
+}
+
+/// The result of binary search on the simple case folding table.
+///
+/// This level of detail is exposed so that we can do case folding on a
+/// range of characters efficiently.
+fnsimple_case_fold_result(c: char) -> ::std::result::Result<usize, usize> {
+ case_folding::C_plus_S_table.binary_search_by(|&(x, _)|x.cmp(&c))
+}
+
+/// Escapes all regular expression meta characters in `text`.
+///
+/// The string returned may be safely used as a literal in a regular
+/// expression.
+pubfnquote(text: &str) ->String {
+ letmutquoted=String::with_capacity(text.len());
+ forcintext.chars() {
+ ifparser::is_punct(c) {
+ quoted.push('\\');
+ }
+ quoted.push(c);
+ }
+ quoted
+}
+
+fnquote_char(c: char) ->String {
+ letmuts=String::new();
+ ifparser::is_punct(c) {
+ s.push('\\');
+ }
+ s.push(c);
+ s
+}
+
+fninc_char(c: char) ->char {
+ matchc {
+ char::MAX=>char::MAX,
+ '\u{D7FF}'=>'\u{E000}',
+ c=>char::from_u32(casu32+1).unwrap(),
+ }
+}
+
+fndec_char(c: char) ->char {
+ matchc {
+ '\x00'=>'\x00',
+ '\u{E000}'=>'\u{D7FF}',
+ c=>char::from_u32(casu32-1).unwrap(),
+ }
+}
+
+/// Returns true if and only if `c` is a word character.
+#[doc(hidden)]
+pubfnis_word_char(c: char) ->bool {
+ matchc {
+ '_'|'0' ... '9'|'a' ... 'z'|'A' ... 'Z'=>true,
+ _ => ::unicode::regex::PERLW.binary_search_by(|&(start, end)| {
+ ifc>=start&&c<=end {
+ Ordering::Equal
+ } elseifstart>c {
+ Ordering::Greater
+ } else {
+ Ordering::Less
+ }
+ }).is_ok(),
+ }
+}
+
+#[cfg(test)]
+modproperties;
+
+#[cfg(test)]
+modtests {
+ use {CharClass, ClassRange};
+
+ fnclass(ranges: &[(char, char)]) ->CharClass {
+ letranges=ranges.iter().cloned()
+ .map(|(c1, c2)|ClassRange::new(c1, c2)).collect();
+ CharClass::new(ranges)
+ }
+
+ fnclassi(ranges: &[(char, char)]) ->CharClass {
+ letmutcls=class(ranges);
+ cls.casei=true;
+ cls
+ }
+
+ #[test]
+ fnclass_canon_no_change() {
+ letcls=class(&[('a', 'c'), ('x', 'z')]);
+ assert_eq!(cls.clone().canonicalize(), cls);
+ }
+
+ #[test]
+ fnclass_canon_unordered() {
+ letcls=class(&[('x', 'z'), ('a', 'c')]);
+ assert_eq!(cls.canonicalize(), class(&[
+ ('a', 'c'), ('x', 'z'),
+ ]));
+ }
+
+ #[test]
+ fnclass_canon_overlap() {
+ letcls=class(&[('x', 'z'), ('w', 'y')]);
+ assert_eq!(cls.canonicalize(), class(&[
+ ('w', 'z'),
+ ]));
+ }
+
+ #[test]
+ fnclass_canon_overlap_many() {
+ letcls=class(&[
+ ('c', 'f'), ('a', 'g'), ('d', 'j'), ('a', 'c'),
+ ('m', 'p'), ('l', 's'),
+ ]);
+ assert_eq!(cls.clone().canonicalize(), class(&[
+ ('a', 'j'), ('l', 's'),
+ ]));
+ }
+
+ #[test]
+ fnclass_canon_overlap_many_case_fold() {
+ letcls=class(&[
+ ('C', 'F'), ('A', 'G'), ('D', 'J'), ('A', 'C'),
+ ('M', 'P'), ('L', 'S'), ('c', 'f'),
+ ]);
+ assert_eq!(cls.case_fold(), classi(&[
+ ('a', 'j'), ('l', 's'),
+ ]));
+ }
+
+ #[test]
+ fnclass_canon_overlap_boundary() {
+ letcls=class(&[('x', 'z'), ('u', 'w')]);
+ assert_eq!(cls.canonicalize(), class(&[
+ ('u', 'z'),
+ ]));
+ }
+
+ #[test]
+ fnclass_canon_extreme_edge_case() {
+ letcls=class(&[('\x00', '\u{10FFFF}'), ('\x00', '\u{10FFFF}')]);
+ assert_eq!(cls.canonicalize(), class(&[
+ ('\x00', '\u{10FFFF}'),
+ ]));
+ }
+
+ #[test]
+ fnclass_canon_singles() {
+ letcls=class(&[('a', 'a'), ('b', 'b')]);
+ assert_eq!(cls.canonicalize(), class(&[('a', 'b')]));
+ }
+
+ #[test]
+ fnclass_negate_single() {
+ letcls=class(&[('a', 'a')]);
+ assert_eq!(cls.negate(), class(&[
+ ('\x00', '\x60'), ('\x62', '\u{10FFFF}'),
+ ]));
+ }
+
+ #[test]
+ fnclass_negate_singles() {
+ letcls=class(&[('a', 'a'), ('b', 'b')]);
+ assert_eq!(cls.negate(), class(&[
+ ('\x00', '\x60'), ('\x63', '\u{10FFFF}'),
+ ]));
+ }
+
+ #[test]
+ fnclass_negate_multiples() {
+ letcls=class(&[('a', 'c'), ('x', 'z')]);
+ assert_eq!(cls.negate(), class(&[
+ ('\x00', '\x60'), ('\x64', '\x77'), ('\x7b', '\u{10FFFF}'),
+ ]));
+ }
+
+ #[test]
+ fnclass_negate_min_scalar() {
+ letcls=class(&[('\x00', 'a')]);
+ assert_eq!(cls.negate(), class(&[
+ ('\x62', '\u{10FFFF}'),
+ ]));
+ }
+
+ #[test]
+ fnclass_negate_max_scalar() {
+ letcls=class(&[('a', '\u{10FFFF}')]);
+ assert_eq!(cls.negate(), class(&[
+ ('\x00', '\x60'),
+ ]));
+ }
+
+ #[test]
+ fnclass_negate_everything() {
+ letcls=class(&[('\x00', '\u{10FFFF}')]);
+ assert_eq!(cls.negate(), class(&[]));
+ }
+
+ #[test]
+ fnclass_negate_everything_sans_one() {
+ letcls=class(&[
+ ('\x00', '\u{10FFFD}'), ('\u{10FFFF}', '\u{10FFFF}')
+ ]);
+ assert_eq!(cls.negate(), class(&[
+ ('\u{10FFFE}', '\u{10FFFE}'),
+ ]));
+ }
+
+ #[test]
+ fnclass_negate_surrogates_min() {
+ letcls=class(&[('\x00', '\u{D7FF}')]);
+ assert_eq!(cls.negate(), class(&[
+ ('\u{E000}', '\u{10FFFF}'),
+ ]));
+ }
+
+ #[test]
+ fnclass_negate_surrogates_min_edge() {
+ letcls=class(&[('\x00', '\u{D7FE}')]);
+ assert_eq!(cls.negate(), class(&[
+ ('\u{D7FF}', '\u{10FFFF}'),
+ ]));
+ }
+
+ #[test]
+ fnclass_negate_surrogates_max() {
+ letcls=class(&[('\u{E000}', '\u{10FFFF}')]);
+ assert_eq!(cls.negate(), class(&[
+ ('\x00', '\u{D7FF}'),
+ ]));
+ }
+
+ #[test]
+ fnclass_negate_surrogates_max_edge() {
+ letcls=class(&[('\u{E001}', '\u{10FFFF}')]);
+ assert_eq!(cls.negate(), class(&[
+ ('\x00', '\u{E000}'),
+ ]));
+ }
+
+ #[test]
+ fnclass_fold_retain_only_needed() {
+ letcls=class(&[('A', 'Z'), ('a', 'z')]);
+ assert_eq!(cls.case_fold(), classi(&[
+ ('a', 'z'),
+ ]));
+ }
+
+ #[test]
+ fnclass_fold_az() {
+ letcls=class(&[('A', 'Z')]);
+ assert_eq!(cls.case_fold(), classi(&[
+ ('a', 'z'),
+ ]));
+ }
+
+ #[test]
+ fnclass_fold_a_underscore() {
+ letcls=class(&[('A', 'A'), ('_', '_')]);
+ assert_eq!(cls.clone().canonicalize(), class(&[
+ ('A', 'A'), ('_', '_'),
+ ]));
+ assert_eq!(cls.case_fold(), classi(&[
+ ('_', '_'), ('a', 'a'),
+ ]));
+ }
+
+ #[test]
+ fnclass_fold_a_equals() {
+ letcls=class(&[('A', 'A'), ('=', '=')]);
+ assert_eq!(cls.clone().canonicalize(), class(&[
+ ('=', '='), ('A', 'A'),
+ ]));
+ assert_eq!(cls.case_fold(), classi(&[
+ ('=', '='), ('a', 'a'),
+ ]));
+ }
+
+ #[test]
+ fnclass_fold_no_folding_needed() {
+ letcls=class(&[('\x00', '\x10')]);
+ assert_eq!(cls.case_fold(), classi(&[
+ ('\x00', '\x10'),
+ ]));
+ }
+}
+
+
+
+
+
+
+
+
+
Keyboard shortcuts
+
+
?
+
Show this help dialog
+
S
+
Focus the search field
+
⇤
+
Move up in search results
+
⇥
+
Move down in search results
+
⏎
+
Go to active search result
+
+
+
+
Search tricks
+
+ Prefix searches with a type followed by a colon (e.g.
+ fn:) to restrict the search to a given type.
+