aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/callback.rs15
-rw-r--r--src/color.rs3
-rw-r--r--src/event.rs33
-rw-r--r--src/events.rs22
-rw-r--r--src/lib.rs5
-rw-r--r--src/main.rs36
-rw-r--r--src/server.rs156
7 files changed, 138 insertions, 132 deletions
diff --git a/src/callback.rs b/src/callback.rs
new file mode 100644
index 0000000..c62ebf8
--- /dev/null
+++ b/src/callback.rs
@@ -0,0 +1,15 @@
+pub struct Callback<A: Sized + Send> {
+ items: Vec<fn(A)>
+}
+
+impl<A: Sized + Clone + Send> Callback<A> {
+ pub fn new() -> Callback<A> {
+ Callback { items: Vec::new() }
+ }
+ pub fn register(&mut self, f: &fn(A)) {
+ self.items.push(*f)
+ }
+ pub fn fire(&self, v: &A) {
+ for _ in self.items.iter().map(|&c| c(v.clone())) {}
+ }
+}
diff --git a/src/color.rs b/src/color.rs
index 45a5eeb..d6ae141 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -17,9 +17,6 @@ pub const LIGHT_GREY: &'static str = "15";
pub const TRANSPARENT: &'static str = "99";
-
-
-
pub fn normal(s: &str) -> String {
format!("\x0F{}\x0F", s)
}
diff --git a/src/event.rs b/src/event.rs
new file mode 100644
index 0000000..7e280a9
--- /dev/null
+++ b/src/event.rs
@@ -0,0 +1,33 @@
+use ident::Ident;
+
+#[deriving(Clone)]
+pub struct Event {
+ pub prefix: String,
+ pub command: String,
+ pub content: String
+}
+
+pub trait ParseResult {
+ fn parse(event: Event) -> Option<Self>;
+}
+
+pub const PRIVMSG: &'static str = "PRIVMSG";
+
+pub struct PrivMsg {
+ pub from: Ident,
+ pub to: String,
+ pub content: String
+}
+
+impl ParseResult for PrivMsg {
+ fn parse(event: Event) -> Option<PrivMsg> {
+ let from = Ident::parse(event.prefix[]);
+ match from {
+ Some(from) => Some(PrivMsg {
+ from: from,
+ content: event.content
+ }),
+ None => None
+ }
+ }
+}
diff --git a/src/events.rs b/src/events.rs
deleted file mode 100644
index 8c474d7..0000000
--- a/src/events.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use ident::Ident;
-
-macro_rules! string_record(
- ($name: ident, $( $fields: ident ),*) => (
- #[deriving(Show, Clone)]
- pub struct $name {
- $(pub $fields: String),*
- }
- )
-)
-
-string_record!(Welcome, source, target, msg)
-string_record!(YourHost, source, target, msg)
-string_record!(Created, source, target, msg)
-
-#[deriving(Show, Clone)]
-pub enum Event {
- RplWelcome(Box<Welcome>),
- RplYourHost(Box<YourHost>),
- RplCreated(Box<Created>),
- PrivMsg(Ident, String, String)
-}
diff --git a/src/lib.rs b/src/lib.rs
index fcb0096..a6125d7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,10 +1,11 @@
-#![feature(globs, phase, slicing_syntax, macro_rules)]
+#![feature(globs, phase, slicing_syntax, macro_rules, unboxed_closures)]
#[phase(plugin)]
extern crate regex_macros;
extern crate regex;
pub mod server;
-pub mod events;
pub mod color;
pub mod ident;
+pub mod callback;
+pub mod event;
diff --git a/src/main.rs b/src/main.rs
index e9965d8..865234a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,32 +3,36 @@
extern crate irsc;
use irsc::server::Server;
-use irsc::events::*;
use irsc::color::bold;
+use irsc::event;
+use irsc::event::{ Event, ParseResult, PrivMsg };
static NAME: &'static str = "rusticbot";
static DESC: &'static str = "A bot, written in Rust.";
+fn callback(arg: (Server, Event)) {
+ let (mut server, event) = arg;
+ match event.command[] {
+ event::PRIVMSG => {
+ let privmsg: PrivMsg = ParseResult::parse(event).unwrap();
+ let response = format!("You wrote: {}", bold(privmsg.content[]));
+ server.msg(privmsg.from.nickname[], response[]).unwrap();
+ },
+ _ => ()
+ }
+}
+
fn main() {
- let mut s = Server::new("irc.freenode.org".into_string(), 6667);
- let events = s.events();
- s.connect().unwrap();
+ let mut s = Server::new();
+ s.connect("irc.freenode.org".into_string(), 6667).unwrap();
s.nick(NAME).unwrap();
s.user(NAME, "*", "*", DESC).unwrap();
s.join("#botzoo").unwrap();
s.msg("flan3002", "Hey!").unwrap();
- for e in events.iter() {
- match e {
- RplWelcome(welcome) => {
- println!("{}", welcome)
- },
- PrivMsg(from, _to, msg) => {
- let response = format!("You wrote: {}", bold(msg[]));
- s.msg(from.nickname[], response[]).unwrap();
- }
- _ => ()
- }
- }
+ s.events.lock().register(&callback);
+
+ // Dedicate this thread to listening and event processing
+ s.listen().unwrap();
}
diff --git a/src/server.rs b/src/server.rs
index 71012ba..277c77e 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -3,98 +3,76 @@ use std::io::{
TcpStream,
IoError
};
-use std::ascii::StrAsciiExt;
-use std::comm;
-use std::comm::{ Sender, Receiver };
-use std::collections::HashMap;
-use std::str::UnicodeStrSlice;
+use std::sync::Arc;
+use std::sync::Mutex;
-use events::*;
-
-use ident::Ident;
-
-fn parse_msg(v: Vec<&str>, from: uint) -> String {
- let mut msg = if v[from].chars().next().unwrap() == ':' {
- v[from][1..].into_string()
- } else { v[from].into_string() };
- for m in v.iter().skip(from + 1) {
- msg.push_str(" ");
- msg.push_str(m.trim_right());
- }
- msg
-}
+use callback::Callback;
+use event::Event;
#[deriving(Show, PartialEq, Eq, Clone)]
pub enum Failure {
+ AlreadyConnected,
NotConnected,
Io(IoError)
}
-pub struct Context<'a> {
- prefix: &'a str,
- command: &'a str,
- parts: [&'a str]
-}
-
-pub struct Server<'a> {
- host: String,
- port: u16,
- stream: Option<TcpStream>,
- event_sender: Option<Sender<Event>>,
- event_types: HashMap<String, &'a Fn<Context<'a>, Event> + 'a>
+#[deriving(Clone)]
+pub struct Server {
+ stream: Arc<Mutex<Option<TcpStream>>>,
+ pub events: Arc<Mutex<Callback<(Server, Event)>>>,
}
-impl<'a> Server<'a> {
- pub fn new(host: String, port: u16) -> Server<'a> {
+impl Server {
+ pub fn new() -> Server {
Server {
- host: host,
- port: port,
- stream: None,
- event_sender: None,
- event_types: HashMap::new()
+ stream: Arc::new(Mutex::new(None)),
+ events: {
+ let mut c = Callback::new();
+ c.register(&Server::handle_event);
+ Arc::new(Mutex::new(c))
+ }
}
}
- pub fn events(&mut self) -> Receiver<Event> {
- let (tx, rx) = comm::channel();
- self.events = Some(tx);
- rx
- }
-
- fn fire_event(&mut self, event: Event) {
- self.events.as_ref().map(|s| s.send(event.clone()));
+ fn handle_event(arg: (Server, Event)) {
+ let (mut server, event) = arg;
+ match event.command[] {
+ "PING" => {
+ server.sendraw(format!("PONG {}", event.content).as_slice(), true).unwrap();
+ }
+ _ => ()
+ }
}
- pub fn connect(&mut self) -> Result<(), Failure> {
- self.stream = match TcpStream::connect(self.host.as_slice(), self.port) {
+ pub fn connect(&mut self, host: String, port: u16) -> Result<(), Failure> {
+ let mut s = self.stream.lock();
+ match *s { Some(_) => return Err(AlreadyConnected), _ => () };
+ *s = match TcpStream::connect(host.as_slice(), port) {
Ok(tcp) => Some(tcp),
Err(e) => return Err(Io(e))
};
- let mut s = self.clone();
- spawn(proc() {
- s.listen();
- });
Ok(())
}
#[inline]
fn sendraw(&mut self, s: &str, newline: bool) -> Result<(), Failure> {
println!("{}", s);
- if self.stream.is_some() {
- let mut st = self.stream.clone().unwrap();
- match st.write_str(s) {
- Ok(_) => match st.flush() {
- Ok(_) if newline => match st.write_str("\r\n") {
- Ok(_) => Ok(()),
+ let mut locked_stream = self.stream.lock();
+ if locked_stream.is_some() {
+ locked_stream.as_mut().map(|stream| {
+ match stream.write_str(s) {
+ Ok(_) => match { if newline { stream.write_str("\r\n") } else { Ok(()) } } {
+ Ok(_) => match stream.flush() {
+ Ok(_) => Ok(()),
+ Err(e) => return Err(Io(e))
+ },
Err(e) => return Err(Io(e))
},
- Ok(_) => Ok(()),
Err(e) => return Err(Io(e))
- },
- Err(e) => return Err(Io(e))
- }
+ }
+ }).unwrap()
} else {
Err(NotConnected)
}
@@ -120,11 +98,15 @@ impl<'a> Server<'a> {
self.sendraw(format!("PRIVMSG {} :{}", target, message).as_slice(), true)
}
- fn listen(&mut self) {
- let stream = match self.stream {
- Some(ref s) => s.clone(),
- None => return
+ pub fn listen(&mut self) -> Result<(), Failure> {
+ let stream = {
+ let lock = self.stream.lock();
+ match *lock {
+ Some(ref s) => s.clone(),
+ None => return Err(NotConnected)
+ }
};
+
let mut reader = BufferedReader::new(stream);
loop {
let line = reader.read_line().unwrap();
@@ -139,30 +121,26 @@ impl<'a> Server<'a> {
parts.remove(0).unwrap()
} else { "" };
- let cmd = parts.remove(0).unwrap();
- let context = Context { prefix: prefix, cmd: cmd, parts: parts };
- self.events.entry(cmd).call(&context);
-
- /*match parts[0].to_ascii_upper().as_slice() {
- "001" => {
- self.fire_event(RplWelcome(box Welcome {
- source: prefix.into_string(),
- target: parts[1].into_string(),
- msg: parse_msg(parts, 2)
- }))
- },
- "PING" => {
- let _ = self.sendraw(format!("PONG {}", parts.get(1)).as_slice(), true);
- continue;
+
+ fn join(v: Vec<&str>, from: uint) -> String {
+ let mut msg = if v[from].chars().next().unwrap() == ':' {
+ v[from][1..].into_string()
+ } else { v[from].into_string() };
+ for m in v.iter().skip(from + 1) {
+ msg.push_str(" ");
+ msg.push_str(m.trim_right());
}
- "PRIVMSG" => {
- let from = Ident::parse(prefix).unwrap();
- let to = parts[1];
- let msg = parse_msg(parts, 2);
- self.fire_event(PrivMsg(from, to.into_string(), msg))
- },
- _ => ()
- }*/
+ msg
+ }
+
+ let cmd = parts.remove(0).unwrap();
+ let event = Event {
+ prefix: prefix.into_string(),
+ command: cmd.into_string(),
+ content: join(parts, 0)
+ };
+
+ self.events.lock().fire(&(self.clone(), event));
}
}
}