diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 147 |
1 files changed, 78 insertions, 69 deletions
@@ -3,64 +3,66 @@ use std::io::TcpStream; use std::io::BufferedReader; -use std::io::IoError; +use std::io::{IoError, IoResult}; +use std::sync::{Arc, RWLock, Mutex}; use msg::Message; use msg::cmd; pub mod msg; -pub mod state { - use msg::Message; - use std::io::TcpStream; - - pub struct Disconnected; - pub struct Connected { - pub output: Sender<Message>, - pub stream: TcpStream, - } +#[deriving(Clone, PartialEq, Eq)] +pub struct ClientConfig<T> { + pub nicks: T, + pub username: String, + pub real_name: String, +} - impl Drop for Connected { - fn drop(&mut self) { - let _ = self.stream.close_read(); - let _ = self.stream.close_write(); - } - } +struct SharedState<It> { + username: String, + real_name: String, + future_nicks: Mutex<It>, + chosen_nick: RWLock<String>, } -pub struct IrcClient<State> { - nick: String, - username: String, - real_name: String, - state: State, +pub struct IrcClient<T> { + state: Arc<SharedState<T>>, + stream: TcpStream, + output_chan: Sender<Message>, } -impl IrcClient <state::Disconnected> { - pub fn new(nick: String, username: String, real_name: String) -> IrcClient<state::Disconnected> { - IrcClient { nick: nick, username: username, real_name: real_name, state: state::Disconnected } +impl<T: Iterator<String>+Send+Share> Clone for IrcClient<T> { + fn clone(&self) -> IrcClient<T> { + IrcClient { + state: self.state.clone(), + stream: self.stream.clone(), + output_chan: self.output_chan.clone(), + } } +} - #[allow(experimental)] - pub fn connect(self, host: &str, port: u16, message_sender: Sender<Message>) -> Result<IrcClient<state::Connected>, (IoError, IrcClient<state::Disconnected>)> { - let stream = match TcpStream::connect(host, port) { - Ok(stream) => stream, - Err(e) => return Err((e, self)) - }; +impl<T: Iterator<String>+Send+Share> IrcClient<T> { + pub fn new(config: ClientConfig<T>, host: &str, port: u16, msg_chan: Sender<Message>) -> IoResult<IrcClient<T>> { + let ClientConfig { mut nicks, username, real_name } = config; + let stream = try!(TcpStream::connect(host, port)); let (send_writer, rec_writer) = channel(); - let IrcClient { nick:nick, username: username, real_name: real_name, .. } = self; + let chosen_nick = nicks.next().unwrap(); - let connection = IrcClient{ - nick: nick, - username: username, - real_name: real_name, - state: state::Connected { - stream: stream.clone(), - output: send_writer.clone(), - } + let state = SharedState { + username: username.clone(), + real_name: real_name.clone(), + future_nicks: Mutex::new(nicks), + chosen_nick: RWLock::new(chosen_nick.clone()) }; + let connection = IrcClient { + state: Arc::new(state), + stream: stream.clone(), + output_chan: send_writer.clone() + }; + let reader = stream.clone(); let writer = stream; @@ -72,8 +74,10 @@ impl IrcClient <state::Disconnected> { } }); + let reader_client = connection.clone(); std::task::TaskBuilder::new().named("rusty-irc:reader").spawn(proc() { let mut reader = BufferedReader::new(reader); + let mut reader_client = reader_client; loop { unsafe { let raw: *mut TcpStream = reader.get_ref() as *_ as *mut _; @@ -84,53 +88,58 @@ impl IrcClient <state::Disconnected> { match line { Ok(line) => match from_str::<Message>(line.as_slice().trim_right()) { Some(msg) => { - if message_sender.send_opt(msg.clone()).is_err() { - break; - } - if on_msg_rec(&msg, &send_writer).is_err() { + if msg_chan.send_opt(msg.clone()).is_err() { break; } + reader_client.on_msg_rec(&msg); }, None => println!("Invalid Message recieved"), }, Err(IoError{kind: std::io::TimedOut, ..}) => continue, - Err(e) => { - fail!("Unable to read line: {}", e); - } + Err(IoError{kind: std::io::EndOfFile, ..}) => fail!("Connection closed by server"), + Err(e) => fail!("Unable to read line: {}", e) } } }); - Ok(connection) - } - -} + connection.send(Message::new(cmd::Nick(chosen_nick))); + connection.send(Message::new(cmd::User(username, 8, real_name))); -impl IrcClient<state::Connected> { - pub fn disconnect(self) -> IrcClient<state::Disconnected> { - let IrcClient { nick: nick, username:username, real_name:real_name, .. } = self; - IrcClient { - state: state::Disconnected, - nick: nick, - username: username, - real_name: real_name, - } + Ok(connection) } - pub fn send(&mut self, msg: Message) { - self.state.output.send(msg); + #[inline] + pub fn send(&self, msg: Message) { + let _ = self.output_chan.send_opt(msg); } pub fn sender<'a>(&'a self) -> &'a Sender<Message> { - &self.state.output + &self.output_chan } -} -fn on_msg_rec(msg: &Message, sender: &Sender<Message>) -> Result<(), Message> { - let _prefix = &msg.prefix; - let cmd = &msg.command; - match *cmd { - cmd::Ping(ref s) => sender.send_opt(Message::new(cmd::Pong(s.clone()))), - _ => Ok(()) + pub fn nick(&self) -> String { + self.state.chosen_nick.read().clone() } + + pub fn username<'a>(&'a self) -> &'a str { + self.state.username.as_slice() + } + + pub fn real_name<'a>(&'a self) -> &'a str { + self.state.real_name.as_slice() + } + + fn on_msg_rec(&mut self, msg: &Message) { + let _prefix = &msg.prefix; + let cmd = &msg.command; + match *cmd { + cmd::Ping(ref s) => + self.send(Message::new(cmd::Pong(s.clone()))), + cmd::Numeric(443, _, _) => { + *self.state.chosen_nick.write() = self.state.future_nicks.lock().next().unwrap(); + }, + _ => () + } + } } + |