From 55b915a75f49957eaddefa74cbbf572ad186ee2f Mon Sep 17 00:00:00 2001 From: Till Hoeppner Date: Mon, 3 Nov 2014 16:39:57 +0100 Subject: API improvements, but more copying --- src/callback.rs | 15 ++++++ src/color.rs | 3 -- src/event.rs | 33 ++++++++++++ src/events.rs | 22 -------- src/lib.rs | 5 +- src/main.rs | 36 +++++++------ src/server.rs | 156 ++++++++++++++++++++++++-------------------------------- 7 files changed, 138 insertions(+), 132 deletions(-) create mode 100644 src/callback.rs create mode 100644 src/event.rs delete mode 100644 src/events.rs 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 { + items: Vec +} + +impl Callback { + pub fn new() -> Callback { + 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; +} + +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 { + 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), - RplYourHost(Box), - RplCreated(Box), - 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, - event_sender: Option>, - event_types: HashMap, Event> + 'a> +#[deriving(Clone)] +pub struct Server { + stream: Arc>>, + pub events: Arc>>, } -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 { - 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)); } } } -- cgit v1.2.3