Implemented thread-safe Connection.
This commit is contained in:
parent
b2006d044d
commit
6da40f2ad3
5 changed files with 83 additions and 58 deletions
35
src/conn.rs
35
src/conn.rs
|
@ -0,0 +1,35 @@
|
|||
use std::sync::Mutex;
|
||||
use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream};
|
||||
use data::kinds::{IrcWriter, IrcReader};
|
||||
use data::message::Message;
|
||||
|
||||
pub struct Connection<T, U> where T: IrcWriter, U: IrcReader {
|
||||
writer: Mutex<T>,
|
||||
reader: Mutex<U>,
|
||||
}
|
||||
|
||||
impl Connection<BufferedWriter<TcpStream>, BufferedReader<TcpStream>> {
|
||||
pub fn connect(host: &str, port: u16) -> IoResult<Connection<BufferedWriter<TcpStream>, BufferedReader<TcpStream>>> {
|
||||
let socket = try!(TcpStream::connect(host, port));
|
||||
Ok(Connection::new(BufferedWriter::new(socket.clone()), BufferedReader::new(socket)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Connection<T, U> where T: IrcWriter, U: IrcReader {
|
||||
pub fn new(writer: T, reader: U) -> Connection<T, U> {
|
||||
Connection {
|
||||
writer: Mutex::new(writer),
|
||||
reader: Mutex::new(reader),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send(&self, msg: Message) -> IoResult<()> {
|
||||
let mut send = self.writer.lock();
|
||||
try!(send.write_str(msg.into_string()[]));
|
||||
send.flush()
|
||||
}
|
||||
|
||||
pub fn recv(&self) -> IoResult<String> {
|
||||
self.reader.lock().read_line()
|
||||
}
|
||||
}
|
38
src/data/message.rs
Normal file
38
src/data/message.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
#[deriving(Clone, PartialEq, Show)]
|
||||
pub struct Message {
|
||||
pub prefix: Option<String>,
|
||||
pub command: String,
|
||||
pub args: Vec<String>,
|
||||
pub suffix: Option<String>,
|
||||
}
|
||||
|
||||
impl Message {
|
||||
pub fn new(prefix: Option<&str>, command: &str, args: Option<Vec<&str>>, suffix: Option<&str>)
|
||||
-> Message {
|
||||
Message {
|
||||
prefix: prefix.map(|s| s.into_string()),
|
||||
command: command.into_string(),
|
||||
args: args.map_or(Vec::new(), |v| v.iter().map(|s| s.into_string()).collect()),
|
||||
suffix: suffix.map(|s| s.into_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_string(&self) -> String {
|
||||
let mut ret = String::new();
|
||||
if let Some(ref prefix) = self.prefix {
|
||||
ret.push(':');
|
||||
ret.push_str(prefix[]);
|
||||
ret.push(' ');
|
||||
}
|
||||
ret.push_str(self.command[]);
|
||||
for arg in self.args.iter() {
|
||||
ret.push(' ');
|
||||
ret.push_str(arg[]);
|
||||
}
|
||||
if let Some(ref suffix) = self.suffix {
|
||||
ret.push_str(" :");
|
||||
ret.push_str(suffix[]);
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
8
src/data/mod.rs
Normal file
8
src/data/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
pub mod kinds {
|
||||
pub trait IrcWriter: Writer + Sized + Send + 'static {}
|
||||
impl<T> IrcWriter for T where T: Writer + Sized + Send + 'static {}
|
||||
pub trait IrcReader: Buffer + Sized + Send + 'static {}
|
||||
impl<T> IrcReader for T where T: Buffer + Sized + Send + 'static {}
|
||||
}
|
||||
|
||||
pub mod message;
|
|
@ -3,12 +3,9 @@
|
|||
#![crate_type = "lib"]
|
||||
|
||||
#![feature(if_let)]
|
||||
#![feature(phase)]
|
||||
#![feature(slicing_syntax)]
|
||||
extern crate regex;
|
||||
#[phase(plugin)] extern crate regex_macros;
|
||||
extern crate serialize;
|
||||
|
||||
mod conn;
|
||||
mod conn;
|
||||
pub mod data;
|
||||
pub mod server;
|
||||
mod utils;
|
||||
|
|
53
src/utils.rs
53
src/utils.rs
|
@ -1,53 +0,0 @@
|
|||
use std::io::{InvalidInput, IoError, IoResult};
|
||||
|
||||
fn process(msg: &str) -> IoResult<(&str, &str, Vec<&str>)> {
|
||||
let reg = regex!(r"^(?::([^ ]+) )?([^ ]+)(.*)");
|
||||
let cap = match reg.captures(msg) {
|
||||
Some(x) => x,
|
||||
None => return Err(IoError {
|
||||
kind: InvalidInput,
|
||||
desc: "Failed to parse line",
|
||||
detail: None,
|
||||
}),
|
||||
};
|
||||
let source = cap.at(1);
|
||||
let command = cap.at(2);
|
||||
let args = parse_args(cap.at(3));
|
||||
Ok((source, command, args))
|
||||
}
|
||||
|
||||
fn parse_args(line: &str) -> Vec<&str> {
|
||||
let reg = regex!(r" ([^: ]+)| :([^\r\n]*)[\r\n]*$");
|
||||
reg.captures_iter(line).map(|cap| {
|
||||
match cap.at(1) {
|
||||
"" => cap.at(2),
|
||||
x => x,
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{process, parse_args};
|
||||
|
||||
#[test]
|
||||
fn process_line() {
|
||||
let res = process(":flare.to.ca.fyrechat.net 353 pickles = #pickles :pickles awe\r\n").unwrap();
|
||||
let (source, command, args) = res;
|
||||
assert_eq!(source, "flare.to.ca.fyrechat.net");
|
||||
assert_eq!(command, "353");
|
||||
assert_eq!(args, vec!["pickles", "=", "#pickles", "pickles awe"]);
|
||||
|
||||
let res = process("PING :flare.to.ca.fyrechat.net\r\n").unwrap();
|
||||
let (source, command, args) = res;
|
||||
assert_eq!(source, "");
|
||||
assert_eq!(command, "PING");
|
||||
assert_eq!(args, vec!["flare.to.ca.fyrechat.net"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn process_args() {
|
||||
let res = parse_args("PRIVMSG #vana :hi");
|
||||
assert_eq!(res, vec!["#vana", "hi"])
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue