aboutsummaryrefslogblamecommitdiffstats
path: root/src/msg.rs
blob: 0ff9e274158edf1c75c646cf0ac2c95dc8ab202b (plain) (tree)
1
2
3
4
5
6




                                 
                                                     







                                             
                                     



                                     
                                                  



                                               




                                       

 
                                       
                    
                                   







                                                          
                                                                              



                                                                  
                            
















                                                                                                                                                                                         
                                                                                                                                                            



















                                                                                                      























                                                                                                      











































                                                                                                         











                                                           
                                                                                   








                                                                            
                                                                                       















                                                                                  
                                                                                  



                                 



                                                 
                                                               
                 
 

                                                                                         
                                                                                                  









                                                                                                                     
                                         










                                                                                                              


                                                                                                              

                                                                              
                                                                                      
                                                                                                     
                                                                                             
                                                         

                                                                                                 

                                         
                         
                                                                    

                  
                                                              

         
use std::fmt;
use std::from_str::FromStr;
use std::ascii::OwnedStrAsciiExt;

pub mod cmd {
	#[deriving(Clone, PartialEq, Eq, Show, Hash)]
	pub enum Command {
		Nick(String),
		User(String, u8, String),
		Quit(Option<String>),
		Join(String),
		Part(String, Option<String>),
		PrivMsg(String, String),
		Notice(String, String),
		Motd(Option<String>),
		Ping(String),
		Pong(String),
		Error(String),
		Away(Option<String>),
		Numeric(u16, String, Vec<String>),
		UnknownCmd(String, Vec<String>)
	}
}

#[deriving(Clone, PartialEq, Eq, Hash)]
pub struct Prefix {
	pub name: String,
	pub user: Option<String>,
	pub host: Option<String>,
}

#[deriving(Clone, PartialEq, Eq, Hash)]
pub struct Message {
	pub prefix: Option<Prefix>,
	pub command: cmd::Command,
}

impl Message {
	pub fn new(command: cmd::Command) -> Message {
		Message { prefix: None, command: command }
	}

	pub fn with_prefix(prefix: Prefix, command: cmd::Command) -> Message {
		Message { prefix: Some(prefix), command: command }
	}
}

impl fmt::Show for Message {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		if self.prefix.is_some() {
			try!(write!(formatter, ":{} ", self.prefix.get_ref()));
		}

		match self.command {
			cmd::Nick(ref nickname) => write!(formatter, "NICK {}", nickname),
			cmd::User(ref username, mode, ref real_name) => write!(formatter, "USER {} {} * :{}", username, mode, real_name),
			cmd::Quit(ref msg) => if msg.is_some() { write!(formatter, "QUIT :{}", *msg.get_ref()) } else { write!(formatter, "QUIT") },
			cmd::Join(ref channel) => write!(formatter, "JOIN :{}", channel),
			cmd::Part(ref channel, ref msg) => if msg.is_some() { write!(formatter, "PART {} :{}", channel, *msg.get_ref()) } else { write!(formatter, "PART {}", channel) },
			cmd::PrivMsg(ref target, ref msg) => write!(formatter, "PRIVMSG {} :{}", target, msg),
			cmd::Notice(ref target, ref msg) => write!(formatter, "NOTICE {} :{}", target, msg),
			cmd::Ping(ref msg) => write!(formatter, "PING :{}", msg),
			cmd::Pong(ref msg) => write!(formatter, "PONG :{}", msg),
			cmd::Error(ref msg) => write!(formatter, "ERROR :{}", msg),
			cmd::Away(ref msg) => if msg.is_some() { write!(formatter, "AWAY :{}", msg.get_ref()) } else { write!(formatter, "AWAY") },
			cmd::Motd(ref target) => if target.is_some() { write!(formatter, "MOTD :{}", target.get_ref()) } else { write!(formatter, "MOTD") },
			cmd::Numeric(i, ref target, ref args) => {
				try!(write!(formatter, "{:03u} {}", i, target));
				let mut iter = args.iter().peekable();
				loop {
					match iter.next() {
						Some(arg) => {
							try!(
								if iter.peek().is_some() {
									write!(formatter, " {}", arg)
								}
								else {
									write!(formatter, " :{}", arg)
								}
							);
						}
						None => break
					}
				}
				Ok(())
			}
			cmd::UnknownCmd(ref cmd, ref args) => {
				try!(write!(formatter, "{}", cmd));
				let mut iter = args.iter().peekable();
				loop {
					match iter.next() {
						Some(arg) => {
							try!(
								if iter.peek().is_some() {
									write!(formatter, " {}", arg)
								}
								else {
									write!(formatter, " :{}", arg)
								}
							)
						}
						None => break
					}
				}
				Ok(())
			}
		}
	}
}

impl fmt::Show for Prefix {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		try!(write!(f, "{}", self.name));
		if self.user.is_some() {
			try!(write!(f, "!{}", self.user.get_ref()));
		}
		if self.host.is_some() {
			try!(write!(f, "@{}", self.host.get_ref()));
		}
		Ok(())
	}
}

impl FromStr for Prefix {
	fn from_str(s: &str) -> Option<Prefix> {
		let name;
		let user;
		let host;
		match s.find('!') {
			Some(user_start) => {
				name = s.slice_to(user_start).to_string();
				let rest = s.slice_from(user_start + 1);
				match rest.find('@') {
					Some(host_start) => {
						user = Some(rest.slice_to(host_start).to_string());
						host = Some(rest.slice_from(host_start + 1).to_string());
					}
					None => {
						user = Some(rest.to_string());
						host = None;
					}
				}
			}
			None => {
				name = s.to_string();
				user = None;
				host = None;
			}
		}
		Some(Prefix{ name: name, user: user, host: host })
	}

}

impl FromStr for Message {
	fn from_str(s: &str) -> Option<Message> {
		let mut prefix = None;
		let mut cmd = None;
		let mut args = Vec::new();
		let mut current_str: Option<String> = None;
		let mut is_prefix = false;
		let mut is_final = false;
		
		for c in s.chars() {
			match c {
				c if is_final => {
					current_str.as_mut().unwrap().push_char(c);
				}
				' ' => {
					if is_prefix {
						prefix = current_str.take();
					}
					else if cmd.is_none() {
						cmd = current_str.take();
					}
					else {
						args.push(current_str.take().unwrap());
					}
					is_prefix = false;
				}
				':' if current_str.is_none() => {
					current_str = Some(String::new());
					if cmd.is_none() {
						is_prefix = true;
					}
					else {
						is_final = true;
					}
				}
				c => {
					if current_str.is_none() {
						current_str = Some(String::new());
					}
					current_str.as_mut().unwrap().push_char(c)
				}
			}
		}

		if cmd.is_none() {
			cmd = current_str.take();
		}
		else {
			args.push(current_str.take().unwrap());
		}

		let prefix: Option<Prefix> = prefix.and_then(|s| from_str(s.as_slice()));

		let cmd = match cmd.map(|s| s.into_ascii_upper()).as_ref().map(|s| s.as_slice()) {
			Some(s) => {
				match s {
					"NICK" if args.len() == 1 => cmd::Nick(args.pop().unwrap()),
					"USER" if args.len() == 4 => {
						let mut iter = args.move_iter();
						let uname = iter.next().unwrap();
						let opt_mode: Option<u8> = from_str(iter.next().unwrap().as_slice());
						iter.next();
						let fullname = iter.next().unwrap();
						cmd::User(uname, opt_mode.unwrap_or(0), fullname)
					}
					"NOTICE" if args.len() == 2 => {
						let mut iter = args.move_iter();
						cmd::Notice(iter.next().unwrap(), iter.next().unwrap())
					}
					"PRIVMSG" if args.len() == 2 => {
						let mut iter = args.move_iter();
						cmd::PrivMsg(iter.next().unwrap(), iter.next().unwrap())
					}
					"PING" if args.len() == 1 => cmd::Ping(args.pop().unwrap()),
					"PONG" if args.len() == 1 => cmd::Pong(args.pop().unwrap()),
					"AWAY" if args.len() == 0 || args.len() == 1 => cmd::Away(args.pop()),
					"QUIT" if args.len() == 0 || args.len() == 1 => cmd::Quit(args.pop()),
					"JOIN" if args.len() == 1 => cmd::Join(args.pop().unwrap()),
					"MOTD" if args.len() == 0 || args.len() == 1 => cmd::Motd(args.pop()),
					other => {
						match from_str::<u16>(other) {
							Some(n) if args.len() > 0 => {
								let target = args.remove(0).unwrap();
								cmd::Numeric(n, target, args)
							}
							_ => cmd::UnknownCmd(s.to_string(), args)
						}
					}
				}
			}
			None => cmd::UnknownCmd(String::new(), args)
		};

		Some(Message { prefix: prefix, command: cmd })
	}
}