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
// Look at the gmail_oauth2.rs example on how to connect to a gmail server securely.
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();
@ -31,15 +35,15 @@ fn main() {
for capability in capabilities.iter() {
println!("{}", capability);
}
},
Err(e) => println!("Error parsing capability: {}", e)
}
Err(e) => println!("Error parsing capability: {}", e),
};
match imap_socket.select("INBOX") {
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]") {
@ -47,8 +51,8 @@ fn main() {
for line in lines.iter() {
print!("{}", line);
}
},
Err(e) => println!("Error Fetching email 2: {}", e)
}
Err(e) => println!("Error Fetching email 2: {}", e),
};
imap_socket.logout().unwrap();

View file

@ -8,7 +8,11 @@ use imap::client::Client;
// 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.
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();
@ -17,15 +21,15 @@ fn main() {
for capability in capabilities.iter() {
println!("{}", capability);
}
},
Err(e) => println!("Error parsing capability: {}", e)
}
Err(e) => println!("Error parsing capability: {}", e),
};
match imap_socket.select("INBOX") {
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]") {
@ -33,8 +37,8 @@ fn main() {
for line in lines.iter() {
print!("{}", line);
}
},
Err(e) => println!("Error Fetching email 2: {}", e)
}
Err(e) => println!("Error Fetching email 2: {}", e),
};
imap_socket.logout().unwrap();

View file

@ -3,34 +3,44 @@ extern crate openssl;
extern crate base64;
use openssl::ssl::{SslConnectorBuilder, SslMethod};
use base64::{encode};
use base64::encode;
use imap::client::Client;
use imap::authenticator::Authenticator;
struct GmailOAuth2 {
user: String,
access_token: String
access_token: String,
}
impl Authenticator for GmailOAuth2 {
#[allow(unused_variables)]
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() {
let gmail_auth = GmailOAuth2{
let gmail_auth = GmailOAuth2 {
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();
match imap_socket.select("INBOX") {
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]") {
@ -38,8 +48,8 @@ fn main() {
for line in lines.iter() {
print!("{}", line);
}
},
Err(e) => println!("Error Fetching email 2: {}", e)
}
Err(e) => println!("Error Fetching email 2: {}", e),
};
imap_socket.logout().unwrap();

View file

@ -5,7 +5,8 @@ use std::time::Duration;
use super::mailbox::Mailbox;
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};
static TAG_PREFIX: &'static str = "a";
@ -17,7 +18,7 @@ const LF: u8 = 0x0a;
pub struct Client<T> {
stream: T,
tag: u32,
pub debug: bool
pub debug: bool,
}
/// `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.
try!(self.client.stream.set_read_timeout(Some(self.keepalive)));
match self.wait() {
Err(Error::Io(ref e)) if e.kind() == io::ErrorKind::TimedOut ||
e.kind() == io::ErrorKind::WouldBlock => {
Err(Error::Io(ref e))
if e.kind() == io::ErrorKind::TimedOut || e.kind() == io::ErrorKind::WouldBlock => {
// we need to refresh the IDLE connection
try!(self.terminate());
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> {
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> {
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,18 +176,22 @@ impl Client<TcpStream> {
try!(socket.read_greeting());
Ok(socket)
},
Err(e) => Err(Error::Io(e))
}
Err(e) => Err(Error::Io(e)),
}
}
/// This will upgrade a regular TCP connection to use SSL.
///
/// 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
self.run_command_and_check_ok("STARTTLS")?;
SslConnector::connect(&ssl_connector,domain, self.stream)
SslConnector::connect(&ssl_connector, domain, self.stream)
.map(Client::new)
.map_err(Error::Ssl)
}
@ -191,37 +199,46 @@ impl Client<TcpStream> {
impl Client<SslStream<TcpStream>> {
/// 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) {
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,
Err(e) => return Err(Error::Ssl(e))
Err(e) => return Err(Error::Ssl(e)),
};
let mut socket = Client::new(ssl_stream);
try!(socket.read_greeting());
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.
pub fn new(stream: T) -> Client<T> {
Client{
Client {
stream: stream,
tag: INITIAL_TAG,
debug: false
debug: false,
}
}
/// Authenticate will authenticate with the server, using the authenticator given.
pub fn authenticate<A: Authenticator>(&mut self, auth_type: &str, authenticator: A) -> Result<()> {
try!(self.run_command(&format!("AUTHENTICATE {}", auth_type).to_string()));
pub fn authenticate<A: Authenticator>(
&mut self,
auth_type: &str,
authenticator: A,
) -> Result<()> {
try!(self.run_command(
&format!("AUTHENTICATE {}", auth_type).to_string()
));
self.do_auth_handshake(authenticator)
}
@ -232,7 +249,9 @@ impl<T: Read+Write> Client<T> {
let line = try!(self.readline());
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);
try!(self.write_line(auth_response.into_bytes().as_slice()))
@ -250,23 +269,23 @@ impl<T: Read+Write> Client<T> {
}
/// Log in to the IMAP server.
pub fn login(&mut self, username: & str, password: & str) -> Result<()> {
pub fn login(&mut self, username: &str, password: &str) -> Result<()> {
self.run_command_and_check_ok(&format!("LOGIN {} {}", username, password).to_string())
}
/// Selects a mailbox
pub fn select(&mut self, mailbox_name: &str) -> Result<Mailbox> {
let lines = try!(
self.run_command_and_read_response(&format!("SELECT {}", mailbox_name).to_string())
);
let lines = try!(self.run_command_and_read_response(
&format!("SELECT {}", mailbox_name).to_string()
));
parse_select_or_examine(lines)
}
/// Examine is identical to Select, but the selected mailbox is identified as read-only
pub fn examine(&mut self, mailbox_name: &str) -> Result<Mailbox> {
let lines = try!(
self.run_command_and_read_response(&format!("EXAMINE {}", mailbox_name).to_string())
);
let lines = try!(self.run_command_and_read_response(
&format!("EXAMINE {}", mailbox_name).to_string()
));
parse_select_or_examine(lines)
}
@ -301,7 +320,11 @@ impl<T: Read+Write> Client<T> {
/// Rename changes the name of a mailbox.
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"
@ -310,17 +333,17 @@ impl<T: Read+Write> Client<T> {
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"
/// mailboxes as returned by the LSUB command.
/// Unsubscribe removes the specified mailbox name from the server's set of
/// "active" or "subscribed mailboxes as returned by the LSUB command.
pub fn unsubscribe(&mut self, mailbox: &str) -> Result<()> {
self.run_command_and_check_ok(&format!("UNSUBSCRIBE {}", mailbox).to_string())
}
/// Capability requests a listing of capabilities that the server supports.
pub fn capability(&mut self) -> Result<Vec<String>> {
let lines = try!(
self.run_command_and_read_response(&format!("CAPABILITY").to_string())
);
let lines = try!(self.run_command_and_read_response(
&format!("CAPABILITY").to_string()
));
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.
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<()> {
@ -361,14 +385,30 @@ impl<T: Read+Write> Client<T> {
/// The LIST command returns a subset of names from the complete set
/// of all names available to the client.
pub fn list(&mut self, reference_name: &str, mailbox_search_pattern: &str) -> Result<Vec<String>> {
self.run_command_and_parse(&format!("LIST {} {}", reference_name, mailbox_search_pattern))
pub fn list(
&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
/// 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>> {
self.run_command_and_parse(&format!("LSUB {} {}", reference_name, mailbox_search_pattern))
pub fn lsub(
&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.
@ -384,7 +424,9 @@ impl<T: Read+Write> Client<T> {
/// The APPEND command adds a mail to a mailbox.
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());
if !line.starts_with(b"+") {
return Err(Error::Append);
@ -441,7 +483,9 @@ impl<T: Read+Write> Client<T> {
fn readline(&mut self) -> Result<Vec<u8>> {
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 n = try!(self.stream.read(byte_buffer));
if n > 0 {
@ -452,7 +496,7 @@ impl<T: Read+Write> Client<T> {
if self.debug {
let mut line = line_buffer.clone();
// Remove CRLF
line.truncate(line_buffer.len()-2);
line.truncate(line_buffer.len() - 2);
print!("S: {}\n", String::from_utf8(line).unwrap());
}
@ -489,7 +533,10 @@ mod tests {
let mock_stream = MockStream::new(response.as_bytes().to_vec());
let mut client = Client::new(mock_stream);
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 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 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]
@ -544,7 +597,10 @@ mod tests {
let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream);
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]
@ -554,7 +610,10 @@ mod tests {
let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream);
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]
@ -562,11 +621,20 @@ mod tests {
let response = b"a1 OK RENAME completed\r\n".to_vec();
let current_mailbox_name = "INBOX";
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 mut client = Client::new(mock_stream);
client.rename(current_mailbox_name, new_mailbox_name).unwrap();
assert!(client.stream.written_buf == command.as_bytes().to_vec(), "Invalid rename command");
client
.rename(current_mailbox_name, new_mailbox_name)
.unwrap();
assert!(
client.stream.written_buf == command.as_bytes().to_vec(),
"Invalid rename command"
);
}
#[test]
@ -577,7 +645,10 @@ mod tests {
let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream);
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]
@ -588,7 +659,10 @@ mod tests {
let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream);
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]
@ -597,7 +671,10 @@ mod tests {
let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream);
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]
@ -606,7 +683,10 @@ mod tests {
let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream);
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]
@ -618,7 +698,8 @@ mod tests {
* OK [UNSEEN 1] First unseen.\r\n\
* OK [UIDVALIDITY 1257842737] UIDs valid\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 {
flags: String::from("(\\Answered \\Flagged \\Deleted \\Seen \\Draft)"),
exists: 1,
@ -626,14 +707,17 @@ mod tests {
unseen: Some(1),
permanent_flags: Some(String::from("()")),
uid_next: Some(2),
uid_validity: Some(1257842737)
uid_validity: Some(1257842737),
};
let mailbox_name = "INBOX";
let command = format!("a1 EXAMINE {}\r\n", mailbox_name);
let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream);
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");
}
@ -646,35 +730,48 @@ mod tests {
* OK [UNSEEN 1] First unseen.\r\n\
* OK [UIDVALIDITY 1257842737] UIDs valid\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 {
flags: String::from("(\\Answered \\Flagged \\Deleted \\Seen \\Draft)"),
exists: 1,
recent: 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_validity: Some(1257842737)
uid_validity: Some(1257842737),
};
let mailbox_name = "INBOX";
let command = format!("a1 SELECT {}\r\n", mailbox_name);
let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream);
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");
}
#[test]
fn capability() {
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 mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream);
let capabilities = client.capability().unwrap();
assert!(client.stream.written_buf == b"a1 CAPABILITY\r\n".to_vec(), "Invalid capability command");
assert!(capabilities == expected_capabilities, "Unexpected capabilities response");
assert!(
client.stream.written_buf == b"a1 CAPABILITY\r\n".to_vec(),
"Invalid capability command"
);
assert!(
capabilities == expected_capabilities,
"Unexpected capabilities response"
);
}
#[test]
@ -685,7 +782,10 @@ mod tests {
let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream);
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]
@ -696,7 +796,10 @@ mod tests {
let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream);
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]
@ -705,7 +808,10 @@ mod tests {
let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream);
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]
@ -714,7 +820,10 @@ mod tests {
let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream);
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]
@ -728,21 +837,16 @@ mod tests {
}
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\
* 3 FETCH (FLAGS (\\Deleted))\r\n\
* 4 FETCH (FLAGS (\\Deleted \\Flagged \\Seen))\r\n\
a1 OK STORE completed\r\n";
generic_with_uid(
res,
"STORE",
"2.4",
"+FLAGS (\\Deleted)",
prefix,
op,
);
generic_with_uid(res, "STORE", "2.4", "+FLAGS (\\Deleted)", prefix, op);
}
#[test]
@ -756,7 +860,9 @@ mod tests {
}
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(
"OK COPY completed\r\n",
@ -779,31 +885,25 @@ mod tests {
}
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(
"OK FETCH completed\r\n",
"FETCH",
"1",
"BODY[]",
prefix,
op
);
generic_with_uid("OK FETCH completed\r\n", "FETCH", "1", "BODY[]", prefix, op);
}
fn generic_with_uid<F, T>(
res: &str,
cmd: &str,
seq: &str,
query: &str,
prefix: &str,
op: F) where F: FnOnce(&mut Client<MockStream>, &str, &str) -> Result<T>,
fn generic_with_uid<F, T>(res: &str, cmd: &str, 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 line = format!("a1{}{} {} {}\r\n", prefix, cmd, seq, query);
let mut client = Client::new(MockStream::new(resp));
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.
Parse(ParseError),
// Error appending a mail
Append
Append,
}
impl From<IoError> for Error {
@ -55,7 +55,7 @@ impl StdError for Error {
Error::Parse(ref e) => e.description(),
Error::BadResponse(_) => "Bad 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.
Capability(Vec<String>),
// Authentication errors.
Authentication(String)
Authentication(String),
}
impl fmt::Display for ParseError {
@ -91,13 +91,13 @@ impl StdError for ParseError {
match *self {
ParseError::StatusResponse(_) => "Unable to parse status 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> {
match *self {
_ => None
_ => None,
}
}
}

View file

@ -1,6 +1,6 @@
use std::fmt;
#[derive(Eq,PartialEq)]
#[derive(Eq, PartialEq)]
pub struct Mailbox {
pub flags: String,
pub exists: u32,
@ -8,7 +8,7 @@ pub struct Mailbox {
pub unseen: Option<u32>,
pub permanent_flags: Option<String>,
pub uid_next: Option<u32>,
pub uid_validity: Option<u32>
pub uid_validity: Option<u32>,
}
impl Default for Mailbox {
@ -20,13 +20,24 @@ impl Default for Mailbox {
unseen: None,
permanent_flags: None,
uid_next: None,
uid_validity: None
uid_validity: None,
}
}
}
impl fmt::Display for Mailbox {
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,32 +6,32 @@ pub struct MockStream {
read_pos: usize,
pub written_buf: Vec<u8>,
err_on_read: bool,
read_delay: usize
read_delay: usize,
}
impl MockStream {
pub fn new(read_buf: Vec<u8>) -> MockStream {
MockStream{
MockStream {
read_buf: read_buf,
read_pos: 0,
written_buf: Vec::new(),
err_on_read: false,
read_delay: 0
read_delay: 0,
}
}
pub fn new_err() -> MockStream {
MockStream{
MockStream {
read_buf: Vec::new(),
read_pos: 0,
written_buf: Vec::new(),
err_on_read: true,
read_delay: 0
read_delay: 0,
}
}
pub fn new_read_delay(read_buf: Vec<u8>) -> MockStream {
MockStream{
MockStream {
read_buf: read_buf,
read_pos: 0,
written_buf: Vec::new(),
@ -42,16 +42,16 @@ impl MockStream {
}
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 {
self.read_delay -= 1;
return Ok(0)
return Ok(0);
}
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() {
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 max_pos = self.read_pos + write_len;

View file

@ -7,7 +7,7 @@ pub fn parse_authenticate_response(line: String) -> Result<String> {
let authenticate_regex = Regex::new("^+(.*)\r\n").unwrap();
for cap in authenticate_regex.captures_iter(line.as_str()) {
let data = cap.get(1).map(|x|x.as_str()).unwrap_or("");
let data = cap.get(1).map(|x| x.as_str()).unwrap_or("");
return Ok(String::from(data));
}
@ -20,7 +20,7 @@ pub fn parse_capability(lines: Vec<String>) -> Result<Vec<String>> {
//Check Ok
match parse_response_ok(lines.clone()) {
Ok(_) => (),
Err(e) => return Err(e)
Err(e) => return Err(e),
};
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<()> {
match parse_response(lines) {
Ok(_) => Ok(()),
Err(e) => return Err(e)
Err(e) => return Err(e),
}
}
@ -45,11 +45,11 @@ pub fn parse_response(lines: Vec<String>) -> Result<Vec<String>> {
let regex = Regex::new(r"^([a-zA-Z0-9]+) (OK|NO|BAD)(.*)").unwrap();
let last_line = match lines.last() {
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) {
let response_type = cap.get(2).map(|x|x.as_str()).unwrap_or("");
let response_type = cap.get(2).map(|x| x.as_str()).unwrap_or("");
match response_type {
"OK" => return Ok(lines.clone()),
"BAD" => return Err(Error::BadResponse(lines.clone())),
@ -79,7 +79,7 @@ pub fn parse_select_or_examine(lines: Vec<String>) -> Result<Mailbox> {
//Check Ok
match parse_response_ok(lines.clone()) {
Ok(_) => (),
Err(e) => return Err(e)
Err(e) => return Err(e),
};
let mut mailbox = Mailbox::default();
@ -118,22 +118,41 @@ mod tests {
#[test]
fn parse_capability_test() {
let expected_capabilities = vec![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 expected_capabilities = vec![
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();
assert!(capabilities == expected_capabilities, "Unexpected capabilities parse response");
assert!(
capabilities == expected_capabilities,
"Unexpected capabilities parse response"
);
}
#[test]
#[should_panic]
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();
}
#[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 actual_lines = parse_response(lines).unwrap();
assert!(expected_lines == actual_lines, "Unexpected parse response");
@ -142,7 +161,10 @@ mod tests {
#[test]
#[should_panic]
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();
}
}