Implemented thread-safe Connection.

This commit is contained in:
Aaron Weiss 2014-11-02 17:25:45 -05:00
parent b2006d044d
commit 6da40f2ad3
5 changed files with 83 additions and 58 deletions

View file

@ -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
View 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
View 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;

View file

@ -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;

View file

@ -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"])
}
}