aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml6
-rw-r--r--README.md48
-rw-r--r--examples/01.rs46
-rw-r--r--src/client.rs75
-rw-r--r--src/event.rs9
-rw-r--r--src/lib.rs16
-rw-r--r--src/message.rs2
7 files changed, 113 insertions, 89 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 797fcc3..4cb462e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,13 +13,19 @@ license = "MIT"
log = "*"
regex = "*"
regex_macros = "*"
+carboxyl = "*"
[features]
ssl = ["openssl"]
+lints = ["clippy"]
[dependencies.openssl]
version = "*"
optional = true
+[dependencies.clippy]
+version = "*"
+optional = true
+
[dev-dependencies]
env_logger = "*"
diff --git a/README.md b/README.md
index ff71b86..273fd24 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@ This library is supposed to be a thin layer over the IRC protocol, doing all the
## Example
-Compiles and works with `rustc 1.1.0-nightly (ce1150b9f 2015-05-04)` and `fbb7251493ff5a9bc42f849b9b781e69aef9d184` of this library.
+Compiles and tested with `rustc 1.2.0-nightly (8937ec100 2015-06-15)` and `63838165c31397fec199bf99c96497a1169c4d52` of this library.
Run with
@@ -38,69 +38,49 @@ and join [#botzoo on irc.mozilla.org](http://irc.lc/mozilla/botzoo).
```rust
extern crate irsc;
extern crate env_logger;
-#[cfg(feature = "ssl")]
extern crate openssl;
-use std::borrow::ToOwned;
-use std::borrow::Cow::*;
-
-use irsc::client::Client;
use irsc::color::bold;
use irsc::*;
use irsc::Command::*;
use irsc::Reply::*;
-#[cfg(feature = "ssl")]
use openssl::ssl::{ Ssl, SslContext, SslMethod };
static NAME: &'static str = "rusticbot";
static DESC: &'static str = "A bot, written in Rust.";
-fn callback(server: &mut Client, msg: &Message) {
- match Command::from_message(msg) {
- Some(PRIVMSG(to, content)) => {
- let from = msg.prefix().and_then(Ident::parse).unwrap();
+fn callback(server: &mut Client, msg: &Message, event: Option<Event>) {
+ match event {
+ Some(Event::Command(PRIVMSG(to, content))) => {
+ let from = msg.ident().unwrap();
let response = match msg.msg_type {
MsgType::Irc => format!("{} wrote: {}", from.nickname, bold(&content)),
- MsgType::Ctcp => format!("{} emoted: {}", from.nickname, bold(&content["ACTION ".len()..]))
+ MsgType::Ctcp => format!("{} emoted: {}", from.nickname,
+ bold(&content["ACTION ".len()..]))
};
// only send to channels, to prevent recursion when we are pm'ed
// technically, there are other prefixes than '#', but ignoring them is fine
if to.starts_with("#") {
- server.send(PRIVMSG(to, Owned(response))).unwrap();
+ server.msg(&to, &response);
}
},
- _ => ()
- }
-
- match Reply::from_message(msg) {
- Some(RPL_WELCOME(_)) => {
- server.send(JOIN(vec![Borrowed("#botzoo")], vec![])).unwrap();
+ Some(Event::Reply(RPL_WELCOME(_))) => {
+ server.join("#botzoo", None);
},
_ => ()
}
}
-#[cfg(feature = "ssl")]
-fn connect(s: &mut Client) {
- let ssl = Ssl::new(&SslContext::new(SslMethod::Tlsv1).unwrap()).unwrap();
- s.connect_ssl("irc.mozilla.org".to_owned(), 6697, ssl).unwrap();
-}
-
-#[cfg(not(feature = "ssl"))]
-fn connect(s: &mut Client) {
- s.connect("irc.mozilla.org".to_owned(), 6667).unwrap();
-}
-
fn main() {
env_logger::init().unwrap();
let mut s = Client::new();
- connect(&mut s);
- s.send(NICK(Borrowed(NAME))).unwrap();
- s.send(USER(Borrowed(NAME), Borrowed("*"), Borrowed("*"), Borrowed(DESC))).unwrap();
+ let ssl = Ssl::new(&SslContext::new(SslMethod::Tlsv1).unwrap()).unwrap();
+ s.connect_ssl("irc.mozilla.org", 6697, ssl);
+ s.register(NAME, NAME, DESC);
// Dedicate this thread to listening and event processing
- s.listen(callback).unwrap();
+ s.listen(Some(callback));
}
```
diff --git a/examples/01.rs b/examples/01.rs
index 96848f6..2f82839 100644
--- a/examples/01.rs
+++ b/examples/01.rs
@@ -1,67 +1,47 @@
extern crate irsc;
extern crate env_logger;
-#[cfg(feature = "ssl")]
extern crate openssl;
-use std::borrow::ToOwned;
-use std::borrow::Cow::*;
-
-use irsc::client::Client;
use irsc::color::bold;
use irsc::*;
use irsc::Command::*;
use irsc::Reply::*;
-#[cfg(feature = "ssl")]
use openssl::ssl::{ Ssl, SslContext, SslMethod };
static NAME: &'static str = "rusticbot";
static DESC: &'static str = "A bot, written in Rust.";
-fn callback(server: &mut Client, msg: &Message) {
- match Command::from_message(msg) {
- Some(PRIVMSG(to, content)) => {
- let from = msg.prefix().and_then(Ident::parse).unwrap();
+fn callback(server: &mut Client, msg: &Message, event: Option<Event>) {
+ match event {
+ Some(Event::Command(PRIVMSG(to, content))) => {
+ let from = msg.ident().unwrap();
let response = match msg.msg_type {
MsgType::Irc => format!("{} wrote: {}", from.nickname, bold(&content)),
- MsgType::Ctcp => format!("{} emoted: {}", from.nickname, bold(&content["ACTION ".len()..]))
+ MsgType::Ctcp => format!("{} emoted: {}", from.nickname,
+ bold(&content["ACTION ".len()..]))
};
// only send to channels, to prevent recursion when we are pm'ed
// technically, there are other prefixes than '#', but ignoring them is fine
if to.starts_with("#") {
- server.send(PRIVMSG(to, Owned(response))).unwrap();
+ server.msg(&to, &response);
}
},
- _ => ()
- }
-
- match Reply::from_message(msg) {
- Some(RPL_WELCOME(_)) => {
- server.send(JOIN(vec![Borrowed("#botzoo")], vec![])).unwrap();
+ Some(Event::Reply(RPL_WELCOME(_))) => {
+ server.join("#botzoo", None);
},
_ => ()
}
}
-#[cfg(feature = "ssl")]
-fn connect(s: &mut Client) {
- let ssl = Ssl::new(&SslContext::new(SslMethod::Tlsv1).unwrap()).unwrap();
- s.connect_ssl("irc.mozilla.org".to_owned(), 6697, ssl).unwrap();
-}
-
-#[cfg(not(feature = "ssl"))]
-fn connect(s: &mut Client) {
- s.connect("irc.mozilla.org".to_owned(), 6667).unwrap();
-}
-
fn main() {
env_logger::init().unwrap();
let mut s = Client::new();
- connect(&mut s);
- s.send(NICK(Borrowed(NAME))).unwrap();
- s.send(USER(Borrowed(NAME), Borrowed("*"), Borrowed("*"), Borrowed(DESC))).unwrap();
+ let ssl = Ssl::new(&SslContext::new(SslMethod::Tlsv1).unwrap()).unwrap();
+ s.connect_ssl("irc.mozilla.org", 6697, ssl);
+ s.register(NAME, NAME, DESC);
// Dedicate this thread to listening and event processing
- s.listen(callback).unwrap();
+ s.listen(Some(callback));
}
diff --git a/src/client.rs b/src/client.rs
index e573596..d830e1e 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -11,6 +11,9 @@ use std::borrow::Cow;
use message::Message;
use command::Command;
+use command::Command::*;
+use reply::Reply;
+use event::Event;
use ::{ DEBUG, Result, IrscError };
#[cfg(feature = "ssl")]
@@ -64,35 +67,38 @@ impl Client {
fn handle_event(&mut self, msg: &Message) {
let _ = match Command::from_message(msg) {
- Some(Command::PING(s1, s2)) => self.send(Command::PONG(s1, s2)),
- _ => Ok(())
+ Some(PING(s1, s2)) => self.send(PONG(s1, s2)),
+ _ => Result(Ok(()))
};
}
- pub fn connect(&mut self, host: String, port: u16) -> Result<()> {
+ pub fn connect(&mut self, host: &str, port: u16) -> Result<()> {
let s = &mut self.stream;
- match *s { Some(_) => return Err(IrscError::AlreadyConnected), _ => () };
- *s = match TcpStream::connect((host.as_ref(), port)) {
+ if s.is_some() { return Result(Err(IrscError::AlreadyConnected)) }
+ *s = match TcpStream::connect((host, port)) {
Ok(tcp) => Some(StreamKind::Plain(tcp)),
- Err(e) => return Err(IrscError::Io(e))
+ Err(e) => return Result(Err(IrscError::Io(e)))
};
- Ok(())
+ Result(Ok(()))
}
#[cfg(feature = "ssl")]
- pub fn connect_ssl(&mut self, host: String, port: u16, ssl: Ssl) -> Result<()> {
+ pub fn connect_ssl(&mut self, host: &str, port: u16, ssl: Ssl) -> Result<()> {
let s = &mut self.stream;
- match *s { Some(_) => return Err(IrscError::AlreadyConnected), _ => () };
- let tcp_stream = match TcpStream::connect((host.as_ref(), port)) {
+ if s.is_some() { return Result(Err(IrscError::AlreadyConnected)) };
+ let tcp_stream = match TcpStream::connect((host, port)) {
Ok(tcp) => Some(tcp),
- Err(e) => return Err(IrscError::Io(e))
+ Err(e) => return Result(Err(IrscError::Io(e)))
};
match tcp_stream.map(|tcp| SslStream::new_from(ssl, tcp)) {
- Some(Ok(ssl_stream)) => { *s = Some(StreamKind::Ssl(ssl_stream)); Ok(()) },
- Some(Err(ssl_error)) => Err(IrscError::Ssl(ssl_error)),
- None => Err(IrscError::NotConnected)
+ Some(Ok(ssl_stream)) => {
+ *s = Some(StreamKind::Ssl(ssl_stream));
+ Result(Ok(()))
+ },
+ Some(Err(ssl_error)) => Result(Err(IrscError::Ssl(ssl_error))),
+ None => Result(Err(IrscError::NotConnected))
}
}
@@ -103,11 +109,11 @@ impl Client {
panic!("Message too long, kittens will die if this runs in release mode. Msg: {}", s)
}
- self.stream.as_mut()
+ Result(self.stream.as_mut()
.ok_or(IrscError::NotConnected)
.and_then(|mut stream| stream.write_all(s.as_bytes())
.and_then(|_| stream.flush())
- .map_err(IrscError::Io))
+ .map_err(IrscError::Io)))
}
pub fn send_message(&mut self, msg: Message) -> Result<()> {
@@ -118,13 +124,13 @@ impl Client {
self.send_message(cmd.to_message())
}
- pub fn listen<F>(&mut self, events: F) -> Result<()>
- where F: Fn(&mut Client, &Message) {
+ pub fn listen<F>(&mut self, events: Option<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()),
#[cfg(feature = "ssl")]
Some(StreamKind::Ssl(ref s)) => StreamKind::Ssl((*s).try_clone().unwrap()),
- None => return Err(IrscError::NotConnected)
+ None => return Result(Err(IrscError::NotConnected))
});
for line in reader.lines() {
@@ -132,9 +138,36 @@ impl Client {
if let Ok(msg) = line {
self.handle_event(&msg);
- events(self, &msg);
+
+ // If a callback is desired, try to parse the message
+ // into a Command or a Reply, and call back.
+ if let Some(ref on_event) = events {
+ let event = match Command::from_message(&msg) {
+ Some(m) => Some(Event::Command(m)),
+ None => match Reply::from_message(&msg) {
+ Some(r) => Some(Event::Reply(r)),
+ None => None
+ }
+ };
+
+ on_event(self, &msg, event);
+ }
}
}
- Ok(())
+ Result(Ok(()))
+ }
+
+ pub 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 msg(&mut self, to: &str, message: &str) -> Result<()> {
+ self.send_message(PRIVMSG(to.into(), message.into()).to_message())
+ }
+
+ pub fn register(&mut self, nick: &str, user: &str, desc: &str) -> Result<()> {
+ Result(self.send_message(NICK(nick.into()).to_message()).inner()
+ .and_then(|_| self.send_message(USER(user.into(), Cow::Borrowed("0"), Cow::Borrowed("*"), desc.into()).to_message()).inner()))
+
}
}
diff --git a/src/event.rs b/src/event.rs
new file mode 100644
index 0000000..70ec816
--- /dev/null
+++ b/src/event.rs
@@ -0,0 +1,9 @@
+use command;
+use reply;
+
+pub enum Event<'a> {
+ Command(command::Command<'a>),
+ Reply(reply::Reply<'a>),
+ Connected,
+ Disconnected
+}
diff --git a/src/lib.rs b/src/lib.rs
index abcbc50..168f137 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,9 +18,11 @@ pub mod callback;
pub mod message;
pub mod command;
pub mod reply;
+pub mod event;
use std::io;
use std::result;
+use std::ops::{ Deref, DerefMut };
#[cfg(feature = "ssl")]
use openssl::ssl::error::SslError;
@@ -29,6 +31,7 @@ pub use ident::Ident;
pub use message::{ Message, MsgType };
pub use command::Command;
pub use reply::Reply;
+pub use event::Event;
pub use client::Client;
#[derive(Debug)]
@@ -46,6 +49,17 @@ impl From<SslError> for IrscError {
fn from(e: SslError) -> IrscError { IrscError::Ssl(e) }
}
-pub type Result<T> = result::Result<T, IrscError>;
+pub struct Result<T>(result::Result<T, IrscError>);
+
+impl<T> Deref for Result<T> {
+ type Target = result::Result<T, IrscError>;
+ fn deref(&self) -> &result::Result<T, IrscError> { &self.0 }
+}
+
+impl<T> DerefMut for Result<T> {
+ fn deref_mut(&mut self) -> &mut result::Result<T, IrscError> { &mut self.0 }
+}
+
+impl<T> Result<T> { fn inner(self) -> result::Result<T, IrscError> { self.0 } }
pub const DEBUG: bool = cfg!(debug_assertions);
diff --git a/src/message.rs b/src/message.rs
index dd88b1f..8b897be 100644
--- a/src/message.rs
+++ b/src/message.rs
@@ -6,6 +6,7 @@ use std::borrow::{ ToOwned };
use std::ops::{ Deref, Range };
use ::IrscError;
+use ident::Ident;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum MsgType {
@@ -92,6 +93,7 @@ impl Message {
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 {