From 4beb868e0ca2aa5ad9c29e4ba934c0fd8fc2501e Mon Sep 17 00:00:00 2001 From: Zachary Dremann Date: Wed, 25 Jun 2014 09:14:42 -0400 Subject: Allow IrcClient to be cloned, and sent between tasks. --- examples/client.rs | 14 +++-- src/lib.rs | 147 ++++++++++++++++++++++++++++------------------------- 2 files changed, 89 insertions(+), 72 deletions(-) diff --git a/examples/client.rs b/examples/client.rs index a48d2a6..9c5e15a 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -18,9 +18,15 @@ fn main() { let (tx, rx) = channel(); - let client = IrcClient::new("rusti-irc".to_string(), "dremann".to_string(), "Zachary Dremann".to_string()); - let connection = client.connect(host.as_slice(), port, tx).ok().unwrap(); - let sender = connection.sender().clone(); + let nicks = (vec!["rusti-irc".to_string()]).move_iter(); + let config = irc::ClientConfig { + nicks: nicks, + username: "dremann".to_string(), + real_name: "Zachary Dremann".to_string() + }; + + let client = IrcClient::new(config, host.as_slice(), port, tx).unwrap(); + let sender = client.sender().clone(); spawn(proc() { let mut stdin = stdio::stdin(); @@ -38,6 +44,8 @@ fn main() { }); for msg in rx.iter() { + let c = client.clone(); + println!("{}", c.nick()); println!("{} {}", msg.prefix, msg.command); } diff --git a/src/lib.rs b/src/lib.rs index eb31afc..64e267a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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, - pub stream: TcpStream, - } +#[deriving(Clone, PartialEq, Eq)] +pub struct ClientConfig { + 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 { + username: String, + real_name: String, + future_nicks: Mutex, + chosen_nick: RWLock, } -pub struct IrcClient { - nick: String, - username: String, - real_name: String, - state: State, +pub struct IrcClient { + state: Arc>, + stream: TcpStream, + output_chan: Sender, } -impl IrcClient { - pub fn new(nick: String, username: String, real_name: String) -> IrcClient { - IrcClient { nick: nick, username: username, real_name: real_name, state: state::Disconnected } +impl+Send+Share> Clone for IrcClient { + fn clone(&self) -> IrcClient { + 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) -> Result, (IoError, IrcClient)> { - let stream = match TcpStream::connect(host, port) { - Ok(stream) => stream, - Err(e) => return Err((e, self)) - }; +impl+Send+Share> IrcClient { + pub fn new(config: ClientConfig, host: &str, port: u16, msg_chan: Sender) -> IoResult> { + 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 { } }); + 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 { match line { Ok(line) => match from_str::(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 { - pub fn disconnect(self) -> IrcClient { - 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 { - &self.state.output + &self.output_chan } -} -fn on_msg_rec(msg: &Message, sender: &Sender) -> 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(); + }, + _ => () + } + } } + -- cgit v1.2.3