Added support for non-unicode encodings.
This commit is contained in:
parent
77a324b0e5
commit
098f9dbbff
10 changed files with 117 additions and 44 deletions
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
|
||||
name = "irc"
|
||||
version = "0.2.1"
|
||||
version = "0.3.0"
|
||||
description = "A simple, thread-safe IRC client library."
|
||||
authors = ["Aaron Weiss <aaronweiss74@gmail.com>"]
|
||||
license = "Unlicense"
|
||||
|
@ -14,6 +14,10 @@ readme = "README.md"
|
|||
|
||||
ssl = ["openssl"]
|
||||
|
||||
[dependencies.encoding]
|
||||
|
||||
encoding = "~0.2.4"
|
||||
|
||||
[dependencies.openssl]
|
||||
|
||||
openssl = "~0.2.0"
|
||||
|
|
|
@ -18,6 +18,7 @@ fn main() {
|
|||
server: "irc.fyrechat.net".into_string(),
|
||||
port: 6667,
|
||||
use_ssl: false,
|
||||
encoding: format!("UTF-8"),
|
||||
channels: vec!("#vana".into_string()),
|
||||
options: HashMap::new(),
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ fn main() {
|
|||
server: "irc.fyrechat.net".into_string(),
|
||||
port: 6667,
|
||||
use_ssl: false,
|
||||
encoding: format!("UTF-8"),
|
||||
channels: vec!("#vana".into_string()),
|
||||
options: HashMap::new(),
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ fn main() {
|
|||
server: "irc.fyrechat.net".into_string(),
|
||||
port: 6697,
|
||||
use_ssl: true,
|
||||
encoding: format!("UTF-8"),
|
||||
channels: vec!("#vana".into_string()),
|
||||
options: HashMap::new(),
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ fn main() {
|
|||
server: "irc.fyrechat.net".into_string(),
|
||||
port: 6667,
|
||||
use_ssl: false,
|
||||
encoding: format!("UTF-8"),
|
||||
channels: vec!("#vana".into_string()),
|
||||
options: HashMap::new(),
|
||||
};
|
||||
|
|
|
@ -1 +1 @@
|
|||
echo "{\"owners\": [\"test\"],\"nickname\": \"test\",\"username\": \"test\",\"realname\": \"test\",\"password\": \"\",\"server\": \"irc.test.net\",\"port\": 6667,\"use_ssl\": false,\"channels\": [\"#test\", \"#test2\"],\"options\": {}}" > config.json
|
||||
echo "{\"owners\": [\"test\"],\"nickname\": \"test\",\"username\": \"test\",\"realname\": \"test\",\"password\": \"\",\"server\": \"irc.test.net\",\"port\": 6667,\"use_ssl\": false,\"encoding\": \"UTF-8\",\"channels\": [\"#test\", \"#test2\"],\"options\": {}}" > config.json
|
||||
|
|
133
src/conn.rs
133
src/conn.rs
|
@ -1,8 +1,9 @@
|
|||
//! Thread-safe connections on IrcStreams.
|
||||
#![experimental]
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
use std::io::{BufferedStream, IoResult, MemWriter, TcpStream};
|
||||
#[cfg(feature = "ssl")] use std::io::{IoError, OtherIoError};
|
||||
use std::io::{BufferedStream, IoError, IoErrorKind, IoResult, MemWriter, TcpStream};
|
||||
use encoding::{DecoderTrap, EncoderTrap, Encoding};
|
||||
use encoding::label::encoding_from_whatwg_label;
|
||||
use data::kinds::{IrcReader, IrcStream, IrcWriter};
|
||||
use data::message::Message;
|
||||
#[cfg(feature = "ssl")] use openssl::ssl::{SslContext, SslStream, Tlsv1};
|
||||
|
@ -74,6 +75,69 @@ impl Connection<BufferedStream<TcpStream>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: IrcStream> Connection<T> {
|
||||
/// Creates a new connection from any arbitrary IrcStream.
|
||||
#[experimental]
|
||||
pub fn new(stream: T) -> Connection<T> {
|
||||
Connection {
|
||||
stream: Mutex::new(stream),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a Message over this connection.
|
||||
#[experimental]
|
||||
pub fn send(&self, message: Message, encoding: &str) -> IoResult<()> {
|
||||
let encoding = match encoding_from_whatwg_label(encoding) {
|
||||
Some(enc) => enc,
|
||||
None => return Err(IoError {
|
||||
kind: IoErrorKind::InvalidInput,
|
||||
desc: "Failed to find decoder.",
|
||||
detail: Some(format!("Invalid decoder: {}", encoding))
|
||||
})
|
||||
};
|
||||
let data = match encoding.encode(message.into_string()[], EncoderTrap::Strict) {
|
||||
Ok(data) => data,
|
||||
Err(data) => return Err(IoError {
|
||||
kind: IoErrorKind::InvalidInput,
|
||||
desc: "Failed to decode message.",
|
||||
detail: Some(format!("Failed to decode {} as {}.", data, encoding.name())),
|
||||
})
|
||||
};
|
||||
let mut stream = self.stream.lock();
|
||||
try!(stream.write(data[]));
|
||||
stream.flush()
|
||||
}
|
||||
|
||||
/// Receives a single line from this connection.
|
||||
#[experimental]
|
||||
pub fn recv(&self, encoding: &str) -> IoResult<String> {
|
||||
let encoding = match encoding_from_whatwg_label(encoding) {
|
||||
Some(enc) => enc,
|
||||
None => return Err(IoError {
|
||||
kind: IoErrorKind::InvalidInput,
|
||||
desc: "Failed to find decoder.",
|
||||
detail: Some(format!("Invalid decoder: {}", encoding))
|
||||
})
|
||||
};
|
||||
self.stream.lock().read_until(b'\n').and_then(|line|
|
||||
match encoding.decode(line[], DecoderTrap::Strict) {
|
||||
Ok(data) => Ok(data),
|
||||
Err(data) => Err(IoError {
|
||||
kind: IoErrorKind::InvalidInput,
|
||||
desc: "Failed to decode message.",
|
||||
detail: Some(format!("Failed to decode {} as {}.", data, encoding.name())),
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Acquires the Stream lock.
|
||||
#[experimental]
|
||||
pub fn stream<'a>(&'a self) -> MutexGuard<'a, T> {
|
||||
self.stream.lock()
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a Result<T, SslError> into an IoResult<T>.
|
||||
#[cfg(feature = "ssl")]
|
||||
fn ssl_to_io<T>(res: Result<T, SslError>) -> IoResult<T> {
|
||||
|
@ -118,36 +182,6 @@ impl Writer for NetStream {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: IrcStream> Connection<T> {
|
||||
/// Creates a new connection from any arbitrary IrcStream.
|
||||
#[experimental]
|
||||
pub fn new(stream: T) -> Connection<T> {
|
||||
Connection {
|
||||
stream: Mutex::new(stream),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a Message over this connection.
|
||||
#[experimental]
|
||||
pub fn send(&self, message: Message) -> IoResult<()> {
|
||||
let mut stream = self.stream.lock();
|
||||
try!(stream.write_str(message.into_string()[]));
|
||||
stream.flush()
|
||||
}
|
||||
|
||||
/// Receives a single line from this connection.
|
||||
#[experimental]
|
||||
pub fn recv(&self) -> IoResult<String> {
|
||||
self.stream.lock().read_line()
|
||||
}
|
||||
|
||||
/// Acquires the Stream lock.
|
||||
#[experimental]
|
||||
pub fn stream<'a>(&'a self) -> MutexGuard<'a, T> {
|
||||
self.stream.lock()
|
||||
}
|
||||
}
|
||||
|
||||
/// An IrcStream built from an IrcWriter and an IrcReader.
|
||||
#[experimental]
|
||||
pub struct IoStream<T: IrcWriter, U: IrcReader> {
|
||||
|
@ -197,22 +231,43 @@ mod test {
|
|||
use std::io::{MemReader, MemWriter};
|
||||
use std::io::util::{NullReader, NullWriter};
|
||||
use data::message::Message;
|
||||
use encoding::{DecoderTrap, Encoding};
|
||||
use encoding::all::ISO_8859_15;
|
||||
|
||||
#[test]
|
||||
fn send() {
|
||||
fn send_utf8() {
|
||||
let conn = Connection::new(IoStream::new(MemWriter::new(), NullReader));
|
||||
assert!(conn.send(
|
||||
Message::new(None, "PRIVMSG", Some(vec!["test"]), Some("Testing!"))
|
||||
Message::new(None, "PRIVMSG", Some(vec!["test"]), Some("€ŠšŽžŒœŸ")), "l9"
|
||||
).is_ok());
|
||||
let data = String::from_utf8(conn.stream().value()).unwrap();
|
||||
assert_eq!(data[], "PRIVMSG test :Testing!\r\n");
|
||||
let data = ISO_8859_15.decode(conn.stream().value()[], DecoderTrap::Strict).unwrap();
|
||||
assert_eq!(data[], "PRIVMSG test :€ŠšŽžŒœŸ\r\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recv() {
|
||||
fn send_iso885915() {
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recv_utf8() {
|
||||
let conn = Connection::new(IoStream::new(
|
||||
NullWriter, MemReader::new("PRIVMSG test :Testing!\r\n".as_bytes().to_vec())
|
||||
NullWriter, MemReader::new(b"PRIVMSG test :Testing!\r\n".to_vec())
|
||||
));
|
||||
assert_eq!(conn.recv().unwrap()[], "PRIVMSG test :Testing!\r\n");
|
||||
assert_eq!(conn.recv("UTF-8").unwrap()[], "PRIVMSG test :Testing!\r\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recv_iso885915() {
|
||||
let conn = Connection::new(IoStream::new(
|
||||
NullWriter, MemReader::new({
|
||||
let mut vec = Vec::new();
|
||||
vec.push_all(b"PRIVMSG test :");
|
||||
vec.push_all(&[0xA4, 0xA6, 0xA8, 0xB4, 0xB8, 0xBC, 0xBD, 0xBE]);
|
||||
vec.push_all(b"\r\n");
|
||||
vec
|
||||
})
|
||||
));
|
||||
assert_eq!(conn.recv("l9").unwrap()[], "PRIVMSG test :€ŠšŽžŒœŸ\r\n");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ pub struct Config {
|
|||
/// Whether or not to use SSL.
|
||||
/// Bots will automatically panic if this is enabled without SSL support.
|
||||
pub use_ssl: bool,
|
||||
/// The encoding type used for this connection.
|
||||
/// This is typically UTF-8, but could be something else.
|
||||
pub encoding: String,
|
||||
/// A list of channels to join on connection.
|
||||
pub channels: Vec<String>,
|
||||
/// A map of additional options to be stored in config.
|
||||
|
@ -81,6 +84,7 @@ mod test {
|
|||
server: format!("irc.test.net"),
|
||||
port: 6667,
|
||||
use_ssl: false,
|
||||
encoding: format!("UTF-8"),
|
||||
channels: vec![format!("#test"), format!("#test2")],
|
||||
options: HashMap::new(),
|
||||
};
|
||||
|
@ -98,6 +102,7 @@ mod test {
|
|||
server: format!("irc.test.net"),
|
||||
port: 6667,
|
||||
use_ssl: false,
|
||||
encoding: format!("UTF-8"),
|
||||
channels: vec![format!("#test"), format!("#test2")],
|
||||
options: HashMap::new(),
|
||||
};
|
||||
|
@ -115,6 +120,7 @@ mod test {
|
|||
server: format!("irc.test.net"),
|
||||
port: 6667,
|
||||
use_ssl: false,
|
||||
encoding: format!("UTF-8"),
|
||||
channels: Vec::new(),
|
||||
options: HashMap::new(),
|
||||
};
|
||||
|
@ -134,6 +140,7 @@ mod test {
|
|||
server: format!("irc.test.net"),
|
||||
port: 6667,
|
||||
use_ssl: false,
|
||||
encoding: format!("UTF-8"),
|
||||
channels: vec![format!("#test"), format!("#test2")],
|
||||
options: {
|
||||
let mut map = HashMap::new();
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#![unstable]
|
||||
|
||||
#![feature(if_let, slicing_syntax)]
|
||||
extern crate encoding;
|
||||
extern crate serialize;
|
||||
#[cfg(feature = "ssl")] extern crate openssl;
|
||||
|
||||
|
|
|
@ -63,7 +63,8 @@ impl IrcServer<BufferedStream<NetStream>> {
|
|||
}
|
||||
|
||||
/// Creates a new IRC server connection from the specified configuration with the specified
|
||||
/// timeout in milliseconds, connecting immediately.
|
||||
/// timeout in milliseconds, connecting
|
||||
/// immediately.
|
||||
#[experimental]
|
||||
pub fn from_config_with_timeout(config: Config, timeout_ms: u64)
|
||||
-> IoResult<IrcServer<BufferedStream<NetStream>>> {
|
||||
|
@ -83,7 +84,7 @@ impl<'a, T> Server<'a, T> for IrcServer<T> where T: IrcStream {
|
|||
}
|
||||
|
||||
fn send(&self, command: Command) -> IoResult<()> {
|
||||
self.conn.send(command.to_message())
|
||||
self.conn.send(command.to_message(), self.config.encoding[])
|
||||
}
|
||||
|
||||
fn iter(&'a self) -> ServerIterator<'a, T> {
|
||||
|
@ -176,7 +177,7 @@ impl<'a, T> ServerIterator<'a, T> where T: IrcStream {
|
|||
|
||||
impl<'a, T> Iterator<Message> for ServerIterator<'a, T> where T: IrcStream {
|
||||
fn next(&mut self) -> Option<Message> {
|
||||
let line = self.server.conn.recv();
|
||||
let line = self.server.conn.recv(self.server.config.encoding[]);
|
||||
match line {
|
||||
Err(_) => None,
|
||||
Ok(msg) => {
|
||||
|
@ -209,6 +210,7 @@ mod test {
|
|||
server: format!("irc.test.net"),
|
||||
port: 6667,
|
||||
use_ssl: false,
|
||||
encoding: format!("UTF-8"),
|
||||
channels: vec![format!("#test"), format!("#test2")],
|
||||
options: HashMap::new(),
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue