rustfmt the codebase (#36)

This will ensure that we are properly formatting this library code according to rust standards
This commit is contained in:
Matt McCoy 2017-07-10 21:38:13 -04:00 committed by GitHub
parent 62cef4a773
commit 86e1d46507
8 changed files with 802 additions and 651 deletions

View file

@ -22,7 +22,11 @@ use imap::client::Client;
// See: https://support.google.com/accounts/answer/6010255?hl=en // See: https://support.google.com/accounts/answer/6010255?hl=en
// Look at the gmail_oauth2.rs example on how to connect to a gmail server securely. // Look at the gmail_oauth2.rs example on how to connect to a gmail server securely.
fn main() { fn main() {
let mut imap_socket = Client::secure_connect(("imap.gmail.com", 993), "imap.gmail.com", SslConnectorBuilder::new(SslMethod::tls()).unwrap().build()).unwrap(); let domain = "imap.gmail.com";
let port = 993;
let socket_addr = (domain, port);
let ssl_connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap().build();
let mut imap_socket = Client::secure_connect(socket_addr, domain, ssl_connector).unwrap();
imap_socket.login("username", "password").unwrap(); imap_socket.login("username", "password").unwrap();
@ -31,15 +35,15 @@ fn main() {
for capability in capabilities.iter() { for capability in capabilities.iter() {
println!("{}", capability); println!("{}", capability);
} }
}, }
Err(e) => println!("Error parsing capability: {}", e) Err(e) => println!("Error parsing capability: {}", e),
}; };
match imap_socket.select("INBOX") { match imap_socket.select("INBOX") {
Ok(mailbox) => { Ok(mailbox) => {
println!("{}", mailbox); println!("{}", mailbox);
}, }
Err(e) => println!("Error selecting INBOX: {}", e) Err(e) => println!("Error selecting INBOX: {}", e),
}; };
match imap_socket.fetch("2", "body[text]") { match imap_socket.fetch("2", "body[text]") {
@ -47,8 +51,8 @@ fn main() {
for line in lines.iter() { for line in lines.iter() {
print!("{}", line); print!("{}", line);
} }
}, }
Err(e) => println!("Error Fetching email 2: {}", e) Err(e) => println!("Error Fetching email 2: {}", e),
}; };
imap_socket.logout().unwrap(); imap_socket.logout().unwrap();

View file

@ -8,7 +8,11 @@ use imap::client::Client;
// See: https://support.google.com/accounts/answer/6010255?hl=en // See: https://support.google.com/accounts/answer/6010255?hl=en
// Look at the gmail_oauth2.rs example on how to connect to a gmail server securely. // Look at the gmail_oauth2.rs example on how to connect to a gmail server securely.
fn main() { fn main() {
let mut imap_socket = Client::secure_connect(("imap.gmail.com", 993), "imap.gmail.com", SslConnectorBuilder::new(SslMethod::tls()).unwrap().build()).unwrap(); let domain = "imap.gmail.com";
let port = 993;
let socket_addr = (domain, port);
let ssl_connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap().build();
let mut imap_socket = Client::secure_connect(socket_addr, domain, ssl_connector).unwrap();
imap_socket.login("username", "password").unwrap(); imap_socket.login("username", "password").unwrap();
@ -17,15 +21,15 @@ fn main() {
for capability in capabilities.iter() { for capability in capabilities.iter() {
println!("{}", capability); println!("{}", capability);
} }
}, }
Err(e) => println!("Error parsing capability: {}", e) Err(e) => println!("Error parsing capability: {}", e),
}; };
match imap_socket.select("INBOX") { match imap_socket.select("INBOX") {
Ok(mailbox) => { Ok(mailbox) => {
println!("{}", mailbox); println!("{}", mailbox);
}, }
Err(e) => println!("Error selecting INBOX: {}", e) Err(e) => println!("Error selecting INBOX: {}", e),
}; };
match imap_socket.fetch("2", "body[text]") { match imap_socket.fetch("2", "body[text]") {
@ -33,8 +37,8 @@ fn main() {
for line in lines.iter() { for line in lines.iter() {
print!("{}", line); print!("{}", line);
} }
}, }
Err(e) => println!("Error Fetching email 2: {}", e) Err(e) => println!("Error Fetching email 2: {}", e),
}; };
imap_socket.logout().unwrap(); imap_socket.logout().unwrap();

View file

@ -3,34 +3,44 @@ extern crate openssl;
extern crate base64; extern crate base64;
use openssl::ssl::{SslConnectorBuilder, SslMethod}; use openssl::ssl::{SslConnectorBuilder, SslMethod};
use base64::{encode}; use base64::encode;
use imap::client::Client; use imap::client::Client;
use imap::authenticator::Authenticator; use imap::authenticator::Authenticator;
struct GmailOAuth2 { struct GmailOAuth2 {
user: String, user: String,
access_token: String access_token: String,
} }
impl Authenticator for GmailOAuth2 { impl Authenticator for GmailOAuth2 {
#[allow(unused_variables)] #[allow(unused_variables)]
fn process(&self, data: String) -> String { fn process(&self, data: String) -> String {
encode(format!("user={}\x01auth=Bearer {}\x01\x01", self.user, self.access_token).as_bytes()) encode(
format!(
"user={}\x01auth=Bearer {}\x01\x01",
self.user,
self.access_token
).as_bytes(),
)
} }
} }
fn main() { fn main() {
let gmail_auth = GmailOAuth2 { let gmail_auth = GmailOAuth2 {
user: String::from("sombody@gmail.com"), user: String::from("sombody@gmail.com"),
access_token: String::from("<access_token>") access_token: String::from("<access_token>"),
}; };
let mut imap_socket = Client::secure_connect(("imap.gmail.com", 993), "imap.gmail.com", SslConnectorBuilder::new(SslMethod::tls()).unwrap().build()).unwrap(); let domain = "imap.gmail.com";
let port = 993;
let socket_addr = (domain, port);
let ssl_connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap().build();
let mut imap_socket = Client::secure_connect(socket_addr, domain, ssl_connector).unwrap();
imap_socket.authenticate("XOAUTH2", gmail_auth).unwrap(); imap_socket.authenticate("XOAUTH2", gmail_auth).unwrap();
match imap_socket.select("INBOX") { match imap_socket.select("INBOX") {
Ok(mailbox) => println!("{}", mailbox), Ok(mailbox) => println!("{}", mailbox),
Err(e) => println!("Error selecting INBOX: {}", e) Err(e) => println!("Error selecting INBOX: {}", e),
}; };
match imap_socket.fetch("2", "body[text]") { match imap_socket.fetch("2", "body[text]") {
@ -38,8 +48,8 @@ fn main() {
for line in lines.iter() { for line in lines.iter() {
print!("{}", line); print!("{}", line);
} }
}, }
Err(e) => println!("Error Fetching email 2: {}", e) Err(e) => println!("Error Fetching email 2: {}", e),
}; };
imap_socket.logout().unwrap(); imap_socket.logout().unwrap();

View file

@ -5,7 +5,8 @@ use std::time::Duration;
use super::mailbox::Mailbox; use super::mailbox::Mailbox;
use super::authenticator::Authenticator; use super::authenticator::Authenticator;
use super::parse::{parse_response_ok, parse_capability, parse_select_or_examine, parse_response, parse_authenticate_response}; use super::parse::{parse_response_ok, parse_capability, parse_select_or_examine, parse_response,
parse_authenticate_response};
use super::error::{Error, Result}; use super::error::{Error, Result};
static TAG_PREFIX: &'static str = "a"; static TAG_PREFIX: &'static str = "a";
@ -17,7 +18,7 @@ const LF: u8 = 0x0a;
pub struct Client<T> { pub struct Client<T> {
stream: T, stream: T,
tag: u32, tag: u32,
pub debug: bool pub debug: bool,
} }
/// `IdleHandle` allows a client to block waiting for changes to the remote mailbox. /// `IdleHandle` allows a client to block waiting for changes to the remote mailbox.
@ -122,8 +123,8 @@ impl<'a, T: SetReadTimeout + Read + Write + 'a> IdleHandle<'a, T> {
// though it need only "poll" at half hour intervals. // though it need only "poll" at half hour intervals.
try!(self.client.stream.set_read_timeout(Some(self.keepalive))); try!(self.client.stream.set_read_timeout(Some(self.keepalive)));
match self.wait() { match self.wait() {
Err(Error::Io(ref e)) if e.kind() == io::ErrorKind::TimedOut || Err(Error::Io(ref e))
e.kind() == io::ErrorKind::WouldBlock => { if e.kind() == io::ErrorKind::TimedOut || e.kind() == io::ErrorKind::WouldBlock => {
// we need to refresh the IDLE connection // we need to refresh the IDLE connection
try!(self.terminate()); try!(self.terminate());
try!(self.init()); try!(self.init());
@ -147,7 +148,8 @@ impl<'a, T: SetReadTimeout + Read + Write + 'a> IdleHandle<'a, T> {
impl<'a, T: Read + Write + 'a> Drop for IdleHandle<'a, T> { impl<'a, T: Read + Write + 'a> Drop for IdleHandle<'a, T> {
fn drop(&mut self) { fn drop(&mut self) {
self.terminate().expect("IDLE connection did not terminate cleanly"); self.terminate()
.expect("IDLE connection did not terminate cleanly");
} }
} }
@ -159,7 +161,9 @@ impl<'a> SetReadTimeout for TcpStream {
impl<'a> SetReadTimeout for SslStream<TcpStream> { impl<'a> SetReadTimeout for SslStream<TcpStream> {
fn set_read_timeout(&mut self, timeout: Option<Duration>) -> Result<()> { fn set_read_timeout(&mut self, timeout: Option<Duration>) -> Result<()> {
self.get_ref().set_read_timeout(timeout).map_err(|e| Error::Io(e)) self.get_ref()
.set_read_timeout(timeout)
.map_err(|e| Error::Io(e))
} }
} }
@ -172,15 +176,19 @@ impl Client<TcpStream> {
try!(socket.read_greeting()); try!(socket.read_greeting());
Ok(socket) Ok(socket)
}, }
Err(e) => Err(Error::Io(e)) Err(e) => Err(Error::Io(e)),
} }
} }
/// This will upgrade a regular TCP connection to use SSL. /// This will upgrade a regular TCP connection to use SSL.
/// ///
/// Use the domain parameter for openssl's SNI and hostname verification. /// Use the domain parameter for openssl's SNI and hostname verification.
pub fn secure(mut self, domain: &str,ssl_connector: SslConnector) -> Result<Client<SslStream<TcpStream>>> { pub fn secure(
mut self,
domain: &str,
ssl_connector: SslConnector,
) -> Result<Client<SslStream<TcpStream>>> {
// TODO This needs to be tested // TODO This needs to be tested
self.run_command_and_check_ok("STARTTLS")?; self.run_command_and_check_ok("STARTTLS")?;
SslConnector::connect(&ssl_connector, domain, self.stream) SslConnector::connect(&ssl_connector, domain, self.stream)
@ -191,37 +199,46 @@ impl Client<TcpStream> {
impl Client<SslStream<TcpStream>> { impl Client<SslStream<TcpStream>> {
/// Creates a client with an SSL wrapper. /// Creates a client with an SSL wrapper.
pub fn secure_connect<A: ToSocketAddrs>(addr: A, domain: &str,ssl_connector: SslConnector) -> Result<Client<SslStream<TcpStream>>> { pub fn secure_connect<A: ToSocketAddrs>(
addr: A,
domain: &str,
ssl_connector: SslConnector,
) -> Result<Client<SslStream<TcpStream>>> {
match TcpStream::connect(addr) { match TcpStream::connect(addr) {
Ok(stream) => { Ok(stream) => {
let ssl_stream = match SslConnector::connect(&ssl_connector, domain, stream) { let ssl_stream = match SslConnector::connect(&ssl_connector, domain, stream) {
Ok(s) => s, Ok(s) => s,
Err(e) => return Err(Error::Ssl(e)) Err(e) => return Err(Error::Ssl(e)),
}; };
let mut socket = Client::new(ssl_stream); let mut socket = Client::new(ssl_stream);
try!(socket.read_greeting()); try!(socket.read_greeting());
Ok(socket) Ok(socket)
}, }
Err(e) => Err(Error::Io(e)) Err(e) => Err(Error::Io(e)),
} }
} }
} }
impl<T: Read + Write> Client<T> { impl<T: Read + Write> Client<T> {
/// Creates a new client with the underlying stream. /// Creates a new client with the underlying stream.
pub fn new(stream: T) -> Client<T> { pub fn new(stream: T) -> Client<T> {
Client { Client {
stream: stream, stream: stream,
tag: INITIAL_TAG, tag: INITIAL_TAG,
debug: false debug: false,
} }
} }
/// Authenticate will authenticate with the server, using the authenticator given. /// Authenticate will authenticate with the server, using the authenticator given.
pub fn authenticate<A: Authenticator>(&mut self, auth_type: &str, authenticator: A) -> Result<()> { pub fn authenticate<A: Authenticator>(
try!(self.run_command(&format!("AUTHENTICATE {}", auth_type).to_string())); &mut self,
auth_type: &str,
authenticator: A,
) -> Result<()> {
try!(self.run_command(
&format!("AUTHENTICATE {}", auth_type).to_string()
));
self.do_auth_handshake(authenticator) self.do_auth_handshake(authenticator)
} }
@ -232,7 +249,9 @@ impl<T: Read+Write> Client<T> {
let line = try!(self.readline()); let line = try!(self.readline());
if line.starts_with(b"+") { if line.starts_with(b"+") {
let data = try!(parse_authenticate_response(String::from_utf8(line).unwrap())); let data = try!(parse_authenticate_response(
String::from_utf8(line).unwrap()
));
let auth_response = authenticator.process(data); let auth_response = authenticator.process(data);
try!(self.write_line(auth_response.into_bytes().as_slice())) try!(self.write_line(auth_response.into_bytes().as_slice()))
@ -256,17 +275,17 @@ impl<T: Read+Write> Client<T> {
/// Selects a mailbox /// Selects a mailbox
pub fn select(&mut self, mailbox_name: &str) -> Result<Mailbox> { pub fn select(&mut self, mailbox_name: &str) -> Result<Mailbox> {
let lines = try!( let lines = try!(self.run_command_and_read_response(
self.run_command_and_read_response(&format!("SELECT {}", mailbox_name).to_string()) &format!("SELECT {}", mailbox_name).to_string()
); ));
parse_select_or_examine(lines) parse_select_or_examine(lines)
} }
/// Examine is identical to Select, but the selected mailbox is identified as read-only /// Examine is identical to Select, but the selected mailbox is identified as read-only
pub fn examine(&mut self, mailbox_name: &str) -> Result<Mailbox> { pub fn examine(&mut self, mailbox_name: &str) -> Result<Mailbox> {
let lines = try!( let lines = try!(self.run_command_and_read_response(
self.run_command_and_read_response(&format!("EXAMINE {}", mailbox_name).to_string()) &format!("EXAMINE {}", mailbox_name).to_string()
); ));
parse_select_or_examine(lines) parse_select_or_examine(lines)
} }
@ -301,7 +320,11 @@ impl<T: Read+Write> Client<T> {
/// Rename changes the name of a mailbox. /// Rename changes the name of a mailbox.
pub fn rename(&mut self, current_mailbox_name: &str, new_mailbox_name: &str) -> Result<()> { pub fn rename(&mut self, current_mailbox_name: &str, new_mailbox_name: &str) -> Result<()> {
self.run_command_and_check_ok(&format!("RENAME {} {}", current_mailbox_name, new_mailbox_name).to_string()) self.run_command_and_check_ok(&format!(
"RENAME {} {}",
current_mailbox_name,
new_mailbox_name
).to_string())
} }
/// Subscribe adds the specified mailbox name to the server's set of "active" or "subscribed" /// Subscribe adds the specified mailbox name to the server's set of "active" or "subscribed"
@ -310,17 +333,17 @@ impl<T: Read+Write> Client<T> {
self.run_command_and_check_ok(&format!("SUBSCRIBE {}", mailbox).to_string()) self.run_command_and_check_ok(&format!("SUBSCRIBE {}", mailbox).to_string())
} }
/// Unsubscribe removes the specified mailbox name from the server's set of "active" or "subscribed" /// Unsubscribe removes the specified mailbox name from the server's set of
/// mailboxes as returned by the LSUB command. /// "active" or "subscribed mailboxes as returned by the LSUB command.
pub fn unsubscribe(&mut self, mailbox: &str) -> Result<()> { pub fn unsubscribe(&mut self, mailbox: &str) -> Result<()> {
self.run_command_and_check_ok(&format!("UNSUBSCRIBE {}", mailbox).to_string()) self.run_command_and_check_ok(&format!("UNSUBSCRIBE {}", mailbox).to_string())
} }
/// Capability requests a listing of capabilities that the server supports. /// Capability requests a listing of capabilities that the server supports.
pub fn capability(&mut self) -> Result<Vec<String>> { pub fn capability(&mut self) -> Result<Vec<String>> {
let lines = try!( let lines = try!(self.run_command_and_read_response(
self.run_command_and_read_response(&format!("CAPABILITY").to_string()) &format!("CAPABILITY").to_string()
); ));
parse_capability(lines) parse_capability(lines)
} }
@ -352,7 +375,8 @@ impl<T: Read+Write> Client<T> {
/// Copy copies the specified message to the end of the specified destination mailbox. /// Copy copies the specified message to the end of the specified destination mailbox.
pub fn copy(&mut self, sequence_set: &str, mailbox_name: &str) -> Result<()> { pub fn copy(&mut self, sequence_set: &str, mailbox_name: &str) -> Result<()> {
self.run_command_and_check_ok(&format!("COPY {} {}", sequence_set, mailbox_name).to_string()) self.run_command_and_check_ok(&format!("COPY {} {}", sequence_set, mailbox_name)
.to_string())
} }
pub fn uid_copy(&mut self, uid_set: &str, mailbox_name: &str) -> Result<()> { pub fn uid_copy(&mut self, uid_set: &str, mailbox_name: &str) -> Result<()> {
@ -361,14 +385,30 @@ impl<T: Read+Write> Client<T> {
/// The LIST command returns a subset of names from the complete set /// The LIST command returns a subset of names from the complete set
/// of all names available to the client. /// of all names available to the client.
pub fn list(&mut self, reference_name: &str, mailbox_search_pattern: &str) -> Result<Vec<String>> { pub fn list(
self.run_command_and_parse(&format!("LIST {} {}", reference_name, mailbox_search_pattern)) &mut self,
reference_name: &str,
mailbox_search_pattern: &str,
) -> Result<Vec<String>> {
self.run_command_and_parse(&format!(
"LIST {} {}",
reference_name,
mailbox_search_pattern
))
} }
/// The LSUB command returns a subset of names from the set of names /// The LSUB command returns a subset of names from the set of names
/// that the user has declared as being "active" or "subscribed". /// that the user has declared as being "active" or "subscribed".
pub fn lsub(&mut self, reference_name: &str, mailbox_search_pattern: &str) -> Result<Vec<String>> { pub fn lsub(
self.run_command_and_parse(&format!("LSUB {} {}", reference_name, mailbox_search_pattern)) &mut self,
reference_name: &str,
mailbox_search_pattern: &str,
) -> Result<Vec<String>> {
self.run_command_and_parse(&format!(
"LSUB {} {}",
reference_name,
mailbox_search_pattern
))
} }
/// The STATUS command requests the status of the indicated mailbox. /// The STATUS command requests the status of the indicated mailbox.
@ -384,7 +424,9 @@ impl<T: Read+Write> Client<T> {
/// The APPEND command adds a mail to a mailbox. /// The APPEND command adds a mail to a mailbox.
pub fn append(&mut self, folder: &str, content: &[u8]) -> Result<Vec<String>> { pub fn append(&mut self, folder: &str, content: &[u8]) -> Result<Vec<String>> {
try!(self.run_command(&format!("APPEND \"{}\" {{{}}}", folder, content.len()))); try!(self.run_command(
&format!("APPEND \"{}\" {{{}}}", folder, content.len())
));
let line = try!(self.readline()); let line = try!(self.readline());
if !line.starts_with(b"+") { if !line.starts_with(b"+") {
return Err(Error::Append); return Err(Error::Append);
@ -441,7 +483,9 @@ impl<T: Read+Write> Client<T> {
fn readline(&mut self) -> Result<Vec<u8>> { fn readline(&mut self) -> Result<Vec<u8>> {
let mut line_buffer: Vec<u8> = Vec::new(); let mut line_buffer: Vec<u8> = Vec::new();
while line_buffer.len() < 2 || (line_buffer[line_buffer.len()-1] != LF && line_buffer[line_buffer.len()-2] != CR) { while line_buffer.len() < 2 ||
(line_buffer[line_buffer.len() - 1] != LF && line_buffer[line_buffer.len() - 2] != CR)
{
let byte_buffer: &mut [u8] = &mut [0]; let byte_buffer: &mut [u8] = &mut [0];
let n = try!(self.stream.read(byte_buffer)); let n = try!(self.stream.read(byte_buffer));
if n > 0 { if n > 0 {
@ -489,7 +533,10 @@ mod tests {
let mock_stream = MockStream::new(response.as_bytes().to_vec()); let mock_stream = MockStream::new(response.as_bytes().to_vec());
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
let actual_response = client.read_response().unwrap(); let actual_response = client.read_response().unwrap();
assert!(expected_response == actual_response, "expected response doesn't equal actual"); assert!(
expected_response == actual_response,
"expected response doesn't equal actual"
);
} }
@ -528,11 +575,17 @@ mod tests {
let expected_command = format!("a1 {}", base_command); let expected_command = format!("a1 {}", base_command);
let command = imap_stream.create_command(String::from(base_command)); let command = imap_stream.create_command(String::from(base_command));
assert!(command == expected_command, "expected command doesn't equal actual command"); assert!(
command == expected_command,
"expected command doesn't equal actual command"
);
let expected_command2 = format!("a2 {}", base_command); let expected_command2 = format!("a2 {}", base_command);
let command2 = imap_stream.create_command(String::from(base_command)); let command2 = imap_stream.create_command(String::from(base_command));
assert!(command2 == expected_command2, "expected command doesn't equal actual command"); assert!(
command2 == expected_command2,
"expected command doesn't equal actual command"
);
} }
#[test] #[test]
@ -544,7 +597,10 @@ mod tests {
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.login(username, password).unwrap(); client.login(username, password).unwrap();
assert!(client.stream.written_buf == command.as_bytes().to_vec(), "Invalid login command"); assert!(
client.stream.written_buf == command.as_bytes().to_vec(),
"Invalid login command"
);
} }
#[test] #[test]
@ -554,7 +610,10 @@ mod tests {
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.logout().unwrap(); client.logout().unwrap();
assert!(client.stream.written_buf == command.as_bytes().to_vec(), "Invalid logout command"); assert!(
client.stream.written_buf == command.as_bytes().to_vec(),
"Invalid logout command"
);
} }
#[test] #[test]
@ -562,11 +621,20 @@ mod tests {
let response = b"a1 OK RENAME completed\r\n".to_vec(); let response = b"a1 OK RENAME completed\r\n".to_vec();
let current_mailbox_name = "INBOX"; let current_mailbox_name = "INBOX";
let new_mailbox_name = "NEWINBOX"; let new_mailbox_name = "NEWINBOX";
let command = format!("a1 RENAME {} {}\r\n", current_mailbox_name, new_mailbox_name); let command = format!(
"a1 RENAME {} {}\r\n",
current_mailbox_name,
new_mailbox_name
);
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.rename(current_mailbox_name, new_mailbox_name).unwrap(); client
assert!(client.stream.written_buf == command.as_bytes().to_vec(), "Invalid rename command"); .rename(current_mailbox_name, new_mailbox_name)
.unwrap();
assert!(
client.stream.written_buf == command.as_bytes().to_vec(),
"Invalid rename command"
);
} }
#[test] #[test]
@ -577,7 +645,10 @@ mod tests {
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.subscribe(mailbox).unwrap(); client.subscribe(mailbox).unwrap();
assert!(client.stream.written_buf == command.as_bytes().to_vec(), "Invalid subscribe command"); assert!(
client.stream.written_buf == command.as_bytes().to_vec(),
"Invalid subscribe command"
);
} }
#[test] #[test]
@ -588,7 +659,10 @@ mod tests {
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.unsubscribe(mailbox).unwrap(); client.unsubscribe(mailbox).unwrap();
assert!(client.stream.written_buf == command.as_bytes().to_vec(), "Invalid unsubscribe command"); assert!(
client.stream.written_buf == command.as_bytes().to_vec(),
"Invalid unsubscribe command"
);
} }
#[test] #[test]
@ -597,7 +671,10 @@ mod tests {
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.expunge().unwrap(); client.expunge().unwrap();
assert!(client.stream.written_buf == b"a1 EXPUNGE\r\n".to_vec(), "Invalid expunge command"); assert!(
client.stream.written_buf == b"a1 EXPUNGE\r\n".to_vec(),
"Invalid expunge command"
);
} }
#[test] #[test]
@ -606,7 +683,10 @@ mod tests {
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.check().unwrap(); client.check().unwrap();
assert!(client.stream.written_buf == b"a1 CHECK\r\n".to_vec(), "Invalid check command"); assert!(
client.stream.written_buf == b"a1 CHECK\r\n".to_vec(),
"Invalid check command"
);
} }
#[test] #[test]
@ -618,7 +698,8 @@ mod tests {
* OK [UNSEEN 1] First unseen.\r\n\ * OK [UNSEEN 1] First unseen.\r\n\
* OK [UIDVALIDITY 1257842737] UIDs valid\r\n\ * OK [UIDVALIDITY 1257842737] UIDs valid\r\n\
* OK [UIDNEXT 2] Predicted next UID\r\n\ * OK [UIDNEXT 2] Predicted next UID\r\n\
a1 OK [READ-ONLY] Select completed.\r\n".to_vec(); a1 OK [READ-ONLY] Select completed.\r\n"
.to_vec();
let expected_mailbox = Mailbox { let expected_mailbox = Mailbox {
flags: String::from("(\\Answered \\Flagged \\Deleted \\Seen \\Draft)"), flags: String::from("(\\Answered \\Flagged \\Deleted \\Seen \\Draft)"),
exists: 1, exists: 1,
@ -626,14 +707,17 @@ mod tests {
unseen: Some(1), unseen: Some(1),
permanent_flags: Some(String::from("()")), permanent_flags: Some(String::from("()")),
uid_next: Some(2), uid_next: Some(2),
uid_validity: Some(1257842737) uid_validity: Some(1257842737),
}; };
let mailbox_name = "INBOX"; let mailbox_name = "INBOX";
let command = format!("a1 EXAMINE {}\r\n", mailbox_name); let command = format!("a1 EXAMINE {}\r\n", mailbox_name);
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
let mailbox = client.examine(mailbox_name).unwrap(); let mailbox = client.examine(mailbox_name).unwrap();
assert!(client.stream.written_buf == command.as_bytes().to_vec(), "Invalid examine command"); assert!(
client.stream.written_buf == command.as_bytes().to_vec(),
"Invalid examine command"
);
assert!(mailbox == expected_mailbox, "Unexpected mailbox returned"); assert!(mailbox == expected_mailbox, "Unexpected mailbox returned");
} }
@ -646,35 +730,48 @@ mod tests {
* OK [UNSEEN 1] First unseen.\r\n\ * OK [UNSEEN 1] First unseen.\r\n\
* OK [UIDVALIDITY 1257842737] UIDs valid\r\n\ * OK [UIDVALIDITY 1257842737] UIDs valid\r\n\
* OK [UIDNEXT 2] Predicted next UID\r\n\ * OK [UIDNEXT 2] Predicted next UID\r\n\
a1 OK [READ-ONLY] Select completed.\r\n".to_vec(); a1 OK [READ-ONLY] Select completed.\r\n"
.to_vec();
let expected_mailbox = Mailbox { let expected_mailbox = Mailbox {
flags: String::from("(\\Answered \\Flagged \\Deleted \\Seen \\Draft)"), flags: String::from("(\\Answered \\Flagged \\Deleted \\Seen \\Draft)"),
exists: 1, exists: 1,
recent: 1, recent: 1,
unseen: Some(1), unseen: Some(1),
permanent_flags: Some(String::from("(\\* \\Answered \\Flagged \\Deleted \\Draft \\Seen)")), permanent_flags: Some(String::from(
"(\\* \\Answered \\Flagged \\Deleted \\Draft \\Seen)",
)),
uid_next: Some(2), uid_next: Some(2),
uid_validity: Some(1257842737) uid_validity: Some(1257842737),
}; };
let mailbox_name = "INBOX"; let mailbox_name = "INBOX";
let command = format!("a1 SELECT {}\r\n", mailbox_name); let command = format!("a1 SELECT {}\r\n", mailbox_name);
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
let mailbox = client.select(mailbox_name).unwrap(); let mailbox = client.select(mailbox_name).unwrap();
assert!(client.stream.written_buf == command.as_bytes().to_vec(), "Invalid select command"); assert!(
client.stream.written_buf == command.as_bytes().to_vec(),
"Invalid select command"
);
assert!(mailbox == expected_mailbox, "Unexpected mailbox returned"); assert!(mailbox == expected_mailbox, "Unexpected mailbox returned");
} }
#[test] #[test]
fn capability() { fn capability() {
let response = b"* CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED\r\n\ let response = b"* CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED\r\n\
a1 OK CAPABILITY completed\r\n".to_vec(); a1 OK CAPABILITY completed\r\n"
.to_vec();
let expected_capabilities = vec!["IMAP4rev1", "STARTTLS", "AUTH=GSSAPI", "LOGINDISABLED"]; let expected_capabilities = vec!["IMAP4rev1", "STARTTLS", "AUTH=GSSAPI", "LOGINDISABLED"];
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
let capabilities = client.capability().unwrap(); let capabilities = client.capability().unwrap();
assert!(client.stream.written_buf == b"a1 CAPABILITY\r\n".to_vec(), "Invalid capability command"); assert!(
assert!(capabilities == expected_capabilities, "Unexpected capabilities response"); client.stream.written_buf == b"a1 CAPABILITY\r\n".to_vec(),
"Invalid capability command"
);
assert!(
capabilities == expected_capabilities,
"Unexpected capabilities response"
);
} }
#[test] #[test]
@ -685,7 +782,10 @@ mod tests {
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.create(mailbox_name).unwrap(); client.create(mailbox_name).unwrap();
assert!(client.stream.written_buf == command.as_bytes().to_vec(), "Invalid create command"); assert!(
client.stream.written_buf == command.as_bytes().to_vec(),
"Invalid create command"
);
} }
#[test] #[test]
@ -696,7 +796,10 @@ mod tests {
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.delete(mailbox_name).unwrap(); client.delete(mailbox_name).unwrap();
assert!(client.stream.written_buf == command.as_bytes().to_vec(), "Invalid delete command"); assert!(
client.stream.written_buf == command.as_bytes().to_vec(),
"Invalid delete command"
);
} }
#[test] #[test]
@ -705,7 +808,10 @@ mod tests {
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.noop().unwrap(); client.noop().unwrap();
assert!(client.stream.written_buf == b"a1 NOOP\r\n".to_vec(), "Invalid noop command"); assert!(
client.stream.written_buf == b"a1 NOOP\r\n".to_vec(),
"Invalid noop command"
);
} }
#[test] #[test]
@ -714,7 +820,10 @@ mod tests {
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.close().unwrap(); client.close().unwrap();
assert!(client.stream.written_buf == b"a1 CLOSE\r\n".to_vec(), "Invalid close command"); assert!(
client.stream.written_buf == b"a1 CLOSE\r\n".to_vec(),
"Invalid close command"
);
} }
#[test] #[test]
@ -728,21 +837,16 @@ mod tests {
} }
fn generic_store<F, T>(prefix: &str, op: F) fn generic_store<F, T>(prefix: &str, op: F)
where F: FnOnce(&mut Client<MockStream>, &str, &str) -> Result<T> { where
F: FnOnce(&mut Client<MockStream>, &str, &str) -> Result<T>,
{
let res = "* 2 FETCH (FLAGS (\\Deleted \\Seen))\r\n\ let res = "* 2 FETCH (FLAGS (\\Deleted \\Seen))\r\n\
* 3 FETCH (FLAGS (\\Deleted))\r\n\ * 3 FETCH (FLAGS (\\Deleted))\r\n\
* 4 FETCH (FLAGS (\\Deleted \\Flagged \\Seen))\r\n\ * 4 FETCH (FLAGS (\\Deleted \\Flagged \\Seen))\r\n\
a1 OK STORE completed\r\n"; a1 OK STORE completed\r\n";
generic_with_uid( generic_with_uid(res, "STORE", "2.4", "+FLAGS (\\Deleted)", prefix, op);
res,
"STORE",
"2.4",
"+FLAGS (\\Deleted)",
prefix,
op,
);
} }
#[test] #[test]
@ -756,7 +860,9 @@ mod tests {
} }
fn generic_copy<F, T>(prefix: &str, op: F) fn generic_copy<F, T>(prefix: &str, op: F)
where F: FnOnce(&mut Client<MockStream>, &str, &str) -> Result<T> { where
F: FnOnce(&mut Client<MockStream>, &str, &str) -> Result<T>,
{
generic_with_uid( generic_with_uid(
"OK COPY completed\r\n", "OK COPY completed\r\n",
@ -779,31 +885,25 @@ mod tests {
} }
fn generic_fetch<F, T>(prefix: &str, op: F) fn generic_fetch<F, T>(prefix: &str, op: F)
where F: FnOnce(&mut Client<MockStream>, &str, &str) -> Result<T> { where
F: FnOnce(&mut Client<MockStream>, &str, &str) -> Result<T>,
{
generic_with_uid( generic_with_uid("OK FETCH completed\r\n", "FETCH", "1", "BODY[]", prefix, op);
"OK FETCH completed\r\n",
"FETCH",
"1",
"BODY[]",
prefix,
op
);
} }
fn generic_with_uid<F, T>( fn generic_with_uid<F, T>(res: &str, cmd: &str, seq: &str, query: &str, prefix: &str, op: F)
res: &str, where
cmd: &str, F: FnOnce(&mut Client<MockStream>, &str, &str) -> Result<T>,
seq: &str,
query: &str,
prefix: &str,
op: F) where F: FnOnce(&mut Client<MockStream>, &str, &str) -> Result<T>,
{ {
let resp = format!("a1 {}\r\n", res).as_bytes().to_vec(); let resp = format!("a1 {}\r\n", res).as_bytes().to_vec();
let line = format!("a1{}{} {} {}\r\n", prefix, cmd, seq, query); let line = format!("a1{}{} {} {}\r\n", prefix, cmd, seq, query);
let mut client = Client::new(MockStream::new(resp)); let mut client = Client::new(MockStream::new(resp));
let _ = op(&mut client, seq, query); let _ = op(&mut client, seq, query);
assert!(client.stream.written_buf == line.as_bytes().to_vec(), "Invalid command"); assert!(
client.stream.written_buf == line.as_bytes().to_vec(),
"Invalid command"
);
} }
} }

View file

@ -22,7 +22,7 @@ pub enum Error {
// Error parsing a server response. // Error parsing a server response.
Parse(ParseError), Parse(ParseError),
// Error appending a mail // Error appending a mail
Append Append,
} }
impl From<IoError> for Error { impl From<IoError> for Error {
@ -55,7 +55,7 @@ impl StdError for Error {
Error::Parse(ref e) => e.description(), Error::Parse(ref e) => e.description(),
Error::BadResponse(_) => "Bad Response", Error::BadResponse(_) => "Bad Response",
Error::NoResponse(_) => "No Response", Error::NoResponse(_) => "No Response",
Error::Append => "Could not append mail to mailbox" Error::Append => "Could not append mail to mailbox",
} }
} }
@ -75,7 +75,7 @@ pub enum ParseError {
// Error parsing the cabability response. // Error parsing the cabability response.
Capability(Vec<String>), Capability(Vec<String>),
// Authentication errors. // Authentication errors.
Authentication(String) Authentication(String),
} }
impl fmt::Display for ParseError { impl fmt::Display for ParseError {
@ -91,13 +91,13 @@ impl StdError for ParseError {
match *self { match *self {
ParseError::StatusResponse(_) => "Unable to parse status response", ParseError::StatusResponse(_) => "Unable to parse status response",
ParseError::Capability(_) => "Unable to parse capability response", ParseError::Capability(_) => "Unable to parse capability response",
ParseError::Authentication(_) => "Unable to parse authentication response" ParseError::Authentication(_) => "Unable to parse authentication response",
} }
} }
fn cause(&self) -> Option<&StdError> { fn cause(&self) -> Option<&StdError> {
match *self { match *self {
_ => None _ => None,
} }
} }
} }

View file

@ -8,7 +8,7 @@ pub struct Mailbox {
pub unseen: Option<u32>, pub unseen: Option<u32>,
pub permanent_flags: Option<String>, pub permanent_flags: Option<String>,
pub uid_next: Option<u32>, pub uid_next: Option<u32>,
pub uid_validity: Option<u32> pub uid_validity: Option<u32>,
} }
impl Default for Mailbox { impl Default for Mailbox {
@ -20,13 +20,24 @@ impl Default for Mailbox {
unseen: None, unseen: None,
permanent_flags: None, permanent_flags: None,
uid_next: None, uid_next: None,
uid_validity: None uid_validity: None,
} }
} }
} }
impl fmt::Display for Mailbox { impl fmt::Display for Mailbox {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "flags: {}, exists: {}, recent: {}, unseen: {:?}, permanent_flags: {:?}, uid_next: {:?}, uid_validity: {:?}", self.flags, self.exists, self.recent, self.unseen, self.permanent_flags, self.uid_next, self.uid_validity) write!(
f,
"flags: {}, exists: {}, recent: {}, unseen: {:?}, permanent_flags: {:?},\
uid_next: {:?}, uid_validity: {:?}",
self.flags,
self.exists,
self.recent,
self.unseen,
self.permanent_flags,
self.uid_next,
self.uid_validity
)
} }
} }

View file

@ -6,7 +6,7 @@ pub struct MockStream {
read_pos: usize, read_pos: usize,
pub written_buf: Vec<u8>, pub written_buf: Vec<u8>,
err_on_read: bool, err_on_read: bool,
read_delay: usize read_delay: usize,
} }
impl MockStream { impl MockStream {
@ -16,7 +16,7 @@ impl MockStream {
read_pos: 0, read_pos: 0,
written_buf: Vec::new(), written_buf: Vec::new(),
err_on_read: false, err_on_read: false,
read_delay: 0 read_delay: 0,
} }
} }
@ -26,7 +26,7 @@ impl MockStream {
read_pos: 0, read_pos: 0,
written_buf: Vec::new(), written_buf: Vec::new(),
err_on_read: true, err_on_read: true,
read_delay: 0 read_delay: 0,
} }
} }
@ -45,13 +45,13 @@ impl Read for MockStream {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> { fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
if self.read_delay > 0 { if self.read_delay > 0 {
self.read_delay -= 1; self.read_delay -= 1;
return Ok(0) return Ok(0);
} }
if self.err_on_read { if self.err_on_read {
return Err(Error::new(ErrorKind::Other, "MockStream Error")) return Err(Error::new(ErrorKind::Other, "MockStream Error"));
} }
if self.read_pos >= self.read_buf.len() { if self.read_pos >= self.read_buf.len() {
return Err(Error::new(ErrorKind::UnexpectedEof, "EOF")) return Err(Error::new(ErrorKind::UnexpectedEof, "EOF"));
} }
let write_len = min(buf.len(), self.read_buf.len() - self.read_pos); let write_len = min(buf.len(), self.read_buf.len() - self.read_pos);
let max_pos = self.read_pos + write_len; let max_pos = self.read_pos + write_len;

View file

@ -20,7 +20,7 @@ pub fn parse_capability(lines: Vec<String>) -> Result<Vec<String>> {
//Check Ok //Check Ok
match parse_response_ok(lines.clone()) { match parse_response_ok(lines.clone()) {
Ok(_) => (), Ok(_) => (),
Err(e) => return Err(e) Err(e) => return Err(e),
}; };
for line in lines.iter() { for line in lines.iter() {
@ -37,7 +37,7 @@ pub fn parse_capability(lines: Vec<String>) -> Result<Vec<String>> {
pub fn parse_response_ok(lines: Vec<String>) -> Result<()> { pub fn parse_response_ok(lines: Vec<String>) -> Result<()> {
match parse_response(lines) { match parse_response(lines) {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(e) => return Err(e) Err(e) => return Err(e),
} }
} }
@ -45,7 +45,7 @@ pub fn parse_response(lines: Vec<String>) -> Result<Vec<String>> {
let regex = Regex::new(r"^([a-zA-Z0-9]+) (OK|NO|BAD)(.*)").unwrap(); let regex = Regex::new(r"^([a-zA-Z0-9]+) (OK|NO|BAD)(.*)").unwrap();
let last_line = match lines.last() { let last_line = match lines.last() {
Some(l) => l, Some(l) => l,
None => return Err(Error::Parse(ParseError::StatusResponse(lines.clone()))) None => return Err(Error::Parse(ParseError::StatusResponse(lines.clone()))),
}; };
for cap in regex.captures_iter(last_line) { for cap in regex.captures_iter(last_line) {
@ -79,7 +79,7 @@ pub fn parse_select_or_examine(lines: Vec<String>) -> Result<Mailbox> {
//Check Ok //Check Ok
match parse_response_ok(lines.clone()) { match parse_response_ok(lines.clone()) {
Ok(_) => (), Ok(_) => (),
Err(e) => return Err(e) Err(e) => return Err(e),
}; };
let mut mailbox = Mailbox::default(); let mut mailbox = Mailbox::default();
@ -118,22 +118,41 @@ mod tests {
#[test] #[test]
fn parse_capability_test() { fn parse_capability_test() {
let expected_capabilities = vec![String::from("IMAP4rev1"), String::from("STARTTLS"), String::from("AUTH=GSSAPI"), String::from("LOGINDISABLED")]; let expected_capabilities = vec![
let lines = vec![String::from("* CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED\r\n"), String::from("a1 OK CAPABILITY completed\r\n")]; String::from("IMAP4rev1"),
String::from("STARTTLS"),
String::from("AUTH=GSSAPI"),
String::from("LOGINDISABLED"),
];
let lines = vec![
String::from(
"* CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED\r\n",
),
String::from("a1 OK CAPABILITY completed\r\n"),
];
let capabilities = parse_capability(lines).unwrap(); let capabilities = parse_capability(lines).unwrap();
assert!(capabilities == expected_capabilities, "Unexpected capabilities parse response"); assert!(
capabilities == expected_capabilities,
"Unexpected capabilities parse response"
);
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn parse_capability_invalid_test() { fn parse_capability_invalid_test() {
let lines = vec![String::from("* JUNK IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED\r\n"), String::from("a1 OK CAPABILITY completed\r\n")]; let lines = vec![
String::from("* JUNK IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED\r\n"),
String::from("a1 OK CAPABILITY completed\r\n"),
];
parse_capability(lines).unwrap(); parse_capability(lines).unwrap();
} }
#[test] #[test]
fn parse_response_test() { fn parse_response_test() {
let lines = vec![String::from("* LIST (\\HasNoChildren) \".\" \"INBOX\"\r\n"), String::from("a2 OK List completed.\r\n")]; let lines = vec![
String::from("* LIST (\\HasNoChildren) \".\" \"INBOX\"\r\n"),
String::from("a2 OK List completed.\r\n"),
];
let expected_lines = lines.clone(); let expected_lines = lines.clone();
let actual_lines = parse_response(lines).unwrap(); let actual_lines = parse_response(lines).unwrap();
assert!(expected_lines == actual_lines, "Unexpected parse response"); assert!(expected_lines == actual_lines, "Unexpected parse response");
@ -142,7 +161,10 @@ mod tests {
#[test] #[test]
#[should_panic] #[should_panic]
fn parse_response_invalid_test() { fn parse_response_invalid_test() {
let lines = vec![String::from("* LIST (\\HasNoChildren) \".\" \"INBOX\"\r\n"), String::from("a2 BAD broken.\r\n")]; let lines = vec![
String::from("* LIST (\\HasNoChildren) \".\" \"INBOX\"\r\n"),
String::from("a2 BAD broken.\r\n"),
];
parse_response(lines).unwrap(); parse_response(lines).unwrap();
} }
} }