#[cfg(test)]
use super::MIN_ALLOWED_LINE_SIZE;
#[cfg(test)]
use std::error::Error;
#[cfg(test)]
use std::fs::File;
#[cfg(test)]
use std::fs::OpenOptions;
use std::io::Error as IoError;
use std::io::Result as IoResult;
use std::io::{ErrorKind, Read, Write};
#[cfg(test)]
use std::iter::{repeat, FromIterator};
use std::ops::{IndexMut, RangeFrom};
use std::vec::Vec;
pub static LINE_TOO_LONG: &str = "line too long";
pub static DATA_TOO_LONG: &str = "message too long";
#[test]
fn test_static_vars() {
}
pub struct InputStream<S> {
stream: S,
max_line_size: usize,
buf: Vec<u8>,
debug: bool,
last_crlf: Option<usize>,
}
enum CRLFState {
Cr,
Lf,
}
fn position_crlf(buf: &[u8]) -> Option<usize> {
let mut state = CRLFState::Cr;
let mut index = 0;
for byte in buf.iter() {
match state {
CRLFState::Cr => {
if byte == &13 {
state = CRLFState::Lf;
}
}
CRLFState::Lf => {
if byte == &10 {
return Some(index - 1);
}
}
}
index += 1;
}
None
}
impl<S: Read> InputStream<S> {
pub fn new(inner: S, max_line_size: usize, debug: bool) -> InputStream<S> {
InputStream {
stream: inner,
max_line_size: max_line_size,
buf: Vec::with_capacity(max_line_size),
debug: debug,
last_crlf: None,
}
}
pub fn move_buf(&mut self) {
match self.last_crlf {
Some(p) => {
self.buf = self.buf[p + 2..].to_vec();
self.buf.reserve(self.max_line_size);
}
_ => {}
}
self.last_crlf = None;
}
fn fill_buf(&mut self) -> IoResult<usize> {
let len = self.buf.len();
let cap = self.buf.capacity();
unsafe { self.buf.set_len(cap) };
match self
.stream
.read(self.buf.index_mut(RangeFrom { start: len }))
{
Ok(num_bytes) => {
unsafe { self.buf.set_len(len + num_bytes) };
Ok(num_bytes)
}
Err(err) => {
unsafe { self.buf.set_len(len) };
Err(err)
}
}
}
pub fn read_line(&mut self) -> IoResult<&[u8]> {
self.move_buf();
let read_line = match position_crlf(self.buf.as_ref()) {
Some(last_crlf) => {
self.last_crlf = Some(last_crlf);
Ok(&self.buf[..last_crlf])
}
None => {
match self.fill_buf() {
Ok(_) => {
match position_crlf(self.buf.as_ref()) {
Some(last_crlf) => {
self.last_crlf = Some(last_crlf);
Ok(&self.buf[..last_crlf])
}
None => {
Err(IoError::new(ErrorKind::InvalidInput, LINE_TOO_LONG))
}
}
}
Err(err) => Err(err),
}
}
};
if let Ok(bytes) = read_line {
if self.debug {
println!("rsmtp: imsg: {}", String::from_utf8_lossy(bytes.as_ref()));
}
}
read_line
}
}
pub struct OutputStream<S> {
stream: S,
debug: bool,
}
impl<S: Write> OutputStream<S> {
pub fn new(inner: S, debug: bool) -> OutputStream<S> {
OutputStream {
stream: inner,
debug: debug,
}
}
pub fn write_line(&mut self, s: &str) -> IoResult<()> {
if self.debug {
println!("rsmtp: omsg: {}", s);
}
write!(&mut self.stream, "{}\r\n", s)
}
}
#[test]
fn test_new() {
}
#[test]
fn test_write_line() {
{
let file_write: File;
let mut stream: OutputStream<File>;
file_write = OpenOptions::new()
.truncate(true)
.write(true)
.open("tests/stream/write_line")
.unwrap();
stream = OutputStream::new(file_write, false);
stream.write_line("HelloWorld").unwrap();
stream.write_line("ByeBye").unwrap();
}
let mut file_read: File;
let mut expected = String::new();
file_read = OpenOptions::new()
.read(true)
.open("tests/stream/write_line")
.unwrap();
file_read.read_to_string(&mut expected).unwrap();
assert_eq!("HelloWorld\r\nByeBye\r\n", expected.as_str());
}
#[test]
fn test_limits() {
let file: File;
let mut stream: InputStream<File>;
file = OpenOptions::new()
.read(true)
.open("tests/stream/1line1")
.unwrap();
stream = InputStream::new(file, 3, false);
match stream.read_line() {
Ok(_) => panic!(),
Err(err) => {
assert_eq!("line too long", err.to_string());
assert_eq!(ErrorKind::InvalidInput, err.kind());
}
}
}
#[test]
fn test_read_line() {
let mut file: File;
let mut stream: InputStream<File>;
let expected: String;
file = OpenOptions::new()
.read(true)
.open("tests/stream/0line1")
.unwrap();
stream = InputStream::new(file, MIN_ALLOWED_LINE_SIZE, false);
assert!(!stream.read_line().is_ok());
file = OpenOptions::new()
.read(true)
.open("tests/stream/0line2")
.unwrap();
stream = InputStream::new(file, MIN_ALLOWED_LINE_SIZE, false);
assert!(!stream.read_line().is_ok());
file = OpenOptions::new()
.read(true)
.open("tests/stream/0line3")
.unwrap();
stream = InputStream::new(file, MIN_ALLOWED_LINE_SIZE, false);
assert!(!stream.read_line().is_ok());
file = OpenOptions::new()
.read(true)
.open("tests/stream/1line1")
.unwrap();
stream = InputStream::new(file, MIN_ALLOWED_LINE_SIZE, false);
assert_eq!(
String::from_utf8_lossy(stream.read_line().unwrap().as_ref())
.to_owned()
.as_ref(),
"hello world!"
);
assert!(!stream.read_line().is_ok());
file = OpenOptions::new()
.read(true)
.open("tests/stream/1line2")
.unwrap();
stream = InputStream::new(file, MIN_ALLOWED_LINE_SIZE, false);
assert_eq!(
String::from_utf8_lossy(stream.read_line().unwrap().as_ref())
.to_owned()
.as_ref(),
"hello world!"
);
assert!(!stream.read_line().is_ok());
file = OpenOptions::new()
.read(true)
.open("tests/stream/2lines1")
.unwrap();
stream = InputStream::new(file, MIN_ALLOWED_LINE_SIZE, false);
assert_eq!(
String::from_utf8_lossy(stream.read_line().unwrap().as_ref())
.to_owned()
.as_ref(),
"hello world!"
);
assert_eq!(
String::from_utf8_lossy(stream.read_line().unwrap().as_ref())
.to_owned()
.as_ref(),
"bye bye world!"
);
assert!(!stream.read_line().is_ok());
expected = String::from_iter(repeat('x').take(62));
file = OpenOptions::new()
.read(true)
.open("tests/stream/xlines1")
.unwrap();
stream = InputStream::new(file, MIN_ALLOWED_LINE_SIZE, false);
assert_eq!(
String::from_utf8_lossy(stream.read_line().unwrap().as_ref()).to_owned(),
expected
);
assert_eq!(
String::from_utf8_lossy(stream.read_line().unwrap().as_ref()).to_owned(),
expected
);
assert_eq!(
String::from_utf8_lossy(stream.read_line().unwrap().as_ref()).to_owned(),
expected
);
assert_eq!(
String::from_utf8_lossy(stream.read_line().unwrap().as_ref()).to_owned(),
expected
);
assert_eq!(
String::from_utf8_lossy(stream.read_line().unwrap().as_ref()).to_owned(),
expected
);
assert_eq!(
String::from_utf8_lossy(stream.read_line().unwrap().as_ref()).to_owned(),
expected
);
assert_eq!(
String::from_utf8_lossy(stream.read_line().unwrap().as_ref()).to_owned(),
expected
);
assert_eq!(
String::from_utf8_lossy(stream.read_line().unwrap().as_ref()).to_owned(),
expected
);
assert_eq!(
String::from_utf8_lossy(stream.read_line().unwrap().as_ref()).to_owned(),
expected
);
assert!(!stream.read_line().is_ok());
}