From ad4f61e29f3072fd7d9e78386439e52f8512287e Mon Sep 17 00:00:00 2001 From: Zachary Dremann Date: Wed, 11 Jun 2014 23:43:41 -0400 Subject: Make construction of an IRC client 2 parts, create, then connect. --- examples/client.rs | 17 +++--- src/lib.rs | 151 +++++++++++++++++++++++++++++++++++------------------ 2 files changed, 107 insertions(+), 61 deletions(-) diff --git a/examples/client.rs b/examples/client.rs index 4363060..0419816 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -3,8 +3,6 @@ extern crate irc; use std::io::stdio; use irc::IrcClient; -use irc::msg::Message; -//use irc::msg::cmd; fn main() { let mut stderr = stdio::stderr(); @@ -17,13 +15,12 @@ fn main() { drop(args); - let mut connection = IrcClient::connect(host.as_slice(), port, "rusty-irc".to_string(), "dremann".to_string(), "Zachary Dremann".to_string()).unwrap(); - let sender = connection.sender(); + 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 on_msg = |message: &Message| { - println!("{} {}", message.prefix, message.command); - }; - spawn(proc() { let mut stdin = stdio::stdin(); for line in stdin.lines() { @@ -39,5 +36,7 @@ fn main() { } }); - connection.run_loop(on_msg); + for msg in rx.iter() { + println!("{} {}", msg.prefix, msg.command); + } } diff --git a/src/lib.rs b/src/lib.rs index 3898480..a707059 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,7 @@ #![crate_id = "irc#0.1"] #![crate_type = "lib"] -use std::io::net::tcp::TcpStream; -use std::io::IoResult; +use std::io::TcpStream; use std::io::BufferedReader; use std::io::IoError; @@ -11,24 +10,58 @@ use msg::cmd; pub mod msg; -pub struct IrcClient { - stream: TcpStream, - output_sender: Sender, +pub mod state { + use msg::Message; + use std::io::TcpStream; + + pub struct Disconnected; + pub struct Connected { + pub output: Sender, + pub stream: TcpStream, + } + + impl Drop for Connected { + fn drop(&mut self) { + let _ = self.stream.close_read(); + let _ = self.stream.close_write(); + } + } +} + +pub struct IrcClient { + nick: Option, + username: Option, + real_name: Option, + state: State, } -impl IrcClient { - pub fn connect( - host: &str, port: u16, nick: String, username: String, real_name: String) -> IoResult { - +impl IrcClient { + pub fn new(nick: String, username: String, real_name: String) -> IrcClient { + IrcClient { nick: Some(nick), username: Some(username), real_name: Some(real_name), state: state::Disconnected } + } + + #[allow(experimental)] + pub fn connect(mut 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)) + }; + let (send_writer, rec_writer) = channel(); - let mut connection = IrcClient{ - stream: try!(TcpStream::connect(host, port)), - output_sender: send_writer.clone(), + let connection = IrcClient{ + nick: self.nick.take(), + username: self.username.take(), + real_name: self.real_name.take(), + state: state::Connected { + stream: stream.clone(), + output: send_writer.clone(), + } }; - let writer = connection.stream.clone(); - + let reader = stream.clone(); + let writer = stream; + // spawn writer thread spawn(proc() { let mut writer = writer; @@ -37,52 +70,66 @@ impl IrcClient { } }); - connection.send(Message::new(cmd::Nick(nick))); - connection.send(Message::new(cmd::User(username, 8, real_name))); + spawn(proc() { + let mut reader = reader; + loop { + fn reader_by_ref<'a, R: Reader>(reader: &'a mut R) -> std::io::RefReader<'a, R> { reader.by_ref() } + + reader.set_read_timeout(Some(500)); + let mut buf_reader = BufferedReader::new(reader_by_ref(&mut reader)); + + let line = buf_reader.read_line(); + 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() { + break; + } + }, + None => println!("Invalid Message recieved"), + }, + Err(IoError{kind: std::io::TimedOut, ..}) => continue, + Err(e) => { + println!("Unable to read line: {}", e); + break; + } + } + } + }); + Ok(connection) } - pub fn send(&mut self, message: Message) { - self.output_sender.send(message); +} + +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, + } } - pub fn sender(&self) -> Sender { - self.output_sender.clone() + pub fn send(&mut self, msg: Message) { + self.state.output.send(msg); } - fn on_msg_rec(msg: &Message, sender: &Sender) { - let _prefix = &msg.prefix; - let cmd = &msg.command; - match *cmd { - cmd::Ping(ref s) => sender.send(Message::new(cmd::Pong(s.clone()))), - _ => { } - }; + pub fn sender<'a>(&'a self) -> &'a Sender { + &self.state.output } +} - #[allow(experimental)] - pub fn run_loop(&mut self, on_msg: |&Message| -> ()) { - let reader = &mut self.stream; - loop { - fn reader_by_ref<'a, R: Reader>(reader: &'a mut R) -> std::io::RefReader<'a, R> { reader.by_ref() } - - reader.set_read_timeout(Some(500)); - let mut buf_reader = BufferedReader::new(reader_by_ref(reader)); - - let line = buf_reader.read_line(); - match line { - Ok(line) => match from_str::(line.as_slice().trim_right()) { - Some(msg) => { - IrcClient::on_msg_rec(&msg, &self.output_sender); - on_msg(&msg); - }, - None => println!("Invalid Message recieved"), - }, - Err(IoError{kind: std::io::TimedOut, ..}) => continue, - Err(e) => { - println!("Unable to read line: {}", e); - break; - } - } - } +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(()) } } -- cgit v1.2.3