diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client.rs | 175 | ||||
-rw-r--r-- | src/command.rs | 523 | ||||
-rw-r--r-- | src/lib.rs | 11 | ||||
-rw-r--r-- | src/message.rs | 218 | ||||
-rw-r--r-- | src/reply.rs | 812 | ||||
-rw-r--r-- | src/text.rs | 196 |
6 files changed, 1044 insertions, 891 deletions
diff --git a/src/client.rs b/src/client.rs index a154fba..690ef30 100644 --- a/src/client.rs +++ b/src/client.rs @@ -11,13 +11,12 @@ use std::sync::{ Arc, RwLock }; use std::mem; use std::cell::UnsafeCell; -use carboxyl::{ Stream, Sink }; - use message::Message; use command::Command; use command::Command::*; use reply::Reply; use event::Event; +use text::*; use ::{ DEBUG, Result, IrscError }; use openssl::ssl::{ Ssl, SslContext, SslMethod, SslStream }; @@ -53,60 +52,14 @@ impl Read for StreamKind { } } -pub trait Client { - fn send_message(&mut self, msg: Message) -> Result<()>; - fn join(&mut self, channel: &str, password: Option<&str>) -> Result<()> { - self.send_message(JOIN(vec![channel.into()], password.iter().map(|&p| p.into()).collect()).to_message()) - } - - fn msg(&mut self, to: &str, message: &str) -> Result<()> { - self.send_message(PRIVMSG(to.into(), message.into()).to_message()) - } - - fn msg_many(&mut self, to: &str, message: &[&str]) -> Result<()> { - for m in message { - self.msg(to, m); - } - Result(Ok(())) - } - fn msg_word_wrap(&mut self, to: &str, message: &str, limit: u16) -> Result<()> { - let mut line = String::new(); - for word in message.split_whitespace() { - if line.len() + word.len() < limit as usize { - line.push_str(" "); - line.push_str(word); - } else { - debug!("Sending {}", line); - self.msg(to, &line); - line.clear(); - } - } - self.msg(to, &line) - } - - fn register(&mut self, nick: &str, user: &str, desc: &str, pass: Option<&str>) -> Result<()> { - Result(if let Some(pass) = pass { - self.send_message(PASS(pass.into()).to_message()).inner() - } else { Ok(()) } - .and_then(|_| self.send_message(NICK(nick.into()).to_message()).inner()) - .and_then(|_| self.send_message(USER(user.into(), Borrowed("0"), Borrowed("*"), desc.into()).to_message()).inner()) - ) - } - -} - -pub struct OwnedClient { +pub struct Client { stream: Option<StreamKind>, - sink: Sink<Message> } -impl OwnedClient { - pub fn new() -> OwnedClient { - OwnedClient { - stream: None, - sink: Sink::new() - } +impl Client { + pub fn new() -> Client { + Client { stream: None } } fn handle_event(&mut self, msg: &Message) { @@ -146,25 +99,27 @@ impl OwnedClient { } #[inline] - fn send_raw(&mut self, s: &str) -> Result<()> { - info!(">> {}", s); - if DEBUG && s.len() > 512 { - panic!("Message too long, kittens will die if this runs in release mode. Msg: {}", s) + fn send_raw(&mut self, s: &[u8]) -> Result<()> { + if DEBUG && s.len() > 1024 { + panic!("Message too long, kittens will die if this runs in release mode.") } Result(self.stream.as_mut() .ok_or(IrscError::NotConnected) - .and_then(|mut stream| stream.write_all(s.as_bytes()) + .and_then(|mut stream| stream.write_all(s) .and_then(|_| stream.flush()) .map_err(IrscError::Io))) } + fn send_message(&mut self, msg: Message) -> Result<()> { + self.send_raw(msg.bytes()) + } pub fn send(&mut self, cmd: Command) -> Result<()> { self.send_message(cmd.to_message()) } - pub fn listen_with_callback<F>(&mut self, on_event: F) -> Result<()> + pub fn listen<F>(&mut self, on_event: F) -> Result<()> where F: Fn(&mut Client, &Message, Option<Event>) { let reader = BufReader::new(match self.stream { Some(StreamKind::Plain(ref s)) => StreamKind::Plain((*s).try_clone().unwrap()), @@ -173,7 +128,7 @@ impl OwnedClient { }); for raw_line in reader.lines() { - let line = raw_line.as_ref().unwrap().parse(); + let line = Message::parse(raw_line.as_ref().unwrap().as_bytes()); info!("<< {}", raw_line.unwrap()); if let Ok(msg) = line { @@ -193,88 +148,42 @@ impl OwnedClient { Result(Ok(())) } - #[allow(mutable_transmutes)] - fn listen_with_events(&self) -> Result<()> { - let mut s: &mut OwnedClient = unsafe { mem::transmute(self) }; - let reader = BufReader::new(match self.stream { - Some(StreamKind::Plain(ref s)) => StreamKind::Plain((*s).try_clone().unwrap()), - Some(StreamKind::Ssl(ref s)) => StreamKind::Ssl((*s).try_clone().unwrap()), - None => return Result(Err(IrscError::NotConnected)) - }); - - for raw_line in reader.lines() { - let line = raw_line.as_ref().unwrap().parse(); - info!("<< {}", raw_line.unwrap()); - - if let Ok(msg) = line { - s.handle_event(&msg); - self.sink.send(msg); - } - } - Result(Ok(())) - } - - pub fn into_shared(self) -> SharedClient { - SharedClient { - client: Arc::new(OwnedClientCell(UnsafeCell::new(self))), - } + fn join(&mut self, channel: &str, password: Option<&str>) -> Result<()> { + self.send_message(JOIN(vec![channel.into()], password.iter().map(|&p| p.into()).collect()).to_message()) } - pub fn messages(&self) -> Stream<Message> { self.sink.stream() } -} - -struct OwnedClientCell(UnsafeCell<OwnedClient>); -unsafe impl Sync for OwnedClientCell {} - -impl Client for OwnedClient { - fn send_message(&mut self, msg: Message) -> Result<()> { - self.send_raw(&msg.to_string()) + fn msg(&mut self, to: &str, message: &str) -> Result<()> { + self.send_message(PRIVMSG(to.into(), message.into()).to_message()) } -} - -#[derive(Clone)] -pub struct SharedClient { - client: Arc<OwnedClientCell>, -} -impl SharedClient { - pub fn messages(&self) -> Stream<(SharedClient, Message)> { - let cl = SharedClient { client: self.client.clone() }; - unsafe { &*self.client.0.get() }.messages() - .map(move |m| (cl.clone(), m)) + fn msg_many(&mut self, to: &str, message: &[&str]) -> Result<()> { + for m in message { + self.msg(to, m); + } + Result(Ok(())) } - pub fn events(&self) -> Stream<(SharedClient, Message, Event<'static>)> { - self.messages().filter_map(|(cl, msg)| match Command::from_message(&msg) { - Some(m) => Some((cl, msg.clone(), Event::Command(m.clone()).to_static())), - None => match Reply::from_message(&msg) { - Some(r) => Some((cl, msg.clone(), Event::Reply(r).to_static())), - None => None + fn msg_word_wrap(&mut self, to: &str, message: &str, limit: u16) -> Result<()> { + let mut line = String::new(); + for word in message.split_whitespace() { + if line.len() + word.len() < limit as usize { + line.push_str(" "); + line.push_str(word); + } else { + debug!("Sending {}", line); + self.msg(to, &line); + line.clear(); } - }) - } - - pub fn listen_with_events(&mut self) -> Result<()> { - unsafe { &*self.client.0.get() }.listen_with_events() - } - - pub fn commands(&self) -> Stream<(SharedClient, Message, Command<'static>)> { - self.messages().filter_map(|(cl, msg)| match Command::from_message(&msg) { - Some(m) => Some((cl, msg.clone(), m.to_static())), - None => None - }) - } - - pub fn replies(&self) -> Stream<(SharedClient, Message, Reply<'static>)> { - self.messages().filter_map(|(cl, msg)| match Reply::from_message(&msg) { - Some(m) => Some((cl, msg.clone(), m.to_static())), - None => None - }) + } + self.msg(to, &line) } -} -impl Client for SharedClient { - fn send_message(&mut self, msg: Message) -> Result<()> { - unsafe { &mut *self.client.0.get() }.send_raw(&msg.to_string()) + fn register(&mut self, nick: &str, user: &str, desc: &str, pass: Option<&str>) -> Result<()> { + Result(if let Some(pass) = pass { + self.send_message(PASS(pass.into()).to_message()).inner() + } else { Ok(()) } + .and_then(|_| self.send_message(NICK(nick.into()).to_message()).inner()) + .and_then(|_| self.send_message(USER(user.into(), tsu("0"), tsu("*"), desc.into()).to_message()).inner()) + ) } } diff --git a/src/command.rs b/src/command.rs index 1043a43..5f76398 100644 --- a/src/command.rs +++ b/src/command.rs @@ -4,7 +4,8 @@ use std::borrow::{ Cow, IntoCow, Borrow, ToOwned }; use std::borrow::Cow::*; use std::iter::Extend; -use message::{ Message, MsgType }; +use message::Message; +use text::{ Text, TextSlice }; pub type CS<'a> = Cow<'a, str>; @@ -20,89 +21,241 @@ pub type CS<'a> = Cow<'a, str>; // // Please don't cry. -#[derive(Debug, Hash, Clone, PartialEq, Eq)] -pub enum Command<'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>), +macro_rules! commands { + ($( $name: ident { + $id: expr, $doc: meta + b $($borrowed_items: ty),* + o $($owned_items: ty),* + t $($to_names: ident),+ => $($to_exprs: expr),+ + }),+) => ( + #[allow(non_camel_case_types)] + #[derive(Debug, Hash, Clone, PartialEq)] + pub enum Command<'a> { + $( + #[$doc] + $name($($borrowed_items),*) + ),+ + } - /// ```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>), + #[allow(non_camel_case_types)] + #[derive(Debug, Hash, Clone, PartialEq)] + pub enum OwnedCommand { + $( + #[$doc] + $name($($owned_items),*) + ),+ + } - /// ```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. - /// ``` + impl<'a> Command<'a> { + pub fn to_owned(self) -> OwnedCommand { + match self { + $( + Command::$name($($to_names),*) => OwnedCommand::$name($($to_exprs),*) + ),+ + } + } + + pub fn from_message(msg: &'a Message) -> Option<Command<'a>> { + use Command::*; + match msg.command() { + $( + $id => msg.last().map(|&e| $name(e)) + ),+ + } + } + + pub fn to_message(&self) -> Message { + use Command::*; + match self { + $( + &$name(ref s) => Message::format(None, $id, Vec::new(), s) + ),+ + } + } + } + ) +} + +commands! { + PASS { + "PASS", doc = r#"```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 + ```"# + b TextSlice<'a> + o Text + t t => t.into() + }, + NICK { + "NICK", doc = r#"```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. + ```"# + b TextSlice<'a> + o Text + t t => t.into() + }, + USER { + "USER", doc = r#"```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. + ```"# + b TextSlice<'a>, TextSlice<'a>, TextSlice<'a>, TextSlice<'a> + o Text, Text, Text, Text + t r, s, t, u => r.into(), s.into(), t.into(), u.into() + }, + JOIN { + "JOIN", doc = r#"```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 + ```"# + b Vec<TextSlice<'a>>, Vec<TextSlice<'a>> + o Vec<Text>, Vec<Text> + t c, p => c.into_iter().map(Into::into).collect(), + p.into_iter().map(Into::into).collect() + }, + PRIVMSG { + "PRIVMSG", doc = "" + b TextSlice<'a>, TextSlice<'a> + o Text, Text + t target, content => target.into(), content.into() + }, + PING { + "PING", doc = "" + b TextSlice<'a>, Option<TextSlice<'a>> + o Text, Option<Text> + t s1, s2 => s1.into(), s2.map(Into::into) + }, + PONG { + "PONG", doc = "" + b TextSlice<'a>, Option<TextSlice<'a>> + o Text, Option<Text> + t s1, s2 => s1.into(), s2.into() + } +} +/* USER(CS<'a>, CS<'a>, CS<'a>, CS<'a>), /// ```text @@ -288,68 +441,6 @@ pub enum Command<'a> { /// ``` 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 @@ -1596,31 +1687,31 @@ pub enum Command<'a> { match self { &PASS(ref pw) => PASS(pw.to_owned().clone()), /*&NICK(ref nick) => - Message::format(None, Borrowed("NICK"), vec![], Some(nick.clone()), MsgType::Irc), + Message::format(None, Borrowed("NICK"), vec![], Some(nick.clone())), &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), + Some(realname.clone())), &OPER(ref name, ref pw) => Message::format(None, Borrowed("OPER"), - vec![name.clone(), pw.clone()], None, MsgType::Irc), + vec![name.clone(), pw.clone()], None), &UMODE(ref mode) => - Message::format(None, Borrowed("MODE"), vec![], Some(mode.clone()), MsgType::Irc), + Message::format(None, Borrowed("MODE"), vec![], Some(mode.clone())), &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), + type_.clone(), reserved2.clone()], Some(info.clone())), &QUIT(ref msg) => - Message::format(None, Borrowed("QUIT"), vec![], msg.clone(), MsgType::Irc), + Message::format(None, Borrowed("QUIT"), vec![], msg.clone()), &SQUIT(ref server, ref comment) => Message::format(None, Borrowed("SQUIT"), - vec![server.clone()], Some(comment.clone()), MsgType::Irc), + vec![server.clone()], Some(comment.clone())), &JOIN(ref ch, ref pw) => Message::format(None, Borrowed("JOIN"), - vec![Owned(ch.connect(",")), Owned(pw.connect(","))], None, MsgType::Irc), + vec![Owned(ch.connect(",")), Owned(pw.connect(","))], None), &PART(ref ch, ref reason) => Message::format(None, Borrowed("PART"), - vec![Owned(ch.connect(","))], reason.clone(), MsgType::Irc), + vec![Owned(ch.connect(","))], reason.clone()), &MODE(ref channel, ref modes) => // Screw this, folding will have to do. Message::format(None, Borrowed("MODE"), @@ -1628,98 +1719,98 @@ pub enum Command<'a> { v.push(a.clone()); v.push(b.clone()); v - }), None, MsgType::Irc), + }), None), &TOPIC(ref channel, ref topic) => Message::format(None, Borrowed("TOPIC"), - vec![channel.clone()], topic.clone(), MsgType::Irc), + vec![channel.clone()], topic.clone()), &NAMES(ref ch, ref target) => Message::format(None, Borrowed("NAMES"), - vec![Owned(ch.connect(","))], target.clone(), MsgType::Irc), + vec![Owned(ch.connect(","))], target.clone()), &LIST(ref ch, ref target) => Message::format(None, Borrowed("LIST"), - vec![Owned(ch.connect(","))], target.clone(), MsgType::Irc), + vec![Owned(ch.connect(","))], target.clone()), &INVITE(ref nick, ref channel) => Message::format(None, Borrowed("INVITE"), - vec![nick.clone()], Some(channel.clone()), MsgType::Irc), + vec![nick.clone()], Some(channel.clone())), &KICK(ref ch, ref users, ref comment) => Message::format(None, Borrowed("KICK"), vec![Owned(ch.connect(",")), Owned(users.connect(","))], - comment.clone(), MsgType::Irc), + comment.clone()), &PRIVMSG(ref target, ref msg) => Message::format(None, Borrowed("PRIVMSG"), - vec![target.clone()], Some(msg.clone()), MsgType::Irc), + vec![target.clone()], Some(msg.clone())), &NOTICE(ref target, ref text) => Message::format(None, Borrowed("NOTICE"), - vec![target.clone()], Some(text.clone()), MsgType::Irc), + vec![target.clone()], Some(text.clone())), &MOTD(ref target) => - Message::format(None, Borrowed("MOTD"), vec![], target.clone(), MsgType::Irc), + Message::format(None, Borrowed("MOTD"), vec![], target.clone()), &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), + lu.as_ref().and_then(|&(_, ref target)| target.clone())), &VERSION(ref target) => - Message::format(None, Borrowed("VERSION"), vec![], target.clone(), MsgType::Irc), + Message::format(None, Borrowed("VERSION"), vec![], target.clone()), &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), + st.as_ref().and_then(|&(_, ref target)| target.clone())), &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), + None), &TIME(ref target) => - Message::format(None, Borrowed("TIME"), vec![], target.clone(), MsgType::Irc), + Message::format(None, Borrowed("TIME"), vec![], target.clone()), &CONNECT(ref server, ref port, ref remote) => Message::format(None, Borrowed("CONNECT"), - vec![server.clone(), Owned(format!("{}", port))], remote.clone(), MsgType::Irc), + vec![server.clone(), Owned(format!("{}", port))], remote.clone()), &TRACE(ref target) => - Message::format(None, Borrowed("TRACE"), vec![], target.clone(), MsgType::Irc), + Message::format(None, Borrowed("TRACE"), vec![], target.clone()), &ADMIN(ref target) => - Message::format(None, Borrowed("ADMIN"), vec![], target.clone(), MsgType::Irc), + Message::format(None, Borrowed("ADMIN"), vec![], target.clone()), &INFO(ref target) => - Message::format(None, Borrowed("INFO"), vec![], target.clone(), MsgType::Irc), + Message::format(None, Borrowed("INFO"), vec![], target.clone()), &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), + .unwrap_or(vec![]), None), &SQUERY(ref name, ref text) => Message::format(None, Borrowed("SQUERY"), - vec![name.clone()], Some(text.clone()), MsgType::Irc), + vec![name.clone()], Some(text.clone())), &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), + }, None), &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), + .unwrap_or_else(|| vec![Owned(masks.connect(","))]), None), &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), + }, None), &PING(ref s1, ref s2) => - Message::format(None, Borrowed("PING"), vec![s1.clone()], s2.clone(), MsgType::Irc), + Message::format(None, Borrowed("PING"), vec![s1.clone()], s2.clone()), &PONG(ref s1, ref s2) => - Message::format(None, Borrowed("PONG"), vec![s1.clone()], s2.clone(), MsgType::Irc), + Message::format(None, Borrowed("PONG"), vec![s1.clone()], s2.clone()), */ /*&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) + Message::format(None, "PING", c, None) }, &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) + Message::format(None, "PONG", c, None) },*/ _ => unimplemented!() } @@ -1773,33 +1864,33 @@ impl<'a> Command<'a> { use self::Command::*; match self { &PASS(ref pw) => - Message::format(None, Borrowed("PASS"), vec![], Some(pw.clone()), MsgType::Irc), + Message::format(None, Borrowed("PASS"), vec![], Some(pw.clone())), &NICK(ref nick) => - Message::format(None, Borrowed("NICK"), vec![], Some(nick.clone()), MsgType::Irc), + Message::format(None, Borrowed("NICK"), vec![], Some(nick.clone())), &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), + Some(realname.clone())), &OPER(ref name, ref pw) => Message::format(None, Borrowed("OPER"), - vec![name.clone(), pw.clone()], None, MsgType::Irc), + vec![name.clone(), pw.clone()], None), &UMODE(ref mode) => - Message::format(None, Borrowed("MODE"), vec![], Some(mode.clone()), MsgType::Irc), + Message::format(None, Borrowed("MODE"), vec![], Some(mode.clone())), &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), + type_.clone(), reserved2.clone()], Some(info.clone())), &QUIT(ref msg) => - Message::format(None, Borrowed("QUIT"), vec![], msg.clone(), MsgType::Irc), + Message::format(None, Borrowed("QUIT"), vec![], msg.clone()), &SQUIT(ref server, ref comment) => Message::format(None, Borrowed("SQUIT"), - vec![server.clone()], Some(comment.clone()), MsgType::Irc), + vec![server.clone()], Some(comment.clone())), &JOIN(ref ch, ref pw) => Message::format(None, Borrowed("JOIN"), - vec![Owned(ch.connect(",")), Owned(pw.connect(","))], None, MsgType::Irc), + vec![Owned(ch.connect(",")), Owned(pw.connect(","))], None), &PART(ref ch, ref reason) => Message::format(None, Borrowed("PART"), - vec![Owned(ch.connect(","))], reason.clone(), MsgType::Irc), + vec![Owned(ch.connect(","))], reason.clone()), &MODE(ref channel, ref modes) => // Screw this, folding will have to do. Message::format(None, Borrowed("MODE"), @@ -1807,87 +1898,87 @@ impl<'a> Command<'a> { v.push(a.clone()); v.push(b.clone()); v - }), None, MsgType::Irc), + }), None), &TOPIC(ref channel, ref topic) => Message::format(None, Borrowed("TOPIC"), - vec![channel.clone()], topic.clone(), MsgType::Irc), + vec![channel.clone()], topic.clone()), &NAMES(ref ch, ref target) => Message::format(None, Borrowed("NAMES"), - vec![Owned(ch.connect(","))], target.clone(), MsgType::Irc), + vec![Owned(ch.connect(","))], target.clone()), &LIST(ref ch, ref target) => Message::format(None, Borrowed("LIST"), - vec![Owned(ch.connect(","))], target.clone(), MsgType::Irc), + vec![Owned(ch.connect(","))], target.clone()), &INVITE(ref nick, ref channel) => Message::format(None, Borrowed("INVITE"), - vec![nick.clone()], Some(channel.clone()), MsgType::Irc), + vec![nick.clone()], Some(channel.clone())), &KICK(ref ch, ref users, ref comment) => Message::format(None, Borrowed("KICK"), vec![Owned(ch.connect(",")), Owned(users.connect(","))], - comment.clone(), MsgType::Irc), + comment.clone()), &PRIVMSG(ref target, ref msg) => Message::format(None, Borrowed("PRIVMSG"), - vec![target.clone()], Some(msg.clone()), MsgType::Irc), + vec![target.clone()], Some(msg.clone())), &NOTICE(ref target, ref text) => Message::format(None, Borrowed("NOTICE"), - vec![target.clone()], Some(text.clone()), MsgType::Irc), + vec![target.clone()], Some(text.clone())), &MOTD(ref target) => - Message::format(None, Borrowed("MOTD"), vec![], target.clone(), MsgType::Irc), + Message::format(None, Borrowed("MOTD"), vec![], target.clone()), &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), + lu.as_ref().and_then(|&(_, ref target)| target.clone())), &VERSION(ref target) => - Message::format(None, Borrowed("VERSION"), vec![], target.clone(), MsgType::Irc), + Message::format(None, Borrowed("VERSION"), vec![], target.clone()), &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), + st.as_ref().and_then(|&(_, ref target)| target.clone())), &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), + None), &TIME(ref target) => - Message::format(None, Borrowed("TIME"), vec![], target.clone(), MsgType::Irc), + Message::format(None, Borrowed("TIME"), vec![], target.clone()), &CONNECT(ref server, ref port, ref remote) => Message::format(None, Borrowed("CONNECT"), - vec![server.clone(), Owned(format!("{}", port))], remote.clone(), MsgType::Irc), + vec![server.clone(), Owned(format!("{}", port))], remote.clone()), &TRACE(ref target) => - Message::format(None, Borrowed("TRACE"), vec![], target.clone(), MsgType::Irc), + Message::format(None, Borrowed("TRACE"), vec![], target.clone()), &ADMIN(ref target) => - Message::format(None, Borrowed("ADMIN"), vec![], target.clone(), MsgType::Irc), + Message::format(None, Borrowed("ADMIN"), vec![], target.clone()), &INFO(ref target) => - Message::format(None, Borrowed("INFO"), vec![], target.clone(), MsgType::Irc), + Message::format(None, Borrowed("INFO"), vec![], target.clone()), &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), + .unwrap_or(vec![]), None), &SQUERY(ref name, ref text) => Message::format(None, Borrowed("SQUERY"), - vec![name.clone()], Some(text.clone()), MsgType::Irc), + vec![name.clone()], Some(text.clone())), &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), + }, None), &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), + .unwrap_or_else(|| vec![Owned(masks.connect(","))]), None), &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), + }, None), &PING(ref s1, ref s2) => - Message::format(None, Borrowed("PING"), vec![s1.clone()], s2.clone(), MsgType::Irc), + Message::format(None, Borrowed("PING"), vec![s1.clone()], s2.clone()), &PONG(ref s1, ref s2) => - Message::format(None, Borrowed("PONG"), vec![s1.clone()], s2.clone(), MsgType::Irc), + Message::format(None, Borrowed("PONG"), vec![s1.clone()], s2.clone()), _ => unimplemented!() } } @@ -1947,4 +2038,4 @@ impl<'a> Command<'a> { } } } - +*/ @@ -9,7 +9,8 @@ extern crate regex; #[macro_use] extern crate log; extern crate openssl; -extern crate carboxyl; +extern crate encoding; +extern crate linear_map; pub mod client; pub mod color; @@ -19,6 +20,7 @@ pub mod message; pub mod command; pub mod reply; pub mod event; +pub mod text; use std::io; use std::result; @@ -26,12 +28,14 @@ use std::ops::{ Deref, DerefMut }; use openssl::ssl::error::SslError; +use encoding::EncodingRef; + pub use ident::Ident; -pub use message::{ Message, MsgType }; +pub use message::Message; pub use command::Command; pub use reply::Reply; pub use event::Event; -pub use client::{ Client, OwnedClient, SharedClient }; +pub use client::Client; #[derive(Debug)] pub enum IrscError { @@ -60,3 +64,4 @@ impl<T> DerefMut for Result<T> { impl<T> Result<T> { fn inner(self) -> result::Result<T, IrscError> { self.0 } } pub const DEBUG: bool = cfg!(debug_assertions); +pub static ENCODING: EncodingRef = encoding::all::UTF_8; diff --git a/src/message.rs b/src/message.rs index be52090..de8766c 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,161 +1,191 @@ #![allow(non_camel_case_types)] -use std::str::FromStr; +use std::str::{ self, FromStr }; use std::string::{ ToString }; use std::borrow::{ ToOwned }; use std::ops::{ Deref, Range }; +use std::fmt; + +use linear_map::LinearMap; use ::IrscError; +use text::{ self, Text, TextSlice }; use ident::Ident; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum MsgType { - /// Plain old IRC messages, as defined in [rfc2812][rfc] - /// rfc: http://tools.ietf.org/html/rfc2812 - Irc, - /// Ctcp messages, wrapped in \u{1} - Ctcp -} - /// Byte indices, be careful. -#[derive(Debug, PartialEq, Eq, Clone)] +/// TODO: more IRCv3 stuff +/// TODO: have MaybeUTF8 enum, try to decode as UTF-8 first, +/// with functions to decode as alternative charsets +/// ircv3.net +#[derive(Clone)] pub struct Message { - pub source: String, + pub source: Text, prefix: Option<Range<u16>>, command: Range<u16>, content: Vec<Range<u16>>, suffix: Option<Range<u16>>, - pub msg_type: MsgType + // only allocates if tags are present + tags: LinearMap<Text, Text> + //pub msg_type: MsgType +} + +impl fmt::Debug for Message { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Message") + .field("source", &self.source) + .finish() + } } impl Message { - pub fn new(source: String, prefix: Option<Range<u16>>, command: Range<u16>, content: Vec<Range<u16>>, suffix: Option<Range<u16>>, msg_type: MsgType) -> Message { + pub fn new(source: Text, prefix: Option<Range<u16>>, command: Range<u16>, content: Vec<Range<u16>>, suffix: Option<Range<u16>>) -> Message { Message { source: source, prefix: prefix, command: command, content: content, suffix: suffix, - msg_type: msg_type + tags: LinearMap::new() } } - #[allow(unused_assignments)] - pub fn format<T: Deref<Target=str>>(prefix: Option<T>, command: T, content: Vec<T>, suffix: Option<T>, msg_type: MsgType) -> Message { - let mut s = String::with_capacity(512); - let mut i = 0; - - let mut i_prefix = None; - if let Some(ref p) = prefix { - i_prefix = Some((i + 1) as u16..(i + 2 + p.len()) as u16); - s.push(':'); - s.push_str(p); - s.push(' '); - i += 2 + p.len(); - } - - let i_command = i as u16..(i + command.len()) as u16; - s.push_str(&*command); - s.push(' '); - i += 1 + command.len(); - - let mut i_content = Vec::new(); - for part in content.iter() { - i_content.push(i as u16..(i + part.len()) as u16); - s.push_str(part); - s.push(' '); - i += 1 + part.len(); - } - - let mut i_suffix = None; - if let Some(ref p) = suffix { - s.push(':'); - if let MsgType::Ctcp = msg_type { s.push('\u{1}'); i += 1; } - let n = i; - s.push_str(p); - if let MsgType::Ctcp = msg_type { s.push('\u{1}'); i += 1; } - i_suffix = Some(n as u16..(n + p.len()) as u16); - i += 1 + p.len(); - } - - s.push_str("\r\n"); - i += 2; - - Message::new(s, i_prefix, i_command, i_content, i_suffix, msg_type) - } - - pub fn range(&self, r: &Range<u16>) -> &str { - &self.source[r.start as usize..r.end as usize] - } - - pub fn prefix(&self) -> Option<&str> { self.prefix.as_ref().map(|r| self.range(r)) } - pub fn command(&self) -> &str { self.range(&self.command) } - pub fn content(&self) -> Vec<&str> { self.content.iter().map(|r| self.range(&r)).collect() } - pub fn suffix(&self) -> Option<&str> { self.suffix.as_ref().map(|r| self.range(r)) } - pub fn elements(&self) -> Vec<&str> { let mut s = self.content(); self.suffix().map(|f| s.push(f)); s } - pub fn ident(&self) -> Option<Ident> { self.prefix().and_then(Ident::parse) } -} - -impl FromStr for Message { - type Err = IrscError; - fn from_str(i: &str) -> Result<Message, IrscError> { + fn parse(i: &[u8]) -> Result<Message, IrscError> { let len = i.len(); + // Use indices instead of subslices, to store // remember, bytes, not chars let mut s = 0; - let prefix = if len >= 1 && i[s..].as_bytes()[0] == ':' as u8 { - i[s..].find(' ').map(|i| { let n = 1u16..(s + i) as u16; s += i + 1; n }) + let prefix = if len >= 1 && i[s] == b':' { + i[s..].iter().cloned() + .find(|&b| b == b' ') + .map(|j| { let n = 1u16..(s + j as usize) as u16; s += j as usize + 1; n }) } else { None }; - let command = i[s..].find(' ').map(|n| { - let p = s as u16..(s + n) as u16; - s += n; + let command = i[s..].iter().cloned() + .find(|&b| b == b' ').map(|n| { + let p = s as u16..(s + n as usize) as u16; + s += n as usize; p }); - let mut content = Vec::with_capacity(15); + let mut content = Vec::with_capacity(3); let mut suffix = None; while s < len - 3 { - if i[s..].as_bytes()[0] == ':' as u8 { + if i[s] == b':' { suffix = Some(s as u16 + 1 as u16..i.len() as u16); break } - i[s..].find(' ').map(|i| { - if i > 0 { - content.push(s as u16..(s + i) as u16); - s += i; + i[s..].iter().cloned() + .find(|&b| b == b' ').map(|j| { + if j > 0 { + content.push(s as u16..(s + j as usize) as u16); + s += j as usize; } }); // if s.chars().next() == Some(' ') { s = &s[1..] }; s += 1; } - let msg_type = if suffix.as_ref().map(|s| i[s.start as usize..].as_bytes()[0] == 1 + /*let msg_type = if suffix.as_ref().map(|s| i[s.start as usize..].as_bytes()[0] == 1 && i[(s.end - 3) as usize..].as_bytes()[0] == 1) - == Some(true) { MsgType::Ctcp } else { MsgType::Irc }; + == Some(true) { MsgType::Ctcp } else { MsgType::Irc };*/ command.map(move |c| Ok(Message::new( - i.to_owned(), + Text::Raw(i.to_owned()), prefix, c, content, // strip \{1} if CTCP message // strip \r\n for each line, relying on their existence - match msg_type { + suffix + /*match msg_type { MsgType::Irc => suffix.map(|s| s.start..s.end - 1), MsgType::Ctcp => suffix.map(|s| s.start + 1..s.end - 2) }, - msg_type + msg_type*/ )) ).unwrap() + } + + #[allow(unused_assignments)] + pub fn format<T: Deref<Target=[u8]>>(prefix: Option<T>, command: T, content: Vec<T>, suffix: Option<T>) -> Message { + let mut s = Vec::with_capacity(512); + let mut i = 0; + + let mut i_prefix = None; + if let Some(ref p) = prefix { + i_prefix = Some((i + 1) as u16..(i + 2 + p.len()) as u16); + s.push(b':'); + s.push_all(p); + s.push(b' '); + i = s.len(); + } + + let i_command = i as u16..(i + command.len()) as u16; + s.push_all(&command); + s.push(b' '); + i = s.len(); + + let mut i_content = Vec::new(); + for part in content.iter() { + i_content.push(i as u16..(i + part.len()) as u16); + s.push_all(part); + s.push(b' '); + i = s.len(); + } + + let mut i_suffix = None; + if let Some(ref p) = suffix { + s.push(b':'); + //if let MsgType::Ctcp = msg_type { s.push('\u{1}'); i += 1; } + let n = i; + s.push_all(p); + //if let MsgType::Ctcp = msg_type { s.push('\u{1}'); i += 1; } + i_suffix = Some(n as u16..(n + p.len()) as u16); + i = s.len(); + } + s.push_all(b"\r\n"); + + Message::new(Text::Raw(s), i_prefix, i_command, i_content, i_suffix) } -} -impl ToString for Message { - fn to_string(&self) -> String { - self.source.clone() + pub fn byte_range(&self, r: &Range<u16>) -> &[u8] { + &self.source[r.start as usize..r.end as usize] + } + + pub fn text_range(&self, r: &Range<u16>) -> TextSlice { + self.source.slice(&(r.start as usize..r.end as usize)) + } + + pub fn string_range(&self, r: &Range<u16>) -> String { + text::def_lossy_decode(self.byte_range(r)) + } + + pub fn str_range(&self, r: &Range<u16>) -> Option<&str> { + str::from_utf8(self.byte_range(r)).ok() + } + + pub fn bytes(&self) -> &[u8] { &*self.source } + + pub fn prefix<'a>(&'a self) -> Option<TextSlice<'a>> { + self.prefix.as_ref().map(|r| self.text_range(r)) } + pub fn command<'a>(&'a self) -> TextSlice<'a> { + self.text_range(&self.command) } + pub fn content<'a>(&'a self) -> Vec<TextSlice<'a>> { + self.content.iter().map(|r| self.text_range(&r)).collect() } + pub fn suffix<'a>(&'a self) -> Option<TextSlice<'a>> { + self.suffix.as_ref().map(|r| self.text_range(r)) } + pub fn last<'a>(&'a self) -> Option<TextSlice<'a>> { + self.suffix().or(self.content.last().map(|l| self.text_range(l))) } + pub fn elements<'a>(&'a self) -> Vec<TextSlice<'a>> { + let mut s = self.content(); self.suffix().map(|f| s.push(f)); s } + pub fn ident(&self) -> Option<Ident> { + self.prefix().and_then(|p| p.utf8()).and_then(Ident::parse) } + pub fn is_ctcp(&self) -> bool { + self.source.get(0) == Some(&1) + && self.source.get(self.source.length() - 3) == Some(&1) } } diff --git a/src/reply.rs b/src/reply.rs index 7b594ff..a1f1dad 100644 --- a/src/reply.rs +++ b/src/reply.rs @@ -3,34 +3,100 @@ use std::borrow::{ Cow, ToOwned }; use std::borrow::Cow::*; use ::{ Result, IrscError }; -use ::message::{ MsgType, Message }; - -pub type CS<'a> = Cow<'a, str>; - -#[allow(non_camel_case_types)] -#[derive(Debug, Hash, Clone, PartialEq, Eq)] -pub enum Reply<'a> { - /// 001 RPL_WELCOME - /// "Welcome to the Internet Relay Network - /// <nick>!<user>@<host>" - RPL_WELCOME(CS<'a>), +use ::message::Message; + +macro_rules! replies { + ($( $name: ident { + $id: expr, $doc: meta + b $($borrowed_items: ty),* + o $($owned_items: ty),* + t $($to_names: ident),+ => $($to_exprs: expr),+ + }),+) => ( + #[allow(non_camel_case_types)] + #[derive(Debug, Hash, Clone, PartialEq, Eq)] + pub enum Reply<'a> { + $( + #[$doc] + $name($($borrowed_items),*) + ),+ + } - /// 002 RPL_YOURHOST - /// "Your host is <servername>, running version <ver>" - RPL_YOURHOST(CS<'a>), + #[allow(non_camel_case_types)] + #[derive(Debug, Hash, Clone, PartialEq, Eq)] + pub enum OwnedReply { + $( + #[$doc] + $name($($owned_items),*) + ),+ + } - /// 003 RPL_CREATED - /// "This server was created <date>" - RPL_CREATED(CS<'a>), + impl<'a> Reply<'a> { + pub fn to_owned(self) -> OwnedReply { + match self { + $( + Reply::$name($($to_names),*) => OwnedReply::$name($($to_exprs),*) + ),+ + } + } + + pub fn from_message(msg: &'a Message) -> Option<Reply<'a>> { + use Reply::*; + match msg.command() { + $( + $id => msg.last().map(|&e| $name(e)) + ),+ + } + } + + pub fn to_message(&self) -> Message { + use Reply::*; + match self { + $( + &$name(ref s) => Message::format(None, $id, Vec::new(), s) + ),+ + } + } + } + ) +} - /// 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>), +replies! { + RPL_WELCOME { + "001", doc = r#"001 RPL_WELCOME + "Welcome to the Internet Relay Network + <nick>!<user>@<host>""# + b &'a [u8] + o Vec<u8> + t s => s.into() + }, + RPL_YOURHOST { + "002", doc = r#"002 RPL_YOURHOST + "Your host is <servername>, running version <ver>""# + b &'a [u8] + o Vec<u8> + t s => s.into() + }, + RPL_CREATED { + "003", doc = r#"003 RPL_CREATED + "This server was created <date>""# + b &'a [u8] + o Vec<u8> + t s => s.into() + }, + RPL_MYINFO { + "004", doc = r#"004 RPL_MYINFO + "<servername> <version> <available user modes> + <available channel modes>" + + - The server sends Replies 001 to 004 to a user upon + successful registration."# + b &'a [u8], &'a [u8], &'a [u8] + o Vec<u8> + t s => s.into() + } +} + /* + RPL_MYINFO(&'a [u8]), /// 005 RPL_BOUNCE /// "Try server <server name>, port <port number>" @@ -39,7 +105,7 @@ pub enum Reply<'a> { /// server. This is often used when the connection is /// refused because the server is already full. /// - RPL_BOUNCE(CS<'a>), + RPL_BOUNCE(&'a [u8]), /// 302 RPL_USERHOST /// ":*1<reply> *( " " <reply> )" @@ -55,7 +121,7 @@ pub enum Reply<'a> { /// whether the client has set an AWAY message or not /// respectively. /// - RPL_USERHOST(CS<'a>), + RPL_USERHOST(&'a [u8]), /// 303 RPL_ISON /// ":*1<nick> *( " " <nick> )" @@ -63,15 +129,15 @@ pub enum Reply<'a> { /// - Reply format used by ISON to list replies to the /// query list. /// - RPL_ISON(CS<'a>), + RPL_ISON(&'a [u8]), /// 301 RPL_AWAY /// "<nick> :<away message>" - RPL_AWAY(CS<'a>), + RPL_AWAY(&'a [u8]), /// 305 RPL_UNAWAY /// ":You are no longer marked as being away" - RPL_UNAWAY(CS<'a>), + RPL_UNAWAY(&'a [u8]), /// 306 RPL_NOWAWAY /// ":You have been marked as being away" @@ -83,27 +149,27 @@ pub enum Reply<'a> { /// Replies RPL_UNAWAY and RPL_NOWAWAY are sent when the /// client removes and sets an AWAY message. /// - RPL_NOWAWAY(CS<'a>), + RPL_NOWAWAY(&'a [u8]), /// 311 RPL_WHOISUSER /// "<nick> <user> <host> * :<real name>" - RPL_WHOISUSER(CS<'a>), + RPL_WHOISUSER(&'a [u8]), /// 312 RPL_WHOISSERVER /// "<nick> <server> :<server info>" - RPL_WHOISSERVER(CS<'a>), + RPL_WHOISSERVER(&'a [u8]), /// 313 RPL_WHOISOPERATOR /// "<nick> :is an IRC operator" - RPL_WHOISOPERATOR(CS<'a>), + RPL_WHOISOPERATOR(&'a [u8]), /// 317 RPL_WHOISIDLE /// "<nick> <integer> :seconds idle" - RPL_WHOISIDLE(CS<'a>), + RPL_WHOISIDLE(&'a [u8]), /// 318 RPL_ENDOFWHOIS /// "<nick> :End of WHOIS list" - RPL_ENDOFWHOIS(CS<'a>), + RPL_ENDOFWHOIS(&'a [u8]), /// 319 RPL_WHOISCHANNELS /// "<nick> :*( ( "@" / "+" ) <channel> " " )" @@ -123,11 +189,11 @@ pub enum Reply<'a> { /// channel. The RPL_ENDOFWHOIS reply is used to mark /// the end of processing a WHOIS message. /// - RPL_WHOISCHANNELS(CS<'a>), + RPL_WHOISCHANNELS(&'a [u8]), /// 314 RPL_WHOWASUSER /// "<nick> <user> <host> * :<real name>" - RPL_WHOWASUSER(CS<'a>), + RPL_WHOWASUSER(&'a [u8]), /// 369 RPL_ENDOFWHOWAS /// "<nick> :End of WHOWAS" @@ -139,7 +205,7 @@ pub enum Reply<'a> { /// be RPL_ENDOFWHOWAS (even if there was only one reply /// and it was an error). /// - RPL_ENDOFWHOWAS(CS<'a>), + RPL_ENDOFWHOWAS(&'a [u8]), /// 321 RPL_LISTSTART /// Obsolete. Not used. @@ -148,7 +214,7 @@ pub enum Reply<'a> { /// 322 RPL_LIST /// "<channel> <# visible> :<topic>" - RPL_LIST(CS<'a>), + RPL_LIST(&'a [u8]), /// 323 RPL_LISTEND /// ":End of LIST" @@ -158,21 +224,21 @@ pub enum Reply<'a> { /// command. If there are no channels available to return, /// only the end reply MUST be sent. /// - RPL_LISTEND(CS<'a>), + RPL_LISTEND(&'a [u8]), /// 325 RPL_UNIQOPIS /// "<channel> <nickname>" /// - RPL_UNIQOPIS(CS<'a>), + RPL_UNIQOPIS(&'a [u8]), /// 324 RPL_CHANNELMODEIS /// "<channel> <mode> <mode params>" /// - RPL_CHANNELMODEIS(CS<'a>), + RPL_CHANNELMODEIS(&'a [u8]), /// 331 RPL_NOTOPIC /// "<channel> :No topic is set" - RPL_NOTOPIC(CS<'a>), + RPL_NOTOPIC(&'a [u8]), /// 332 RPL_TOPIC /// "<channel> :<topic>" @@ -182,7 +248,7 @@ pub enum Reply<'a> { /// the topic is set, RPL_TOPIC is sent back else /// RPL_NOTOPIC. /// - RPL_TOPIC(CS<'a>), + RPL_TOPIC(&'a [u8]), /// 341 RPL_INVITING /// "<channel> <nick>" @@ -191,7 +257,7 @@ pub enum Reply<'a> { /// attempted INVITE message was successful and is /// being passed onto the end client. /// - RPL_INVITING(CS<'a>), + RPL_INVITING(&'a [u8]), /// 342 RPL_SUMMONING /// "<user> :Summoning user to IRC" @@ -199,11 +265,11 @@ pub enum Reply<'a> { /// - Returned by a server answering a SUMMON message to /// indicate that it is summoning that user. /// - RPL_SUMMONING(CS<'a>), + RPL_SUMMONING(&'a [u8]), /// 346 RPL_INVITELIST /// "<channel> <invitemask>" - RPL_INVITELIST(CS<'a>), + RPL_INVITELIST(&'a [u8]), /// 347 RPL_ENDOFINVITELIST /// "<channel> :End of channel invite list" @@ -215,11 +281,11 @@ pub enum Reply<'a> { /// After the masks have been listed (or if none present) a /// RPL_ENDOFINVITELIST MUST be sent. /// - RPL_ENDOFINVITELIST(CS<'a>), + RPL_ENDOFINVITELIST(&'a [u8]), /// 348 RPL_EXCEPTLIST /// "<channel> <exceptionmask>" - RPL_EXCEPTLIST(CS<'a>), + RPL_EXCEPTLIST(&'a [u8]), /// 349 RPL_ENDOFEXCEPTLIST /// "<channel> :End of channel exception list" @@ -231,7 +297,7 @@ pub enum Reply<'a> { /// After the masks have been listed (or if none present) /// a RPL_ENDOFEXCEPTLIST MUST be sent. /// - RPL_ENDOFEXCEPTLIST(CS<'a>), + RPL_ENDOFEXCEPTLIST(&'a [u8]), /// 351 RPL_VERSION /// "<version>.<debuglevel> <server> :<comments>" @@ -245,14 +311,14 @@ pub enum Reply<'a> { /// The "comments" field may contain any comments about /// the version or further version details. /// - RPL_VERSION(CS<'a>), + RPL_VERSION(&'a [u8]), /// 352 RPL_WHOREPLY /// "<channel> <user> <host> <server> <nick> /// ( "H" / "G" > ["*"] [ ( "@" / "+" ) ] /// :<hopcount> <real name>" /// - RPL_WHOREPLY(CS<'a>), + RPL_WHOREPLY(&'a [u8]), /// 315 RPL_ENDOFWHO /// "<name> :End of WHO list" @@ -265,7 +331,7 @@ pub enum Reply<'a> { /// after processing each list item with <name> being /// the item. /// - RPL_ENDOFWHO(CS<'a>), + RPL_ENDOFWHO(&'a [u8]), /// 353 RPL_NAMREPLY /// "( "=" / "*" / "@" ) <channel> @@ -273,7 +339,7 @@ pub enum Reply<'a> { /// - "@" is used for secret channels, "*" for private /// channels, and "=" for others (public channels). /// - RPL_NAMREPLY(CS<'a>), + RPL_NAMREPLY(&'a [u8]), /// 366 RPL_ENDOFNAMES /// "<channel> :End of NAMES list" @@ -288,11 +354,11 @@ pub enum Reply<'a> { /// RPL_NAMEREPLY messages with a RPL_ENDOFNAMES to mark /// the end. /// - RPL_ENDOFNAMES(CS<'a>), + RPL_ENDOFNAMES(&'a [u8]), /// 364 RPL_LINKS /// "<mask> <server> :<hopcount> <server info>" - RPL_LINKS(CS<'a>), + RPL_LINKS(&'a [u8]), /// 365 RPL_ENDOFLINKS /// "<mask> :End of LINKS list" @@ -301,11 +367,11 @@ pub enum Reply<'a> { /// replies back using the RPL_LINKS numeric and mark the /// end of the list using an RPL_ENDOFLINKS reply. /// - RPL_ENDOFLINKS(CS<'a>), + RPL_ENDOFLINKS(&'a [u8]), /// 367 RPL_BANLIST /// "<channel> <banmask>" - RPL_BANLIST(CS<'a>), + RPL_BANLIST(&'a [u8]), /// 368 RPL_ENDOFBANLIST /// "<channel> :End of channel ban list" @@ -317,11 +383,11 @@ pub enum Reply<'a> { /// banmasks have been listed (or if none present) a /// RPL_ENDOFBANLIST MUST be sent. /// - RPL_ENDOFBANLIST(CS<'a>), + RPL_ENDOFBANLIST(&'a [u8]), /// 371 RPL_INFO /// ":<string>" - RPL_INFO(CS<'a>), + RPL_INFO(&'a [u8]), /// 374 RPL_ENDOFINFO /// ":End of INFO list" @@ -331,15 +397,15 @@ pub enum Reply<'a> { /// with a RPL_ENDOFINFO reply to indicate the end of the /// replies. /// - RPL_ENDOFINFO(CS<'a>), + RPL_ENDOFINFO(&'a [u8]), /// 375 RPL_MOTDSTART /// ":- <server> Message of the day - " - RPL_MOTDSTART(CS<'a>), + RPL_MOTDSTART(&'a [u8]), /// 372 RPL_MOTD /// ":- <text>" - RPL_MOTD(CS<'a>), + RPL_MOTD(&'a [u8]), /// 376 RPL_ENDOFMOTD /// ":End of MOTD command" @@ -351,7 +417,7 @@ pub enum Reply<'a> { /// by a RPL_MOTDSTART (before the RPL_MOTDs) and an /// RPL_ENDOFMOTD (after). /// - RPL_ENDOFMOTD(CS<'a>), + RPL_ENDOFMOTD(&'a [u8]), /// 381 RPL_YOUREOPER /// ":You are now an IRC operator" @@ -360,7 +426,7 @@ pub enum Reply<'a> { /// just successfully issued an OPER message and gained /// operator status. /// - RPL_YOUREOPER(CS<'a>), + RPL_YOUREOPER(&'a [u8]), /// 382 RPL_REHASHING /// "<config file> :Rehashing" @@ -369,7 +435,7 @@ pub enum Reply<'a> { /// a REHASH message, an RPL_REHASHING is sent back to /// the operator. /// - RPL_REHASHING(CS<'a>), + RPL_REHASHING(&'a [u8]), /// 383 RPL_YOURESERVICE /// "You are service <servicename>" @@ -377,7 +443,7 @@ pub enum Reply<'a> { /// - Sent by the server to a service upon successful /// registration. /// - RPL_YOURESERVICE(CS<'a>), + RPL_YOURESERVICE(&'a [u8]), /// 391 RPL_TIME /// "<server> :<string showing server's local time>" @@ -388,19 +454,19 @@ pub enum Reply<'a> { /// time there. There is no further requirement for the /// time string. /// - RPL_TIME(CS<'a>), + RPL_TIME(&'a [u8]), /// 392 RPL_USERSSTART /// ":UserID Terminal Host" - RPL_USERSSTART(CS<'a>), + RPL_USERSSTART(&'a [u8]), /// 393 RPL_USERS /// ":<username> <ttyline> <hostname>" - RPL_USERS(CS<'a>), + RPL_USERS(&'a [u8]), /// 394 RPL_ENDOFUSERS /// ":End of users" - RPL_ENDOFUSERS(CS<'a>), + RPL_ENDOFUSERS(&'a [u8]), /// 395 RPL_NOUSERS /// ":Nobody logged in" @@ -412,59 +478,59 @@ pub enum Reply<'a> { /// or a single RPL_NOUSER. Following this is /// RPL_ENDOFUSERS. /// - RPL_NOUSERS(CS<'a>), + RPL_NOUSERS(&'a [u8]), /// 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>), + RPL_TRACELINK(&'a [u8]), /// 201 RPL_TRACECONNECTING /// "Try. <class> <server>" - RPL_TRACECONNECTING(CS<'a>), + RPL_TRACECONNECTING(&'a [u8]), /// 202 RPL_TRACEHANDSHAKE /// "H.S. <class> <server>" - RPL_TRACEHANDSHAKE(CS<'a>), + RPL_TRACEHANDSHAKE(&'a [u8]), /// 203 RPL_TRACEUNKNOWN /// "???? <class> [<client IP address in dot form>]" - RPL_TRACEUNKNOWN(CS<'a>), + RPL_TRACEUNKNOWN(&'a [u8]), /// 204 RPL_TRACEOPERATOR /// "Oper <class> <nick>" - RPL_TRACEOPERATOR(CS<'a>), + RPL_TRACEOPERATOR(&'a [u8]), /// 205 RPL_TRACEUSER /// "User <class> <nick>" - RPL_TRACEUSER(CS<'a>), + RPL_TRACEUSER(&'a [u8]), /// 206 RPL_TRACESERVER /// "Serv <class> <int>S <int>C <server> /// <nick!user|*!*>@<host|server> V<protocol version>" - RPL_TRACESERVER(CS<'a>), + RPL_TRACESERVER(&'a [u8]), /// 207 RPL_TRACESERVICE /// "Service <class> <name> <type> <active type>" - RPL_TRACESERVICE(CS<'a>), + RPL_TRACESERVICE(&'a [u8]), /// 208 RPL_TRACENEWTYPE /// "<newtype> 0 <client name>" - RPL_TRACENEWTYPE(CS<'a>), + RPL_TRACENEWTYPE(&'a [u8]), /// 209 RPL_TRACECLASS /// "Class <class> <count>" - RPL_TRACECLASS(CS<'a>), + RPL_TRACECLASS(&'a [u8]), /// 210 RPL_TRACERECONNECT /// Unused. - RPL_TRACERECONNECT(CS<'a>), + RPL_TRACERECONNECT(&'a [u8]), /// 261 RPL_TRACELOG /// "File <logfile> <debug level>" - RPL_TRACELOG(CS<'a>), + RPL_TRACELOG(&'a [u8]), /// 262 RPL_TRACEEND /// "<server name> <version & debug level> :End of TRACE" @@ -491,7 +557,7 @@ pub enum Reply<'a> { /// being displayed anyway. /// RPL_TRACEEND is sent to indicate the end of the list. /// - RPL_TRACEEND(CS<'a>), + RPL_TRACEEND(&'a [u8]), /// 211 RPL_STATSLINKINFO /// "<linkname> <sendq> <sent messages> @@ -509,26 +575,26 @@ pub enum Reply<'a> { /// open> indicates how long ago the connection was /// opened, in seconds. /// - RPL_STATSLINKINFO(CS<'a>), + RPL_STATSLINKINFO(&'a [u8]), /// 212 RPL_STATSCOMMANDS /// "<command> <count> <byte count> <remote count>" /// /// - reports statistics on commands usage. /// - RPL_STATSCOMMANDS(CS<'a>), + RPL_STATSCOMMANDS(&'a [u8]), /// 219 RPL_ENDOFSTATS /// "<stats letter> :End of STATS report" /// - RPL_ENDOFSTATS(CS<'a>), + RPL_ENDOFSTATS(&'a [u8]), /// 242 RPL_STATSUPTIME /// ":Server Up %d days %d:%02d:%02d" /// /// - reports the server uptime. /// - RPL_STATSUPTIME(CS<'a>), + RPL_STATSUPTIME(&'a [u8]), /// 243 RPL_STATSOLINE /// "O <hostmask> * <name>" @@ -536,7 +602,7 @@ pub enum Reply<'a> { /// - reports the allowed hosts from where user may become IRC /// operators. /// - RPL_STATSOLINE(CS<'a>), + RPL_STATSOLINE(&'a [u8]), /// 221 RPL_UMODEIS /// "<user mode string>" @@ -544,12 +610,12 @@ pub enum Reply<'a> { /// - To answer a query about a client's own mode, /// RPL_UMODEIS is sent back. /// - RPL_UMODEIS(CS<'a>), + RPL_UMODEIS(&'a [u8]), /// 234 RPL_SERVLIST /// "<name> <server> <mask> <type> <hopcount> <info>" /// - RPL_SERVLIST(CS<'a>), + RPL_SERVLIST(&'a [u8]), /// 235 RPL_SERVLISTEND /// "<mask> <type> :End of service listing" @@ -561,24 +627,24 @@ pub enum Reply<'a> { /// services have been listed (or if none present) a /// RPL_SERVLISTEND MUST be sent. /// - RPL_SERVLISTEND(CS<'a>), + RPL_SERVLISTEND(&'a [u8]), /// 251 RPL_LUSERCLIENT /// ":There are <integer> users and <integer> /// services on <integer> servers" - RPL_LUSERCLIENT(CS<'a>), + RPL_LUSERCLIENT(&'a [u8]), /// 252 RPL_LUSEROP /// "<integer> :operator(s) online" - RPL_LUSEROP(CS<'a>), + RPL_LUSEROP(&'a [u8]), /// 253 RPL_LUSERUNKNOWN /// "<integer> :unknown connection(s)" - RPL_LUSERUNKNOWN(CS<'a>), + RPL_LUSERUNKNOWN(&'a [u8]), /// 254 RPL_LUSERCHANNELS /// "<integer> :channels formed" - RPL_LUSERCHANNELS(CS<'a>), + RPL_LUSERCHANNELS(&'a [u8]), /// 255 RPL_LUSERME /// ":I have <integer> clients and <integer> @@ -593,19 +659,19 @@ pub enum Reply<'a> { /// replies are only sent back if a non-zero count /// is found for them. /// - RPL_LUSERME(CS<'a>), + RPL_LUSERME(&'a [u8]), /// 256 RPL_ADMINME /// "<server> :Administrative info" - RPL_ADMINME(CS<'a>), + RPL_ADMINME(&'a [u8]), /// 257 RPL_ADMINLOC1 /// ":<admin info>" - RPL_ADMINLOC1(CS<'a>), + RPL_ADMINLOC1(&'a [u8]), /// 258 RPL_ADMINLOC2 /// ":<admin info>" - RPL_ADMINLOC2(CS<'a>), + RPL_ADMINLOC2(&'a [u8]), /// 259 RPL_ADMINEMAIL /// ":<admin info>" @@ -622,7 +688,7 @@ pub enum Reply<'a> { /// server (an email address here is REQUIRED) /// in RPL_ADMINEMAIL. /// - RPL_ADMINEMAIL(CS<'a>), + RPL_ADMINEMAIL(&'a [u8]), /// 263 RPL_TRYAGAIN /// "<command> :Please wait a while and try again." @@ -631,7 +697,7 @@ pub enum Reply<'a> { /// it MUST use the reply RPL_TRYAGAIN to inform the /// originating client. /// - RPL_TRYAGAIN(CS<'a>), + RPL_TRYAGAIN(&'a [u8]), /// 401 ERR_NOSUCHNICK /// "<nickname> :No such nick/channel" @@ -639,7 +705,7 @@ pub enum Reply<'a> { /// - Used to indicate the nickname parameter supplied to a /// command is currently unused. /// - ERR_NOSUCHNICK(CS<'a>), + ERR_NOSUCHNICK(&'a [u8]), /// 402 ERR_NOSUCHSERVER /// "<server name> :No such server" @@ -647,14 +713,14 @@ pub enum Reply<'a> { /// - Used to indicate the server name given currently /// does not exist. /// - ERR_NOSUCHSERVER(CS<'a>), + ERR_NOSUCHSERVER(&'a [u8]), /// 403 ERR_NOSUCHCHANNEL /// "<channel name> :No such channel" /// /// - Used to indicate the given channel name is invalid. /// - ERR_NOSUCHCHANNEL(CS<'a>), + ERR_NOSUCHCHANNEL(&'a [u8]), /// 404 ERR_CANNOTSENDTOCHAN /// "<channel name> :Cannot send to channel" @@ -665,7 +731,7 @@ pub enum Reply<'a> { /// banned and is trying to send a PRIVMSG message to /// that channel. /// - ERR_CANNOTSENDTOCHAN(CS<'a>), + ERR_CANNOTSENDTOCHAN(&'a [u8]), /// 405 ERR_TOOMANYCHANNELS /// "<channel name> :You have joined too many channels" @@ -674,7 +740,7 @@ pub enum Reply<'a> { /// number of allowed channels and they try to join /// another channel. /// - ERR_TOOMANYCHANNELS(CS<'a>), + ERR_TOOMANYCHANNELS(&'a [u8]), /// 406 ERR_WASNOSUCHNICK /// "<nickname> :There was no such nickname" @@ -682,7 +748,7 @@ pub enum Reply<'a> { /// - Returned by WHOWAS to indicate there is no history /// information for that nickname. /// - ERR_WASNOSUCHNICK(CS<'a>), + ERR_WASNOSUCHNICK(&'a [u8]), /// 407 ERR_TOOMANYTARGETS /// "<target> :<error code> recipients. <abort message>" @@ -698,7 +764,7 @@ pub enum Reply<'a> { /// channel using the shortname when there are more than one /// such channel. /// - ERR_TOOMANYTARGETS(CS<'a>), + ERR_TOOMANYTARGETS(&'a [u8]), /// 408 ERR_NOSUCHSERVICE /// "<service name> :No such service" @@ -706,30 +772,30 @@ pub enum Reply<'a> { /// - Returned to a client which is attempting to send a SQUERY /// to a service which does not exist. /// - ERR_NOSUCHSERVICE(CS<'a>), + ERR_NOSUCHSERVICE(&'a [u8]), /// 409 ERR_NOORIGIN /// ":No origin specified" /// /// - PING or PONG message missing the originator parameter. /// - ERR_NOORIGIN(CS<'a>), + ERR_NOORIGIN(&'a [u8]), /// 411 ERR_NORECIPIENT /// ":No recipient given (<command>)" - ERR_NORECIPIENT(CS<'a>), + ERR_NORECIPIENT(&'a [u8]), /// 412 ERR_NOTEXTTOSEND /// ":No text to send" - ERR_NOTEXTTOSEND(CS<'a>), + ERR_NOTEXTTOSEND(&'a [u8]), /// 413 ERR_NOTOPLEVEL /// "<mask> :No toplevel domain specified" - ERR_NOTOPLEVEL(CS<'a>), + ERR_NOTOPLEVEL(&'a [u8]), /// 414 ERR_WILDTOPLEVEL /// "<mask> :Wildcard in toplevel domain" - ERR_WILDTOPLEVEL(CS<'a>), + ERR_WILDTOPLEVEL(&'a [u8]), /// 415 ERR_BADMASK /// "<mask> :Bad Server/host mask" @@ -740,7 +806,7 @@ pub enum Reply<'a> { /// are returned when an invalid use of /// "PRIVMSG $<server>" or "PRIVMSG #<host>" is attempted. /// - ERR_BADMASK(CS<'a>), + ERR_BADMASK(&'a [u8]), /// 421 ERR_UNKNOWNCOMMAND /// "<command> :Unknown command" @@ -748,14 +814,14 @@ pub enum Reply<'a> { /// - Returned to a registered client to indicate that the /// command sent is unknown by the server. /// - ERR_UNKNOWNCOMMAND(CS<'a>), + ERR_UNKNOWNCOMMAND(&'a [u8]), /// 422 ERR_NOMOTD /// ":MOTD File is missing" /// /// - Server's MOTD file could not be opened by the server. /// - ERR_NOMOTD(CS<'a>), + ERR_NOMOTD(&'a [u8]), /// 423 ERR_NOADMININFO /// "<server> :No administrative info available" @@ -764,7 +830,7 @@ pub enum Reply<'a> { /// when there is an error in finding the appropriate /// information. /// - ERR_NOADMININFO(CS<'a>), + ERR_NOADMININFO(&'a [u8]), /// 424 ERR_FILEERROR /// ":File error doing <file op> on <file>" @@ -772,7 +838,7 @@ pub enum Reply<'a> { /// - Generic error message used to report a failed file /// operation during the processing of a message. /// - ERR_FILEERROR(CS<'a>), + ERR_FILEERROR(&'a [u8]), /// 431 ERR_NONICKNAMEGIVEN /// ":No nickname given" @@ -780,7 +846,7 @@ pub enum Reply<'a> { /// - Returned when a nickname parameter expected for a /// command and isn't found. /// - ERR_NONICKNAMEGIVEN(CS<'a>), + ERR_NONICKNAMEGIVEN(&'a [u8]), /// 432 ERR_ERRONEUSNICKNAME /// "<nick> :Erroneous nickname" @@ -789,7 +855,7 @@ pub enum Reply<'a> { /// characters which do not fall in the defined set. See /// section 2.3.1 for details on valid nicknames. /// - ERR_ERRONEUSNICKNAME(CS<'a>), + ERR_ERRONEUSNICKNAME(&'a [u8]), /// 433 ERR_NICKNAMEINUSE /// "<nick> :Nickname is already in use" @@ -798,7 +864,7 @@ pub enum Reply<'a> { /// in an attempt to change to a currently existing /// nickname. /// - ERR_NICKNAMEINUSE(CS<'a>), + ERR_NICKNAMEINUSE(&'a [u8]), /// 436 ERR_NICKCOLLISION /// "<nick> :Nickname collision KILL from <user>@<host>" @@ -807,7 +873,7 @@ pub enum Reply<'a> { /// nickname collision (registered of a NICK that /// already exists by another server). /// - ERR_NICKCOLLISION(CS<'a>), + ERR_NICKCOLLISION(&'a [u8]), /// 437 ERR_UNAVAILRESOURCE /// "<nick/channel> :Nick/channel is temporarily unavailable" @@ -819,7 +885,7 @@ pub enum Reply<'a> { /// when the desired nickname is blocked by the nick delay /// mechanism. /// - ERR_UNAVAILRESOURCE(CS<'a>), + ERR_UNAVAILRESOURCE(&'a [u8]), /// 441 ERR_USERNOTINCHANNEL /// "<nick> <channel> :They aren't on that channel" @@ -827,7 +893,7 @@ pub enum Reply<'a> { /// - Returned by the server to indicate that the target /// user of the command is not on the given channel. /// - ERR_USERNOTINCHANNEL(CS<'a>), + ERR_USERNOTINCHANNEL(&'a [u8]), /// 442 ERR_NOTONCHANNEL /// "<channel> :You're not on that channel" @@ -836,7 +902,7 @@ pub enum Reply<'a> { /// perform a channel affecting command for which the /// client isn't a member. /// - ERR_NOTONCHANNEL(CS<'a>), + ERR_NOTONCHANNEL(&'a [u8]), /// 443 ERR_USERONCHANNEL /// "<user> <channel> :is already on channel" @@ -844,7 +910,7 @@ pub enum Reply<'a> { /// - Returned when a client tries to invite a user to a /// channel they are already on. /// - ERR_USERONCHANNEL(CS<'a>), + ERR_USERONCHANNEL(&'a [u8]), /// 444 ERR_NOLOGIN /// "<user> :User not logged in" @@ -853,7 +919,7 @@ pub enum Reply<'a> { /// user was unable to be performed since they were not /// logged in. /// - ERR_NOLOGIN(CS<'a>), + ERR_NOLOGIN(&'a [u8]), /// 445 ERR_SUMMONDISABLED /// ":SUMMON has been disabled" @@ -861,7 +927,7 @@ pub enum Reply<'a> { /// - Returned as a response to the SUMMON command. MUST be /// returned by any server which doesn't implement it. /// - ERR_SUMMONDISABLED(CS<'a>), + ERR_SUMMONDISABLED(&'a [u8]), /// 446 ERR_USERSDISABLED /// ":USERS has been disabled" @@ -869,7 +935,7 @@ pub enum Reply<'a> { /// - Returned as a response to the USERS command. MUST be /// returned by any server which does not implement it. /// - ERR_USERSDISABLED(CS<'a>), + ERR_USERSDISABLED(&'a [u8]), /// 451 ERR_NOTREGISTERED /// ":You have not registered" @@ -878,7 +944,7 @@ pub enum Reply<'a> { /// MUST be registered before the server will allow it /// to be parsed in detail. /// - ERR_NOTREGISTERED(CS<'a>), + ERR_NOTREGISTERED(&'a [u8]), /// 461 ERR_NEEDMOREPARAMS /// "<command> :Not enough parameters" @@ -887,7 +953,7 @@ pub enum Reply<'a> { /// indicate to the client that it didn't supply enough /// parameters. /// - ERR_NEEDMOREPARAMS(CS<'a>), + ERR_NEEDMOREPARAMS(&'a [u8]), /// 462 ERR_ALREADYREGISTRED /// ":Unauthorized command (already registered)" @@ -896,7 +962,7 @@ pub enum Reply<'a> { /// change part of the registered details (such as /// password or user details from second USER message). /// - ERR_ALREADYREGISTRED(CS<'a>), + ERR_ALREADYREGISTRED(&'a [u8]), /// 463 ERR_NOPERMFORHOST /// ":Your host isn't among the privileged" @@ -906,7 +972,7 @@ pub enum Reply<'a> { /// connections from the host the attempted connection /// is tried. /// - ERR_NOPERMFORHOST(CS<'a>), + ERR_NOPERMFORHOST(&'a [u8]), /// 464 ERR_PASSWDMISMATCH /// ":Password incorrect" @@ -915,7 +981,7 @@ pub enum Reply<'a> { /// a connection for which a password was required and /// was either not given or incorrect. /// - ERR_PASSWDMISMATCH(CS<'a>), + ERR_PASSWDMISMATCH(&'a [u8]), /// 465 ERR_YOUREBANNEDCREEP /// ":You are banned from this server" @@ -924,51 +990,51 @@ pub enum Reply<'a> { /// yourself with a server which has been setup to /// explicitly deny connections to you. /// - ERR_YOUREBANNEDCREEP(CS<'a>), + ERR_YOUREBANNEDCREEP(&'a [u8]), /// 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>), + ERR_YOUWILLBEBANNED(&'a [u8]), /// 467 ERR_KEYSET /// "<channel> :Channel key already set" - ERR_KEYSET(CS<'a>), + ERR_KEYSET(&'a [u8]), /// 471 ERR_CHANNELISFULL /// "<channel> :Cannot join channel (+l)" - ERR_CHANNELISFULL(CS<'a>), + ERR_CHANNELISFULL(&'a [u8]), /// 472 ERR_UNKNOWNMODE /// "<char> :is unknown mode char to me for <channel>" - ERR_UNKNOWNMODE(CS<'a>), + ERR_UNKNOWNMODE(&'a [u8]), /// 473 ERR_INVITEONLYCHAN /// "<channel> :Cannot join channel (+i)" - ERR_INVITEONLYCHAN(CS<'a>), + ERR_INVITEONLYCHAN(&'a [u8]), /// 474 ERR_BANNEDFROMCHAN /// "<channel> :Cannot join channel (+b)" - ERR_BANNEDFROMCHAN(CS<'a>), + ERR_BANNEDFROMCHAN(&'a [u8]), /// 475 ERR_BADCHANNELKEY /// "<channel> :Cannot join channel (+k)" - ERR_BADCHANNELKEY(CS<'a>), + ERR_BADCHANNELKEY(&'a [u8]), /// 476 ERR_BADCHANMASK /// "<channel> :Bad Channel Mask" - ERR_BADCHANMASK(CS<'a>), + ERR_BADCHANMASK(&'a [u8]), /// 477 ERR_NOCHANMODES /// "<channel> :Channel doesn't support modes" - ERR_NOCHANMODES(CS<'a>), + ERR_NOCHANMODES(&'a [u8]), /// 478 ERR_BANLISTFULL /// "<channel> <char> :Channel list is full" /// - ERR_BANLISTFULL(CS<'a>), + ERR_BANLISTFULL(&'a [u8]), /// 481 ERR_NOPRIVILEGES /// ":Permission Denied- You're not an IRC operator" @@ -977,7 +1043,7 @@ pub enum Reply<'a> { /// MUST return this error to indicate the attempt was /// unsuccessful. /// - ERR_NOPRIVILEGES(CS<'a>), + ERR_NOPRIVILEGES(&'a [u8]), /// 482 ERR_CHANOPRIVSNEEDED /// "<channel> :You're not channel operator" @@ -987,7 +1053,7 @@ pub enum Reply<'a> { /// making the attempt is not a chanop on the specified /// channel. /// - ERR_CHANOPRIVSNEEDED(CS<'a>), + ERR_CHANOPRIVSNEEDED(&'a [u8]), /// 483 ERR_CANTKILLSERVER /// ":You can't kill a server!" @@ -996,7 +1062,7 @@ pub enum Reply<'a> { /// are to be refused and this error returned directly /// to the client. /// - ERR_CANTKILLSERVER(CS<'a>), + ERR_CANTKILLSERVER(&'a [u8]), /// 484 ERR_RESTRICTED /// ":Your connection is restricted!" @@ -1004,7 +1070,7 @@ pub enum Reply<'a> { /// - Sent by the server to a user upon connection to indicate /// the restricted nature of the connection (user mode "+r"). /// - ERR_RESTRICTED(CS<'a>), + ERR_RESTRICTED(&'a [u8]), /// 485 ERR_UNIQOPPRIVSNEEDED /// ":You're not the original channel operator" @@ -1013,7 +1079,7 @@ pub enum Reply<'a> { /// return this error if the client making the attempt is not /// a chanop on the specified channel. /// - ERR_UNIQOPPRIVSNEEDED(CS<'a>), + ERR_UNIQOPPRIVSNEEDED(&'a [u8]), /// 491 ERR_NOOPERHOST /// ":No O-lines for your host" @@ -1023,7 +1089,7 @@ pub enum Reply<'a> { /// client's host as an operator, this error MUST be /// returned. /// - ERR_NOOPERHOST(CS<'a>), + ERR_NOOPERHOST(&'a [u8]), /// 501 ERR_UMODEUNKNOWNFLAG /// ":Unknown MODE flag" @@ -1032,7 +1098,7 @@ pub enum Reply<'a> { /// message was sent with a nickname parameter and that /// the a mode flag sent was not recognized. /// - ERR_UMODEUNKNOWNFLAG(CS<'a>), + ERR_UMODEUNKNOWNFLAG(&'a [u8]), /// 502 ERR_USERSDONTMATCH /// ":Cannot change mode for other users" @@ -1040,7 +1106,7 @@ pub enum Reply<'a> { /// - Error sent to any user trying to view or change the /// user mode for a user other than themselves. /// - ERR_USERSDONTMATCH(CS<'a>), + ERR_USERSDONTMATCH(&'a [u8]), } @@ -1048,12 +1114,12 @@ impl<'a> Reply<'a> { pub fn from_message(msg: &'a Message) -> Option<Reply<'a>> { use self::Reply::*; match msg.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))), + "001" => msg.last().map(|&e| RPL_WELCOME(Borrowed(e))), + "002" => msg.last().map(|&e| RPL_YOURHOST(Borrowed(e))), + "003" => msg.last().map(|&e| RPL_CREATED(Borrowed(e))), + "004" => msg.last().map(|&e| RPL_MYINFO(Borrowed(e))), + "005" => msg.last().map(|&e| RPL_BOUNCE(Borrowed(e))), + "302" => msg.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))), @@ -1192,287 +1258,143 @@ impl<'a> Reply<'a> { pub fn to_message(&'a self) -> Message { use self::Reply::*; match self { - &RPL_WELCOME(ref s) => Message::format(None, Borrowed("001"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_YOURHOST(ref s) => Message::format(None, Borrowed("002"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_CREATED(ref s) => Message::format(None, Borrowed("003"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_MYINFO(ref s) => Message::format(None, Borrowed("004"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_BOUNCE(ref s) => Message::format(None, Borrowed("005"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_USERHOST(ref s) => Message::format(None, Borrowed("302"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ISON(ref s) => Message::format(None, Borrowed("303"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_AWAY(ref s) => Message::format(None, Borrowed("301"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_UNAWAY(ref s) => Message::format(None, Borrowed("305"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_NOWAWAY(ref s) => Message::format(None, Borrowed("306"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_WHOISUSER(ref s) => Message::format(None, Borrowed("311"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_WHOISSERVER(ref s) => Message::format(None, Borrowed("312"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_WHOISOPERATOR(ref s) => Message::format(None, Borrowed("313"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_WHOISIDLE(ref s) => Message::format(None, Borrowed("317"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ENDOFWHOIS(ref s) => Message::format(None, Borrowed("318"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_WHOISCHANNELS(ref s) => Message::format(None, Borrowed("319"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_WHOWASUSER(ref s) => Message::format(None, Borrowed("314"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ENDOFWHOWAS(ref s) => Message::format(None, Borrowed("369"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_LISTSTART => Message::format(None, Borrowed("321"), vec![], None, MsgType::Irc), - &RPL_LIST(ref s) => Message::format(None, Borrowed("322"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_LISTEND(ref s) => Message::format(None, Borrowed("323"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_UNIQOPIS(ref s) => Message::format(None, Borrowed("325"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_CHANNELMODEIS(ref s) => Message::format(None, Borrowed("324"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_NOTOPIC(ref s) => Message::format(None, Borrowed("331"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TOPIC(ref s) => Message::format(None, Borrowed("332"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_INVITING(ref s) => Message::format(None, Borrowed("341"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_SUMMONING(ref s) => Message::format(None, Borrowed("342"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_INVITELIST(ref s) => Message::format(None, Borrowed("346"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ENDOFINVITELIST(ref s) => Message::format(None, Borrowed("347"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_EXCEPTLIST(ref s) => Message::format(None, Borrowed("348"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ENDOFEXCEPTLIST(ref s) => Message::format(None, Borrowed("349"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_VERSION(ref s) => Message::format(None, Borrowed("351"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_WHOREPLY(ref s) => Message::format(None, Borrowed("352"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ENDOFWHO(ref s) => Message::format(None, Borrowed("315"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_NAMREPLY(ref s) => Message::format(None, Borrowed("353"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ENDOFNAMES(ref s) => Message::format(None, Borrowed("366"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_LINKS(ref s) => Message::format(None, Borrowed("364"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ENDOFLINKS(ref s) => Message::format(None, Borrowed("365"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_BANLIST(ref s) => Message::format(None, Borrowed("367"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ENDOFBANLIST(ref s) => Message::format(None, Borrowed("368"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_INFO(ref s) => Message::format(None, Borrowed("371"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ENDOFINFO(ref s) => Message::format(None, Borrowed("374"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_MOTDSTART(ref s) => Message::format(None, Borrowed("375"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_MOTD(ref s) => Message::format(None, Borrowed("372"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ENDOFMOTD(ref s) => Message::format(None, Borrowed("376"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_YOUREOPER(ref s) => Message::format(None, Borrowed("381"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_REHASHING(ref s) => Message::format(None, Borrowed("382"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_YOURESERVICE(ref s) => Message::format(None, Borrowed("383"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TIME(ref s) => Message::format(None, Borrowed("391"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_USERSSTART(ref s) => Message::format(None, Borrowed("392"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_USERS(ref s) => Message::format(None, Borrowed("393"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ENDOFUSERS(ref s) => Message::format(None, Borrowed("394"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_NOUSERS(ref s) => Message::format(None, Borrowed("395"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TRACELINK(ref s) => Message::format(None, Borrowed("200"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TRACECONNECTING(ref s) => Message::format(None, Borrowed("201"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TRACEHANDSHAKE(ref s) => Message::format(None, Borrowed("202"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TRACEUNKNOWN(ref s) => Message::format(None, Borrowed("203"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TRACEOPERATOR(ref s) => Message::format(None, Borrowed("204"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TRACEUSER(ref s) => Message::format(None, Borrowed("205"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TRACESERVER(ref s) => Message::format(None, Borrowed("206"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TRACESERVICE(ref s) => Message::format(None, Borrowed("207"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TRACENEWTYPE(ref s) => Message::format(None, Borrowed("208"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TRACECLASS(ref s) => Message::format(None, Borrowed("209"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TRACERECONNECT(ref s) => Message::format(None, Borrowed("210"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TRACELOG(ref s) => Message::format(None, Borrowed("261"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TRACEEND(ref s) => Message::format(None, Borrowed("262"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_STATSLINKINFO(ref s) => Message::format(None, Borrowed("211"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_STATSCOMMANDS(ref s) => Message::format(None, Borrowed("212"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ENDOFSTATS(ref s) => Message::format(None, Borrowed("219"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_STATSUPTIME(ref s) => Message::format(None, Borrowed("242"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_STATSOLINE(ref s) => Message::format(None, Borrowed("243"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_UMODEIS(ref s) => Message::format(None, Borrowed("221"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_SERVLIST(ref s) => Message::format(None, Borrowed("234"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_SERVLISTEND(ref s) => Message::format(None, Borrowed("235"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_LUSERCLIENT(ref s) => Message::format(None, Borrowed("251"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_LUSEROP(ref s) => Message::format(None, Borrowed("252"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_LUSERUNKNOWN(ref s) => Message::format(None, Borrowed("253"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_LUSERCHANNELS(ref s) => Message::format(None, Borrowed("254"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_LUSERME(ref s) => Message::format(None, Borrowed("255"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ADMINME(ref s) => Message::format(None, Borrowed("256"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ADMINLOC1(ref s) => Message::format(None, Borrowed("257"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ADMINLOC2(ref s) => Message::format(None, Borrowed("258"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_ADMINEMAIL(ref s) => Message::format(None, Borrowed("259"), vec![], Some(s.clone()), MsgType::Irc), - &RPL_TRYAGAIN(ref s) => Message::format(None, Borrowed("263"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOSUCHNICK(ref s) => Message::format(None, Borrowed("401"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOSUCHSERVER(ref s) => Message::format(None, Borrowed("402"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOSUCHCHANNEL(ref s) => Message::format(None, Borrowed("403"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_CANNOTSENDTOCHAN(ref s) => Message::format(None, Borrowed("404"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_TOOMANYCHANNELS(ref s) => Message::format(None, Borrowed("405"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_WASNOSUCHNICK(ref s) => Message::format(None, Borrowed("406"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_TOOMANYTARGETS(ref s) => Message::format(None, Borrowed("407"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOSUCHSERVICE(ref s) => Message::format(None, Borrowed("408"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOORIGIN(ref s) => Message::format(None, Borrowed("409"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NORECIPIENT(ref s) => Message::format(None, Borrowed("411"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOTEXTTOSEND(ref s) => Message::format(None, Borrowed("412"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOTOPLEVEL(ref s) => Message::format(None, Borrowed("413"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_WILDTOPLEVEL(ref s) => Message::format(None, Borrowed("414"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_BADMASK(ref s) => Message::format(None, Borrowed("415"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_UNKNOWNCOMMAND(ref s) => Message::format(None, Borrowed("421"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOMOTD(ref s) => Message::format(None, Borrowed("422"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOADMININFO(ref s) => Message::format(None, Borrowed("423"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_FILEERROR(ref s) => Message::format(None, Borrowed("424"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NONICKNAMEGIVEN(ref s) => Message::format(None, Borrowed("431"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_ERRONEUSNICKNAME(ref s) => Message::format(None, Borrowed("432"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NICKNAMEINUSE(ref s) => Message::format(None, Borrowed("433"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NICKCOLLISION(ref s) => Message::format(None, Borrowed("436"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_UNAVAILRESOURCE(ref s) => Message::format(None, Borrowed("437"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_USERNOTINCHANNEL(ref s) => Message::format(None, Borrowed("441"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOTONCHANNEL(ref s) => Message::format(None, Borrowed("442"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_USERONCHANNEL(ref s) => Message::format(None, Borrowed("443"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOLOGIN(ref s) => Message::format(None, Borrowed("444"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_SUMMONDISABLED(ref s) => Message::format(None, Borrowed("445"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_USERSDISABLED(ref s) => Message::format(None, Borrowed("446"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOTREGISTERED(ref s) => Message::format(None, Borrowed("451"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NEEDMOREPARAMS(ref s) => Message::format(None, Borrowed("461"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_ALREADYREGISTRED(ref s) => Message::format(None, Borrowed("462"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOPERMFORHOST(ref s) => Message::format(None, Borrowed("463"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_PASSWDMISMATCH(ref s) => Message::format(None, Borrowed("464"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_YOUREBANNEDCREEP(ref s) => Message::format(None, Borrowed("465"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_YOUWILLBEBANNED(ref s) => Message::format(None, Borrowed("466"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_KEYSET(ref s) => Message::format(None, Borrowed("467"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_CHANNELISFULL(ref s) => Message::format(None, Borrowed("471"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_UNKNOWNMODE(ref s) => Message::format(None, Borrowed("472"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_INVITEONLYCHAN(ref s) => Message::format(None, Borrowed("473"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_BANNEDFROMCHAN(ref s) => Message::format(None, Borrowed("474"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_BADCHANNELKEY(ref s) => Message::format(None, Borrowed("475"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_BADCHANMASK(ref s) => Message::format(None, Borrowed("476"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOCHANMODES(ref s) => Message::format(None, Borrowed("477"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_BANLISTFULL(ref s) => Message::format(None, Borrowed("478"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOPRIVILEGES(ref s) => Message::format(None, Borrowed("481"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_CHANOPRIVSNEEDED(ref s) => Message::format(None, Borrowed("482"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_CANTKILLSERVER(ref s) => Message::format(None, Borrowed("483"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_RESTRICTED(ref s) => Message::format(None, Borrowed("484"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_UNIQOPPRIVSNEEDED(ref s) => Message::format(None, Borrowed("485"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_NOOPERHOST(ref s) => Message::format(None, Borrowed("491"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_UMODEUNKNOWNFLAG(ref s) => Message::format(None, Borrowed("501"), vec![], Some(s.clone()), MsgType::Irc), - &ERR_USERSDONTMATCH(ref s) => Message::format(None, Borrowed("502"), vec![], Some(s.clone()), MsgType::Irc), + &RPL_WELCOME(ref s) => Message::format(None, Borrowed("001"), vec![], s), + &RPL_YOURHOST(ref s) => Message::format(None, Borrowed("002"), vec![], s), + &RPL_CREATED(ref s) => Message::format(None, Borrowed("003"), vec![], s), + &RPL_MYINFO(ref s) => Message::format(None, Borrowed("004"), vec![], s), + &RPL_BOUNCE(ref s) => Message::format(None, Borrowed("005"), vec![], s), + &RPL_USERHOST(ref s) => Message::format(None, Borrowed("302"), vec![], s), + &RPL_ISON(ref s) => Message::format(None, Borrowed("303"), vec![], s), + &RPL_AWAY(ref s) => Message::format(None, Borrowed("301"), vec![], s), + &RPL_UNAWAY(ref s) => Message::format(None, Borrowed("305"), vec![], s), + &RPL_NOWAWAY(ref s) => Message::format(None, Borrowed("306"), vec![], s), + &RPL_WHOISUSER(ref s) => Message::format(None, Borrowed("311"), vec![], s), + &RPL_WHOISSERVER(ref s) => Message::format(None, Borrowed("312"), vec![], s), + &RPL_WHOISOPERATOR(ref s) => Message::format(None, Borrowed("313"), vec![], s), + &RPL_WHOISIDLE(ref s) => Message::format(None, Borrowed("317"), vec![], s), + &RPL_ENDOFWHOIS(ref s) => Message::format(None, Borrowed("318"), vec![], s), + &RPL_WHOISCHANNELS(ref s) => Message::format(None, Borrowed("319"), vec![], s), + &RPL_WHOWASUSER(ref s) => Message::format(None, Borrowed("314"), vec![], s), + &RPL_ENDOFWHOWAS(ref s) => Message::format(None, Borrowed("369"), vec![], s), + &RPL_LISTSTART => Message::format(None, Borrowed("321"), vec![], None), + &RPL_LIST(ref s) => Message::format(None, Borrowed("322"), vec![], s), + &RPL_LISTEND(ref s) => Message::format(None, Borrowed("323"), vec![], s), + &RPL_UNIQOPIS(ref s) => Message::format(None, Borrowed("325"), vec![], s), + &RPL_CHANNELMODEIS(ref s) => Message::format(None, Borrowed("324"), vec![], s), + &RPL_NOTOPIC(ref s) => Message::format(None, Borrowed("331"), vec![], s), + &RPL_TOPIC(ref s) => Message::format(None, Borrowed("332"), vec![], s), + &RPL_INVITING(ref s) => Message::format(None, Borrowed("341"), vec![], s), + &RPL_SUMMONING(ref s) => Message::format(None, Borrowed("342"), vec![], s), + &RPL_INVITELIST(ref s) => Message::format(None, Borrowed("346"), vec![], s), + &RPL_ENDOFINVITELIST(ref s) => Message::format(None, Borrowed("347"), vec![], s), + &RPL_EXCEPTLIST(ref s) => Message::format(None, Borrowed("348"), vec![], s), + &RPL_ENDOFEXCEPTLIST(ref s) => Message::format(None, Borrowed("349"), vec![], s), + &RPL_VERSION(ref s) => Message::format(None, Borrowed("351"), vec![], s), + &RPL_WHOREPLY(ref s) => Message::format(None, Borrowed("352"), vec![], s), + &RPL_ENDOFWHO(ref s) => Message::format(None, Borrowed("315"), vec![], s), + &RPL_NAMREPLY(ref s) => Message::format(None, Borrowed("353"), vec![], s), + &RPL_ENDOFNAMES(ref s) => Message::format(None, Borrowed("366"), vec![], s), + &RPL_LINKS(ref s) => Message::format(None, Borrowed("364"), vec![], s), + &RPL_ENDOFLINKS(ref s) => Message::format(None, Borrowed("365"), vec![], s), + &RPL_BANLIST(ref s) => Message::format(None, Borrowed("367"), vec![], s), + &RPL_ENDOFBANLIST(ref s) => Message::format(None, Borrowed("368"), vec![], s), + &RPL_INFO(ref s) => Message::format(None, Borrowed("371"), vec![], s), + &RPL_ENDOFINFO(ref s) => Message::format(None, Borrowed("374"), vec![], s), + &RPL_MOTDSTART(ref s) => Message::format(None, Borrowed("375"), vec![], s), + &RPL_MOTD(ref s) => Message::format(None, Borrowed("372"), vec![], s), + &RPL_ENDOFMOTD(ref s) => Message::format(None, Borrowed("376"), vec![], s), + &RPL_YOUREOPER(ref s) => Message::format(None, Borrowed("381"), vec![], s), + &RPL_REHASHING(ref s) => Message::format(None, Borrowed("382"), vec![], s), + &RPL_YOURESERVICE(ref s) => Message::format(None, Borrowed("383"), vec![], s), + &RPL_TIME(ref s) => Message::format(None, Borrowed("391"), vec![], s), + &RPL_USERSSTART(ref s) => Message::format(None, Borrowed("392"), vec![], s), + &RPL_USERS(ref s) => Message::format(None, Borrowed("393"), vec![], s), + &RPL_ENDOFUSERS(ref s) => Message::format(None, Borrowed("394"), vec![], s), + &RPL_NOUSERS(ref s) => Message::format(None, Borrowed("395"), vec![], s), + &RPL_TRACELINK(ref s) => Message::format(None, Borrowed("200"), vec![], s), + &RPL_TRACECONNECTING(ref s) => Message::format(None, Borrowed("201"), vec![], s), + &RPL_TRACEHANDSHAKE(ref s) => Message::format(None, Borrowed("202"), vec![], s), + &RPL_TRACEUNKNOWN(ref s) => Message::format(None, Borrowed("203"), vec![], s), + &RPL_TRACEOPERATOR(ref s) => Message::format(None, Borrowed("204"), vec![], s), + &RPL_TRACEUSER(ref s) => Message::format(None, Borrowed("205"), vec![], s), + &RPL_TRACESERVER(ref s) => Message::format(None, Borrowed("206"), vec![], s), + &RPL_TRACESERVICE(ref s) => Message::format(None, Borrowed("207"), vec![], s), + &RPL_TRACENEWTYPE(ref s) => Message::format(None, Borrowed("208"), vec![], s), + &RPL_TRACECLASS(ref s) => Message::format(None, Borrowed("209"), vec![], s), + &RPL_TRACERECONNECT(ref s) => Message::format(None, Borrowed("210"), vec![], s), + &RPL_TRACELOG(ref s) => Message::format(None, Borrowed("261"), vec![], s), + &RPL_TRACEEND(ref s) => Message::format(None, Borrowed("262"), vec![], s), + &RPL_STATSLINKINFO(ref s) => Message::format(None, Borrowed("211"), vec![], s), + &RPL_STATSCOMMANDS(ref s) => Message::format(None, Borrowed("212"), vec![], s), + &RPL_ENDOFSTATS(ref s) => Message::format(None, Borrowed("219"), vec![], s), + &RPL_STATSUPTIME(ref s) => Message::format(None, Borrowed("242"), vec![], s), + &RPL_STATSOLINE(ref s) => Message::format(None, Borrowed("243"), vec![], s), + &RPL_UMODEIS(ref s) => Message::format(None, Borrowed("221"), vec![], s), + &RPL_SERVLIST(ref s) => Message::format(None, Borrowed("234"), vec![], s), + &RPL_SERVLISTEND(ref s) => Message::format(None, Borrowed("235"), vec![], s), + &RPL_LUSERCLIENT(ref s) => Message::format(None, Borrowed("251"), vec![], s), + &RPL_LUSEROP(ref s) => Message::format(None, Borrowed("252"), vec![], s), + &RPL_LUSERUNKNOWN(ref s) => Message::format(None, Borrowed("253"), vec![], s), + &RPL_LUSERCHANNELS(ref s) => Message::format(None, Borrowed("254"), vec![], s), + &RPL_LUSERME(ref s) => Message::format(None, Borrowed("255"), vec![], s), + &RPL_ADMINME(ref s) => Message::format(None, Borrowed("256"), vec![], s), + &RPL_ADMINLOC1(ref s) => Message::format(None, Borrowed("257"), vec![], s), + &RPL_ADMINLOC2(ref s) => Message::format(None, Borrowed("258"), vec![], s), + &RPL_ADMINEMAIL(ref s) => Message::format(None, Borrowed("259"), vec![], s), + &RPL_TRYAGAIN(ref s) => Message::format(None, Borrowed("263"), vec![], s), + &ERR_NOSUCHNICK(ref s) => Message::format(None, Borrowed("401"), vec![], s), + &ERR_NOSUCHSERVER(ref s) => Message::format(None, Borrowed("402"), vec![], s), + &ERR_NOSUCHCHANNEL(ref s) => Message::format(None, Borrowed("403"), vec![], s), + &ERR_CANNOTSENDTOCHAN(ref s) => Message::format(None, Borrowed("404"), vec![], s), + &ERR_TOOMANYCHANNELS(ref s) => Message::format(None, Borrowed("405"), vec![], s), + &ERR_WASNOSUCHNICK(ref s) => Message::format(None, Borrowed("406"), vec![], s), + &ERR_TOOMANYTARGETS(ref s) => Message::format(None, Borrowed("407"), vec![], s), + &ERR_NOSUCHSERVICE(ref s) => Message::format(None, Borrowed("408"), vec![], s), + &ERR_NOORIGIN(ref s) => Message::format(None, Borrowed("409"), vec![], s), + &ERR_NORECIPIENT(ref s) => Message::format(None, Borrowed("411"), vec![], s), + &ERR_NOTEXTTOSEND(ref s) => Message::format(None, Borrowed("412"), vec![], s), + &ERR_NOTOPLEVEL(ref s) => Message::format(None, Borrowed("413"), vec![], s), + &ERR_WILDTOPLEVEL(ref s) => Message::format(None, Borrowed("414"), vec![], s), + &ERR_BADMASK(ref s) => Message::format(None, Borrowed("415"), vec![], s), + &ERR_UNKNOWNCOMMAND(ref s) => Message::format(None, Borrowed("421"), vec![], s), + &ERR_NOMOTD(ref s) => Message::format(None, Borrowed("422"), vec![], s), + &ERR_NOADMININFO(ref s) => Message::format(None, Borrowed("423"), vec![], s), + &ERR_FILEERROR(ref s) => Message::format(None, Borrowed("424"), vec![], s), + &ERR_NONICKNAMEGIVEN(ref s) => Message::format(None, Borrowed("431"), vec![], s), + &ERR_ERRONEUSNICKNAME(ref s) => Message::format(None, Borrowed("432"), vec![], s), + &ERR_NICKNAMEINUSE(ref s) => Message::format(None, Borrowed("433"), vec![], s), + &ERR_NICKCOLLISION(ref s) => Message::format(None, Borrowed("436"), vec![], s), + &ERR_UNAVAILRESOURCE(ref s) => Message::format(None, Borrowed("437"), vec![], s), + &ERR_USERNOTINCHANNEL(ref s) => Message::format(None, Borrowed("441"), vec![], s), + &ERR_NOTONCHANNEL(ref s) => Message::format(None, Borrowed("442"), vec![], s), + &ERR_USERONCHANNEL(ref s) => Message::format(None, Borrowed("443"), vec![], s), + &ERR_NOLOGIN(ref s) => Message::format(None, Borrowed("444"), vec![], s), + &ERR_SUMMONDISABLED(ref s) => Message::format(None, Borrowed("445"), vec![], s), + &ERR_USERSDISABLED(ref s) => Message::format(None, Borrowed("446"), vec![], s), + &ERR_NOTREGISTERED(ref s) => Message::format(None, Borrowed("451"), vec![], s), + &ERR_NEEDMOREPARAMS(ref s) => Message::format(None, Borrowed("461"), vec![], s), + &ERR_ALREADYREGISTRED(ref s) => Message::format(None, Borrowed("462"), vec![], s), + &ERR_NOPERMFORHOST(ref s) => Message::format(None, Borrowed("463"), vec![], s), + &ERR_PASSWDMISMATCH(ref s) => Message::format(None, Borrowed("464"), vec![], s), + &ERR_YOUREBANNEDCREEP(ref s) => Message::format(None, Borrowed("465"), vec![], s), + &ERR_YOUWILLBEBANNED(ref s) => Message::format(None, Borrowed("466"), vec![], s), + &ERR_KEYSET(ref s) => Message::format(None, Borrowed("467"), vec![], s), + &ERR_CHANNELISFULL(ref s) => Message::format(None, Borrowed("471"), vec![], s), + &ERR_UNKNOWNMODE(ref s) => Message::format(None, Borrowed("472"), vec![], s), + &ERR_INVITEONLYCHAN(ref s) => Message::format(None, Borrowed("473"), vec![], s), + &ERR_BANNEDFROMCHAN(ref s) => Message::format(None, Borrowed("474"), vec![], s), + &ERR_BADCHANNELKEY(ref s) => Message::format(None, Borrowed("475"), vec![], s), + &ERR_BADCHANMASK(ref s) => Message::format(None, Borrowed("476"), vec![], s), + &ERR_NOCHANMODES(ref s) => Message::format(None, Borrowed("477"), vec![], s), + &ERR_BANLISTFULL(ref s) => Message::format(None, Borrowed("478"), vec![], s), + &ERR_NOPRIVILEGES(ref s) => Message::format(None, Borrowed("481"), vec![], s), + &ERR_CHANOPRIVSNEEDED(ref s) => Message::format(None, Borrowed("482"), vec![], s), + &ERR_CANTKILLSERVER(ref s) => Message::format(None, Borrowed("483"), vec![], s), + &ERR_RESTRICTED(ref s) => Message::format(None, Borrowed("484"), vec![], s), + &ERR_UNIQOPPRIVSNEEDED(ref s) => Message::format(None, Borrowed("485"), vec![], s), + &ERR_NOOPERHOST(ref s) => Message::format(None, Borrowed("491"), vec![], s), + &ERR_UMODEUNKNOWNFLAG(ref s) => Message::format(None, Borrowed("501"), vec![], s), + &ERR_USERSDONTMATCH(ref s) => Message::format(None, Borrowed("502"), vec![], s), } } - - pub fn to_static(&self) -> Reply<'static> { - use self::Reply::*; - match self { - &RPL_WELCOME(ref s) => RPL_WELCOME(Cow::Owned(s.clone().into_owned())), - &RPL_YOURHOST(ref s) => RPL_YOURHOST(Cow::Owned(s.clone().into_owned())), - &RPL_CREATED(ref s) => RPL_CREATED(Cow::Owned(s.clone().into_owned())), - &RPL_MYINFO(ref s) => RPL_MYINFO(Cow::Owned(s.clone().into_owned())), - &RPL_BOUNCE(ref s) => RPL_BOUNCE(Cow::Owned(s.clone().into_owned())), - &RPL_USERHOST(ref s) => RPL_USERHOST(Cow::Owned(s.clone().into_owned())), - &RPL_ISON(ref s) => RPL_ISON(Cow::Owned(s.clone().into_owned())), - &RPL_AWAY(ref s) => RPL_AWAY(Cow::Owned(s.clone().into_owned())), - &RPL_UNAWAY(ref s) => RPL_UNAWAY(Cow::Owned(s.clone().into_owned())), - &RPL_NOWAWAY(ref s) => RPL_NOWAWAY(Cow::Owned(s.clone().into_owned())), - &RPL_WHOISUSER(ref s) => RPL_WHOISUSER(Cow::Owned(s.clone().into_owned())), - &RPL_WHOISSERVER(ref s) => RPL_WHOISSERVER(Cow::Owned(s.clone().into_owned())), - &RPL_WHOISOPERATOR(ref s) => RPL_WHOISOPERATOR(Cow::Owned(s.clone().into_owned())), - &RPL_WHOISIDLE(ref s) => RPL_WHOISIDLE(Cow::Owned(s.clone().into_owned())), - &RPL_ENDOFWHOIS(ref s) => RPL_ENDOFWHOIS(Cow::Owned(s.clone().into_owned())), - &RPL_WHOISCHANNELS(ref s) => RPL_WHOISCHANNELS(Cow::Owned(s.clone().into_owned())), - &RPL_WHOWASUSER(ref s) => RPL_WHOWASUSER(Cow::Owned(s.clone().into_owned())), - &RPL_ENDOFWHOWAS(ref s) => RPL_ENDOFWHOWAS(Cow::Owned(s.clone().into_owned())), - &RPL_LISTSTART => RPL_LISTSTART, - &RPL_LIST(ref s) => RPL_LIST(Cow::Owned(s.clone().into_owned())), - &RPL_LISTEND(ref s) => RPL_LISTEND(Cow::Owned(s.clone().into_owned())), - &RPL_UNIQOPIS(ref s) => RPL_UNIQOPIS(Cow::Owned(s.clone().into_owned())), - &RPL_CHANNELMODEIS(ref s) => RPL_CHANNELMODEIS(Cow::Owned(s.clone().into_owned())), - &RPL_NOTOPIC(ref s) => RPL_NOTOPIC(Cow::Owned(s.clone().into_owned())), - &RPL_TOPIC(ref s) => RPL_TOPIC(Cow::Owned(s.clone().into_owned())), - &RPL_INVITING(ref s) => RPL_INVITING(Cow::Owned(s.clone().into_owned())), - &RPL_SUMMONING(ref s) => RPL_SUMMONING(Cow::Owned(s.clone().into_owned())), - &RPL_INVITELIST(ref s) => RPL_INVITELIST(Cow::Owned(s.clone().into_owned())), - &RPL_ENDOFINVITELIST(ref s) => RPL_ENDOFINVITELIST(Cow::Owned(s.clone().into_owned())), - &RPL_EXCEPTLIST(ref s) => RPL_EXCEPTLIST(Cow::Owned(s.clone().into_owned())), - &RPL_ENDOFEXCEPTLIST(ref s) => RPL_ENDOFEXCEPTLIST(Cow::Owned(s.clone().into_owned())), - &RPL_VERSION(ref s) => RPL_VERSION(Cow::Owned(s.clone().into_owned())), - &RPL_WHOREPLY(ref s) => RPL_WHOREPLY(Cow::Owned(s.clone().into_owned())), - &RPL_ENDOFWHO(ref s) => RPL_ENDOFWHO(Cow::Owned(s.clone().into_owned())), - &RPL_NAMREPLY(ref s) => RPL_NAMREPLY(Cow::Owned(s.clone().into_owned())), - &RPL_ENDOFNAMES(ref s) => RPL_ENDOFNAMES(Cow::Owned(s.clone().into_owned())), - &RPL_LINKS(ref s) => RPL_LINKS(Cow::Owned(s.clone().into_owned())), - &RPL_ENDOFLINKS(ref s) => RPL_ENDOFLINKS(Cow::Owned(s.clone().into_owned())), - &RPL_BANLIST(ref s) => RPL_BANLIST(Cow::Owned(s.clone().into_owned())), - &RPL_ENDOFBANLIST(ref s) => RPL_ENDOFBANLIST(Cow::Owned(s.clone().into_owned())), - &RPL_INFO(ref s) => RPL_INFO(Cow::Owned(s.clone().into_owned())), - &RPL_ENDOFINFO(ref s) => RPL_ENDOFINFO(Cow::Owned(s.clone().into_owned())), - &RPL_MOTDSTART(ref s) => RPL_MOTDSTART(Cow::Owned(s.clone().into_owned())), - &RPL_MOTD(ref s) => RPL_MOTD(Cow::Owned(s.clone().into_owned())), - &RPL_ENDOFMOTD(ref s) => RPL_ENDOFMOTD(Cow::Owned(s.clone().into_owned())), - &RPL_YOUREOPER(ref s) => RPL_YOUREOPER(Cow::Owned(s.clone().into_owned())), - &RPL_REHASHING(ref s) => RPL_REHASHING(Cow::Owned(s.clone().into_owned())), - &RPL_YOURESERVICE(ref s) => RPL_YOURESERVICE(Cow::Owned(s.clone().into_owned())), - &RPL_TIME(ref s) => RPL_TIME(Cow::Owned(s.clone().into_owned())), - &RPL_USERSSTART(ref s) => RPL_USERSSTART(Cow::Owned(s.clone().into_owned())), - &RPL_USERS(ref s) => RPL_USERS(Cow::Owned(s.clone().into_owned())), - &RPL_ENDOFUSERS(ref s) => RPL_ENDOFUSERS(Cow::Owned(s.clone().into_owned())), - &RPL_NOUSERS(ref s) => RPL_NOUSERS(Cow::Owned(s.clone().into_owned())), - &RPL_TRACELINK(ref s) => RPL_TRACELINK(Cow::Owned(s.clone().into_owned())), - &RPL_TRACECONNECTING(ref s) => RPL_TRACECONNECTING(Cow::Owned(s.clone().into_owned())), - &RPL_TRACEHANDSHAKE(ref s) => RPL_TRACEHANDSHAKE(Cow::Owned(s.clone().into_owned())), - &RPL_TRACEUNKNOWN(ref s) => RPL_TRACEUNKNOWN(Cow::Owned(s.clone().into_owned())), - &RPL_TRACEOPERATOR(ref s) => RPL_TRACEOPERATOR(Cow::Owned(s.clone().into_owned())), - &RPL_TRACEUSER(ref s) => RPL_TRACEUSER(Cow::Owned(s.clone().into_owned())), - &RPL_TRACESERVER(ref s) => RPL_TRACESERVER(Cow::Owned(s.clone().into_owned())), - &RPL_TRACESERVICE(ref s) => RPL_TRACESERVICE(Cow::Owned(s.clone().into_owned())), - &RPL_TRACENEWTYPE(ref s) => RPL_TRACENEWTYPE(Cow::Owned(s.clone().into_owned())), - &RPL_TRACECLASS(ref s) => RPL_TRACECLASS(Cow::Owned(s.clone().into_owned())), - &RPL_TRACERECONNECT(ref s) => RPL_TRACERECONNECT(Cow::Owned(s.clone().into_owned())), - &RPL_TRACELOG(ref s) => RPL_TRACELOG(Cow::Owned(s.clone().into_owned())), - &RPL_TRACEEND(ref s) => RPL_TRACEEND(Cow::Owned(s.clone().into_owned())), - &RPL_STATSLINKINFO(ref s) => RPL_STATSLINKINFO(Cow::Owned(s.clone().into_owned())), - &RPL_STATSCOMMANDS(ref s) => RPL_STATSCOMMANDS(Cow::Owned(s.clone().into_owned())), - &RPL_ENDOFSTATS(ref s) => RPL_ENDOFSTATS(Cow::Owned(s.clone().into_owned())), - &RPL_STATSUPTIME(ref s) => RPL_STATSUPTIME(Cow::Owned(s.clone().into_owned())), - &RPL_STATSOLINE(ref s) => RPL_STATSOLINE(Cow::Owned(s.clone().into_owned())), - &RPL_UMODEIS(ref s) => RPL_UMODEIS(Cow::Owned(s.clone().into_owned())), - &RPL_SERVLIST(ref s) => RPL_SERVLIST(Cow::Owned(s.clone().into_owned())), - &RPL_SERVLISTEND(ref s) => RPL_SERVLISTEND(Cow::Owned(s.clone().into_owned())), - &RPL_LUSERCLIENT(ref s) => RPL_LUSERCLIENT(Cow::Owned(s.clone().into_owned())), - &RPL_LUSEROP(ref s) => RPL_LUSEROP(Cow::Owned(s.clone().into_owned())), - &RPL_LUSERUNKNOWN(ref s) => RPL_LUSERUNKNOWN(Cow::Owned(s.clone().into_owned())), - &RPL_LUSERCHANNELS(ref s) => RPL_LUSERCHANNELS(Cow::Owned(s.clone().into_owned())), - &RPL_LUSERME(ref s) => RPL_LUSERME(Cow::Owned(s.clone().into_owned())), - &RPL_ADMINME(ref s) => RPL_ADMINME(Cow::Owned(s.clone().into_owned())), - &RPL_ADMINLOC1(ref s) => RPL_ADMINLOC1(Cow::Owned(s.clone().into_owned())), - &RPL_ADMINLOC2(ref s) => RPL_ADMINLOC2(Cow::Owned(s.clone().into_owned())), - &RPL_ADMINEMAIL(ref s) => RPL_ADMINEMAIL(Cow::Owned(s.clone().into_owned())), - &RPL_TRYAGAIN(ref s) => RPL_TRYAGAIN(Cow::Owned(s.clone().into_owned())), - &ERR_NOSUCHNICK(ref s) => ERR_NOSUCHNICK(Cow::Owned(s.clone().into_owned())), - &ERR_NOSUCHSERVER(ref s) => ERR_NOSUCHSERVER(Cow::Owned(s.clone().into_owned())), - &ERR_NOSUCHCHANNEL(ref s) => ERR_NOSUCHCHANNEL(Cow::Owned(s.clone().into_owned())), - &ERR_CANNOTSENDTOCHAN(ref s) => ERR_CANNOTSENDTOCHAN(Cow::Owned(s.clone().into_owned())), - &ERR_TOOMANYCHANNELS(ref s) => ERR_TOOMANYCHANNELS(Cow::Owned(s.clone().into_owned())), - &ERR_WASNOSUCHNICK(ref s) => ERR_WASNOSUCHNICK(Cow::Owned(s.clone().into_owned())), - &ERR_TOOMANYTARGETS(ref s) => ERR_TOOMANYTARGETS(Cow::Owned(s.clone().into_owned())), - &ERR_NOSUCHSERVICE(ref s) => ERR_NOSUCHSERVICE(Cow::Owned(s.clone().into_owned())), - &ERR_NOORIGIN(ref s) => ERR_NOORIGIN(Cow::Owned(s.clone().into_owned())), - &ERR_NORECIPIENT(ref s) => ERR_NORECIPIENT(Cow::Owned(s.clone().into_owned())), - &ERR_NOTEXTTOSEND(ref s) => ERR_NOTEXTTOSEND(Cow::Owned(s.clone().into_owned())), - &ERR_NOTOPLEVEL(ref s) => ERR_NOTOPLEVEL(Cow::Owned(s.clone().into_owned())), - &ERR_WILDTOPLEVEL(ref s) => ERR_WILDTOPLEVEL(Cow::Owned(s.clone().into_owned())), - &ERR_BADMASK(ref s) => ERR_BADMASK(Cow::Owned(s.clone().into_owned())), - &ERR_UNKNOWNCOMMAND(ref s) => ERR_UNKNOWNCOMMAND(Cow::Owned(s.clone().into_owned())), - &ERR_NOMOTD(ref s) => ERR_NOMOTD(Cow::Owned(s.clone().into_owned())), - &ERR_NOADMININFO(ref s) => ERR_NOADMININFO(Cow::Owned(s.clone().into_owned())), - &ERR_FILEERROR(ref s) => ERR_FILEERROR(Cow::Owned(s.clone().into_owned())), - &ERR_NONICKNAMEGIVEN(ref s) => ERR_NONICKNAMEGIVEN(Cow::Owned(s.clone().into_owned())), - &ERR_ERRONEUSNICKNAME(ref s) => ERR_ERRONEUSNICKNAME(Cow::Owned(s.clone().into_owned())), - &ERR_NICKNAMEINUSE(ref s) => ERR_NICKNAMEINUSE(Cow::Owned(s.clone().into_owned())), - &ERR_NICKCOLLISION(ref s) => ERR_NICKCOLLISION(Cow::Owned(s.clone().into_owned())), - &ERR_UNAVAILRESOURCE(ref s) => ERR_UNAVAILRESOURCE(Cow::Owned(s.clone().into_owned())), - &ERR_USERNOTINCHANNEL(ref s) => ERR_USERNOTINCHANNEL(Cow::Owned(s.clone().into_owned())), - &ERR_NOTONCHANNEL(ref s) => ERR_NOTONCHANNEL(Cow::Owned(s.clone().into_owned())), - &ERR_USERONCHANNEL(ref s) => ERR_USERONCHANNEL(Cow::Owned(s.clone().into_owned())), - &ERR_NOLOGIN(ref s) => ERR_NOLOGIN(Cow::Owned(s.clone().into_owned())), - &ERR_SUMMONDISABLED(ref s) => ERR_SUMMONDISABLED(Cow::Owned(s.clone().into_owned())), - &ERR_USERSDISABLED(ref s) => ERR_USERSDISABLED(Cow::Owned(s.clone().into_owned())), - &ERR_NOTREGISTERED(ref s) => ERR_NOTREGISTERED(Cow::Owned(s.clone().into_owned())), - &ERR_NEEDMOREPARAMS(ref s) => ERR_NEEDMOREPARAMS(Cow::Owned(s.clone().into_owned())), - &ERR_ALREADYREGISTRED(ref s) => ERR_ALREADYREGISTRED(Cow::Owned(s.clone().into_owned())), - &ERR_NOPERMFORHOST(ref s) => ERR_NOPERMFORHOST(Cow::Owned(s.clone().into_owned())), - &ERR_PASSWDMISMATCH(ref s) => ERR_PASSWDMISMATCH(Cow::Owned(s.clone().into_owned())), - &ERR_YOUREBANNEDCREEP(ref s) => ERR_YOUREBANNEDCREEP(Cow::Owned(s.clone().into_owned())), - &ERR_YOUWILLBEBANNED(ref s) => ERR_YOUWILLBEBANNED(Cow::Owned(s.clone().into_owned())), - &ERR_KEYSET(ref s) => ERR_KEYSET(Cow::Owned(s.clone().into_owned())), - &ERR_CHANNELISFULL(ref s) => ERR_CHANNELISFULL(Cow::Owned(s.clone().into_owned())), - &ERR_UNKNOWNMODE(ref s) => ERR_UNKNOWNMODE(Cow::Owned(s.clone().into_owned())), - &ERR_INVITEONLYCHAN(ref s) => ERR_INVITEONLYCHAN(Cow::Owned(s.clone().into_owned())), - &ERR_BANNEDFROMCHAN(ref s) => ERR_BANNEDFROMCHAN(Cow::Owned(s.clone().into_owned())), - &ERR_BADCHANNELKEY(ref s) => ERR_BADCHANNELKEY(Cow::Owned(s.clone().into_owned())), - &ERR_BADCHANMASK(ref s) => ERR_BADCHANMASK(Cow::Owned(s.clone().into_owned())), - &ERR_NOCHANMODES(ref s) => ERR_NOCHANMODES(Cow::Owned(s.clone().into_owned())), - &ERR_BANLISTFULL(ref s) => ERR_BANLISTFULL(Cow::Owned(s.clone().into_owned())), - &ERR_NOPRIVILEGES(ref s) => ERR_NOPRIVILEGES(Cow::Owned(s.clone().into_owned())), - &ERR_CHANOPRIVSNEEDED(ref s) => ERR_CHANOPRIVSNEEDED(Cow::Owned(s.clone().into_owned())), - &ERR_CANTKILLSERVER(ref s) => ERR_CANTKILLSERVER(Cow::Owned(s.clone().into_owned())), - &ERR_RESTRICTED(ref s) => ERR_RESTRICTED(Cow::Owned(s.clone().into_owned())), - &ERR_UNIQOPPRIVSNEEDED(ref s) => ERR_UNIQOPPRIVSNEEDED(Cow::Owned(s.clone().into_owned())), - &ERR_NOOPERHOST(ref s) => ERR_NOOPERHOST(Cow::Owned(s.clone().into_owned())), - &ERR_UMODEUNKNOWNFLAG(ref s) => ERR_UMODEUNKNOWNFLAG(Cow::Owned(s.clone().into_owned())), - &ERR_USERSDONTMATCH(ref s) => ERR_USERSDONTMATCH(Cow::Owned(s.clone().into_owned())), - } - - } -} +}*/ diff --git a/src/text.rs b/src/text.rs new file mode 100644 index 0000000..98da9b8 --- /dev/null +++ b/src/text.rs @@ -0,0 +1,196 @@ +use encoding::types::{ DecoderTrap, EncodingRef }; +use encoding::all::encodings; + +use std::ops::{ Range, Deref, Index }; +use std::borrow::Borrow; +use std::fmt; + +// shorthand-exports for construction +pub use self::Text::Raw as tr; +pub use self::Text::Utf8 as tu; +pub use self::TextSlice::Raw as tsr; +pub use self::TextSlice::Utf8 as tsu; + +/// Safe wrapper around something that's supposed to represent text. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum Text { + Raw(Vec<u8>), + Utf8(String), +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum TextSlice<'a> { + Raw(&'a [u8]), + Utf8(&'a str) +} + +pub const EMPTY_RAW: TextSlice<'static> = TextSlice::Raw(&[]); +pub const EMPTY_UTF8: TextSlice<'static> = TextSlice::Utf8(""); + +impl Text { + pub fn decode_with(&self, e: EncodingRef, d: DecoderTrap) -> Text { + match self { + &Text::Raw(b) => match e.decode(&b, d) { + Ok(s) => Text::Utf8(s), + Err(_) => Text::Raw(b) + }, + &Text::Utf8(s) => Text::Utf8(s) + } + } + + pub fn try_decode_with(&self, e: EncodingRef) -> Text { + self.decode_with(DecoderTrap::Strict) + } + + pub fn try_decode_as(&self, e: &str) -> Option<Text> { + encoding(e).map(|e| self.try_decode_with(e)) + } + + pub fn lossy_decode_with(&self, e: EncodingRef) -> Text { + self.decode_with(DecoderTrap::Replace) + } + + pub fn lossy_decode_as(&self, e: &str) -> Option<Text> { + encoding(e).map(|e| self.lossy_decode_with(e)) + } + + pub fn raw(&self) -> Option<&[u8]> { + match self { + &Text::Raw(ref b) => Some(b), + _ => None + } + } + + pub fn utf8(&self) -> Option<&str> { + match self { + &Text::Utf8(ref s) => Some(s), + _ => None + } + } + + pub fn slice<'a>(&'a self, r: &Range<usize>) -> TextSlice<'a> { + match self { + &Text::Raw(ref b) => TextSlice::Raw(&b[r]), + &Text::Utf8(ref s) => TextSlice::Utf8(&s[r]) + } + } + + pub fn length(&self) -> usize { + match self { + &Text::Raw(ref b) => b.len(), + &Text::Utf8(ref s) => s.len() + } + } +} + +impl<'a> TextSlice<'a> { + pub fn raw(&self) -> Option<&[u8]> { + match self { + &TextSlice::Raw(ref b) => Some(b), + _ => None + } + } + + pub fn utf8(&self) -> Option<&str> { + match self { + &TextSlice::Utf8(ref s) => Some(s), + _ => None + } + } +} + +impl<'a> From<TextSlice<'a>> for Text { + fn from(ts: TextSlice<'a>) -> Text { + match ts { + TextSlice::Raw(b) => Text::Raw(b.into()), + TextSlice::Utf8(s) => Text::Utf8(s.into()) + } + } +} + +impl<'a> From<&'a Text> for TextSlice<'a> { + fn from(t: &'a Text) -> TextSlice<'a> { + match t { + &Text::Raw(ref b) => TextSlice::Raw(b), + &Text::Utf8(ref s) => TextSlice::Utf8(s) + } + } +} + +impl<'a> From<&'a str> for Text { + fn from(s: &'a str) -> Text { + Text::Utf8(s.into()) + } +} + +impl<'a> From<&'a [u8]> for Text { + fn from(b: &'a [u8]) -> Text { + Text::Raw(b.into()) + } +} + +impl<'a> From<&'a str> for TextSlice<'a> { + fn from(s: &'a str) -> TextSlice<'a> { + TextSlice::Utf8(s) + } +} + +impl<'a> From<&'a [u8]> for TextSlice<'a> { + fn from(b: &'a [u8]) -> TextSlice<'a> { + TextSlice::Raw(b) + } +} + + +impl<'a> Deref for TextSlice<'a> { + type Target = [u8]; + fn deref(&self) -> &[u8] { + match self { + &Text::Raw(ref b) => b, + &Text::Utf8(ref s) => s.as_bytes() + } + } +} + +impl Deref for Text { + type Target = [u8]; + fn deref(&self) -> &[u8] { + match self { + &Text::Raw(ref b) => b, + &Text::Utf8(ref s) => s.as_bytes() + } + } +} + +/*impl<'a> PartialEq for TextSlice<'a> { + fn eq(&self, rhs: &TextSlice<'a>) -> bool { + match (self, rhs) { + (&tsr(ref b), &tsr(ref c)) => b == c, + (&tsu(ref b), &tsu(ref c)) => b == c, + _ => false + } + } +} + +impl PartialEq for Text { + fn eq(&self, rhs: &Text) -> bool { + match (self, rhs) { + (&tr(ref b), &tr(ref c)) => b == c, + (&tu(ref b), &tu(ref c)) => b == c, + _ => false + } + } +}*/ + +pub fn encoding(s: &str) -> Option<EncodingRef> { + encodings().into_iter().cloned().find(|e| e.name() == s) +} + +pub fn lossy_decode(b: &[u8], e: EncodingRef) -> String { + e.decode(b, DecoderTrap::Replace) + .ok().expect("Shouldn't error with replacing trap") +} + +pub fn def_lossy_decode(b: &[u8]) -> String { + lossy_decode(b, ::ENCODING) +} |