From 86e1d46507edbea5bd180aaf1f6e1bd8fefb79ba Mon Sep 17 00:00:00 2001 From: Matt McCoy Date: Mon, 10 Jul 2017 21:38:13 -0400 Subject: [PATCH] rustfmt the codebase (#36) This will ensure that we are properly formatting this library code according to rust standards --- README.md | 54 +- examples/basic.rs | 54 +- examples/gmail_oauth2.rs | 28 +- src/client.rs | 1188 +++++++++++++++++++++----------------- src/error.rs | 10 +- src/mailbox.rs | 53 +- src/mock_stream.rs | 20 +- src/parse.rs | 46 +- 8 files changed, 802 insertions(+), 651 deletions(-) diff --git a/README.md b/README.md index 2d108bc..406a1d0 100644 --- a/README.md +++ b/README.md @@ -22,36 +22,40 @@ 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(); + imap_socket.login("username", "password").unwrap(); - match imap_socket.capability() { - Ok(capabilities) => { - for capability in capabilities.iter() { - println!("{}", capability); - } - }, - Err(e) => println!("Error parsing capability: {}", e) - }; + match imap_socket.capability() { + Ok(capabilities) => { + for capability in capabilities.iter() { + println!("{}", capability); + } + } + Err(e) => println!("Error parsing capability: {}", e), + }; - match imap_socket.select("INBOX") { - Ok(mailbox) => { - println!("{}", mailbox); - }, - Err(e) => println!("Error selecting INBOX: {}", e) - }; + match imap_socket.select("INBOX") { + Ok(mailbox) => { + println!("{}", mailbox); + } + Err(e) => println!("Error selecting INBOX: {}", e), + }; - match imap_socket.fetch("2", "body[text]") { - Ok(lines) => { - for line in lines.iter() { - print!("{}", line); - } - }, - Err(e) => println!("Error Fetching email 2: {}", e) - }; + match imap_socket.fetch("2", "body[text]") { + Ok(lines) => { + for line in lines.iter() { + print!("{}", line); + } + } + Err(e) => println!("Error Fetching email 2: {}", e), + }; - imap_socket.logout().unwrap(); + imap_socket.logout().unwrap(); } ``` diff --git a/examples/basic.rs b/examples/basic.rs index f38dc12..1aee000 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -8,34 +8,38 @@ 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(); + imap_socket.login("username", "password").unwrap(); - match imap_socket.capability() { - Ok(capabilities) => { - for capability in capabilities.iter() { - println!("{}", capability); - } - }, - Err(e) => println!("Error parsing capability: {}", e) - }; + match imap_socket.capability() { + Ok(capabilities) => { + for capability in capabilities.iter() { + println!("{}", capability); + } + } + Err(e) => println!("Error parsing capability: {}", e), + }; - match imap_socket.select("INBOX") { - Ok(mailbox) => { - println!("{}", mailbox); - }, - Err(e) => println!("Error selecting INBOX: {}", e) - }; + match imap_socket.select("INBOX") { + Ok(mailbox) => { + println!("{}", mailbox); + } + Err(e) => println!("Error selecting INBOX: {}", e), + }; - match imap_socket.fetch("2", "body[text]") { - Ok(lines) => { - for line in lines.iter() { - print!("{}", line); - } - }, - Err(e) => println!("Error Fetching email 2: {}", e) - }; + match imap_socket.fetch("2", "body[text]") { + Ok(lines) => { + for line in lines.iter() { + print!("{}", line); + } + } + Err(e) => println!("Error Fetching email 2: {}", e), + }; - imap_socket.logout().unwrap(); + imap_socket.logout().unwrap(); } diff --git a/examples/gmail_oauth2.rs b/examples/gmail_oauth2.rs index 30d2180..6f5b91d 100644 --- a/examples/gmail_oauth2.rs +++ b/examples/gmail_oauth2.rs @@ -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: String::from(""), }; - 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(); diff --git a/src/client.rs b/src/client.rs index 3d41232..4bd4b99 100644 --- a/src/client.rs +++ b/src/client.rs @@ -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"; @@ -15,9 +16,9 @@ const LF: u8 = 0x0a; /// Stream to interface with the IMAP server. This interface is only for the command stream. pub struct Client { - stream: T, - tag: u32, - pub debug: bool + stream: T, + tag: u32, + 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,651 +161,749 @@ impl<'a> SetReadTimeout for TcpStream { impl<'a> SetReadTimeout for SslStream { fn set_read_timeout(&mut self, timeout: Option) -> 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)) } } impl Client { - /// Creates a new client. - pub fn connect(addr: A) -> Result> { - match TcpStream::connect(addr) { - Ok(stream) => { - let mut socket = Client::new(stream); + /// Creates a new client. + pub fn connect(addr: A) -> Result> { + match TcpStream::connect(addr) { + Ok(stream) => { + let mut socket = Client::new(stream); - try!(socket.read_greeting()); - Ok(socket) - }, - Err(e) => Err(Error::Io(e)) - } - } + try!(socket.read_greeting()); + Ok(socket) + } + 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. - pub fn secure(mut self, domain: &str,ssl_connector: SslConnector) -> Result>> { - // TODO This needs to be tested - self.run_command_and_check_ok("STARTTLS")?; - SslConnector::connect(&ssl_connector,domain, self.stream) - .map(Client::new) - .map_err(Error::Ssl) - } + pub fn secure( + mut self, + domain: &str, + ssl_connector: SslConnector, + ) -> Result>> { + // TODO This needs to be tested + self.run_command_and_check_ok("STARTTLS")?; + SslConnector::connect(&ssl_connector, domain, self.stream) + .map(Client::new) + .map_err(Error::Ssl) + } } impl Client> { - /// Creates a client with an SSL wrapper. - pub fn secure_connect(addr: A, domain: &str,ssl_connector: SslConnector) -> Result>> { - match TcpStream::connect(addr) { - Ok(stream) => { - let ssl_stream = match SslConnector::connect(&ssl_connector, domain,stream) { - Ok(s) => s, - Err(e) => return Err(Error::Ssl(e)) - }; - let mut socket = Client::new(ssl_stream); + /// Creates a client with an SSL wrapper. + pub fn secure_connect( + addr: A, + domain: &str, + ssl_connector: SslConnector, + ) -> Result>> { + match TcpStream::connect(addr) { + Ok(stream) => { + let ssl_stream = match SslConnector::connect(&ssl_connector, domain, stream) { + Ok(s) => s, + 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)) - } - } + try!(socket.read_greeting()); + Ok(socket) + } + Err(e) => Err(Error::Io(e)), + } + } } -impl Client { +impl Client { + /// Creates a new client with the underlying stream. + pub fn new(stream: T) -> Client { + Client { + stream: stream, + tag: INITIAL_TAG, + debug: false, + } + } - /// Creates a new client with the underlying stream. - pub fn new(stream: T) -> Client { - Client{ - stream: stream, - tag: INITIAL_TAG, - debug: false - } - } + /// Authenticate will authenticate with the server, using the authenticator given. + pub fn authenticate( + &mut self, + auth_type: &str, + authenticator: A, + ) -> Result<()> { + try!(self.run_command( + &format!("AUTHENTICATE {}", auth_type).to_string() + )); + self.do_auth_handshake(authenticator) + } - /// Authenticate will authenticate with the server, using the authenticator given. - pub fn authenticate(&mut self, auth_type: &str, authenticator: A) -> Result<()> { - try!(self.run_command(&format!("AUTHENTICATE {}", auth_type).to_string())); - self.do_auth_handshake(authenticator) - } + /// This func does the handshake process once the authenticate command is made. + fn do_auth_handshake(&mut self, authenticator: A) -> Result<()> { + // TODO Clean up this code + loop { + let line = try!(self.readline()); - /// This func does the handshake process once the authenticate command is made. - fn do_auth_handshake(&mut self, authenticator: A) -> Result<()> { - // TODO Clean up this code - loop { - let line = try!(self.readline()); + if line.starts_with(b"+") { + let data = try!(parse_authenticate_response( + String::from_utf8(line).unwrap() + )); + let auth_response = authenticator.process(data); - if line.starts_with(b"+") { - 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())) + } else if line.starts_with(format!("{}{} ", TAG_PREFIX, self.tag).as_bytes()) { + try!(parse_response(vec![String::from_utf8(line).unwrap()])); + return Ok(()); - try!(self.write_line(auth_response.into_bytes().as_slice())) - } else if line.starts_with(format!("{}{} ", TAG_PREFIX, self.tag).as_bytes()) { - try!(parse_response(vec![String::from_utf8(line).unwrap()])); - return Ok(()); + } else { + let mut lines = try!(self.read_response()); + lines.insert(0, String::from_utf8(line).unwrap()); + try!(parse_response(lines.clone())); + return Ok(()); + } + } + } - } else { - let mut lines = try!(self.read_response()); - lines.insert(0, String::from_utf8(line).unwrap()); - try!(parse_response(lines.clone())); - return Ok(()); - } - } - } + /// Log in to the IMAP server. + pub fn login(&mut self, username: &str, password: &str) -> Result<()> { + self.run_command_and_check_ok(&format!("LOGIN {} {}", username, password).to_string()) + } - /// Log in to the IMAP server. - 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 { + let lines = try!(self.run_command_and_read_response( + &format!("SELECT {}", mailbox_name).to_string() + )); + parse_select_or_examine(lines) + } - /// Selects a mailbox - pub fn select(&mut self, mailbox_name: &str) -> Result { - 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 { + let lines = try!(self.run_command_and_read_response( + &format!("EXAMINE {}", 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 { - let lines = try!( - self.run_command_and_read_response(&format!("EXAMINE {}", mailbox_name).to_string()) - ); - parse_select_or_examine(lines) - } + /// Fetch retreives data associated with a message in the mailbox. + pub fn fetch(&mut self, sequence_set: &str, query: &str) -> Result> { + self.run_command_and_read_response(&format!("FETCH {} {}", sequence_set, query).to_string()) + } - /// Fetch retreives data associated with a message in the mailbox. - pub fn fetch(&mut self, sequence_set: &str, query: &str) -> Result> { - self.run_command_and_read_response(&format!("FETCH {} {}", sequence_set, query).to_string()) - } + pub fn uid_fetch(&mut self, uid_set: &str, query: &str) -> Result> { + self.run_command_and_read_response(&format!("UID FETCH {} {}", uid_set, query).to_string()) + } - pub fn uid_fetch(&mut self, uid_set: &str, query: &str) -> Result> { - self.run_command_and_read_response(&format!("UID FETCH {} {}", uid_set, query).to_string()) - } + /// Noop always succeeds, and it does nothing. + pub fn noop(&mut self) -> Result<()> { + self.run_command_and_check_ok("NOOP") + } - /// Noop always succeeds, and it does nothing. - pub fn noop(&mut self) -> Result<()> { - self.run_command_and_check_ok("NOOP") - } + /// Logout informs the server that the client is done with the connection. + pub fn logout(&mut self) -> Result<()> { + self.run_command_and_check_ok("LOGOUT") + } - /// Logout informs the server that the client is done with the connection. - pub fn logout(&mut self) -> Result<()> { - self.run_command_and_check_ok("LOGOUT") - } + /// Create creates a mailbox with the given name. + pub fn create(&mut self, mailbox_name: &str) -> Result<()> { + self.run_command_and_check_ok(&format!("CREATE {}", mailbox_name).to_string()) + } - /// Create creates a mailbox with the given name. - pub fn create(&mut self, mailbox_name: &str) -> Result<()> { - self.run_command_and_check_ok(&format!("CREATE {}", mailbox_name).to_string()) - } + /// Delete permanently removes the mailbox with the given name. + pub fn delete(&mut self, mailbox_name: &str) -> Result<()> { + self.run_command_and_check_ok(&format!("DELETE {}", mailbox_name).to_string()) + } - /// Delete permanently removes the mailbox with the given name. - pub fn delete(&mut self, mailbox_name: &str) -> Result<()> { - self.run_command_and_check_ok(&format!("DELETE {}", mailbox_name).to_string()) - } + /// 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()) + } - /// 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()) - } + /// Subscribe adds the specified mailbox name to the server's set of "active" or "subscribed" + /// mailboxes as returned by the LSUB command. + pub fn subscribe(&mut self, mailbox: &str) -> Result<()> { + self.run_command_and_check_ok(&format!("SUBSCRIBE {}", mailbox).to_string()) + } - /// Subscribe adds the specified mailbox name to the server's set of "active" or "subscribed" - /// mailboxes as returned by the LSUB command. - pub fn subscribe(&mut self, mailbox: &str) -> Result<()> { - 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. + pub fn unsubscribe(&mut self, mailbox: &str) -> Result<()> { + self.run_command_and_check_ok(&format!("UNSUBSCRIBE {}", 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. - 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> { + let lines = try!(self.run_command_and_read_response( + &format!("CAPABILITY").to_string() + )); + parse_capability(lines) + } - /// Capability requests a listing of capabilities that the server supports. - pub fn capability(&mut self) -> Result> { - let lines = try!( - self.run_command_and_read_response(&format!("CAPABILITY").to_string()) - ); - parse_capability(lines) - } + /// Expunge permanently removes all messages that have the \Deleted flag set from the currently + /// selected mailbox. + pub fn expunge(&mut self) -> Result<()> { + self.run_command_and_check_ok("EXPUNGE") + } - /// Expunge permanently removes all messages that have the \Deleted flag set from the currently - /// selected mailbox. - pub fn expunge(&mut self) -> Result<()> { - self.run_command_and_check_ok("EXPUNGE") - } + /// Check requests a checkpoint of the currently selected mailbox. + pub fn check(&mut self) -> Result<()> { + self.run_command_and_check_ok("CHECK") + } - /// Check requests a checkpoint of the currently selected mailbox. - pub fn check(&mut self) -> Result<()> { - self.run_command_and_check_ok("CHECK") - } + /// Close permanently removes all messages that have the \Deleted flag set from the currently + /// selected mailbox, and returns to the authenticated state from the selected state. + pub fn close(&mut self) -> Result<()> { + self.run_command_and_check_ok("CLOSE") + } - /// Close permanently removes all messages that have the \Deleted flag set from the currently - /// selected mailbox, and returns to the authenticated state from the selected state. - pub fn close(&mut self) -> Result<()> { - self.run_command_and_check_ok("CLOSE") - } + /// Store alters data associated with a message in the mailbox. + pub fn store(&mut self, sequence_set: &str, query: &str) -> Result> { + self.run_command_and_read_response(&format!("STORE {} {}", sequence_set, query)) + } - /// Store alters data associated with a message in the mailbox. - pub fn store(&mut self, sequence_set: &str, query: &str) -> Result> { - self.run_command_and_read_response(&format!("STORE {} {}", sequence_set, query)) - } + pub fn uid_store(&mut self, uid_set: &str, query: &str) -> Result> { + self.run_command_and_read_response(&format!("UID STORE {} {}", uid_set, query)) + } - pub fn uid_store(&mut self, uid_set: &str, query: &str) -> Result> { - self.run_command_and_read_response(&format!("UID STORE {} {}", uid_set, query)) - } + /// 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()) + } - /// 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()) - } + pub fn uid_copy(&mut self, uid_set: &str, mailbox_name: &str) -> Result<()> { + self.run_command_and_check_ok(&format!("UID COPY {} {}", uid_set, mailbox_name)) + } - pub fn uid_copy(&mut self, uid_set: &str, mailbox_name: &str) -> Result<()> { - self.run_command_and_check_ok(&format!("UID COPY {} {}", uid_set, mailbox_name)) - } + /// 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> { + self.run_command_and_parse(&format!( + "LIST {} {}", + reference_name, + mailbox_search_pattern + )) + } - /// 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> { - 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> { + self.run_command_and_parse(&format!( + "LSUB {} {}", + 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> { - self.run_command_and_parse(&format!("LSUB {} {}", reference_name, mailbox_search_pattern)) - } + /// The STATUS command requests the status of the indicated mailbox. + pub fn status(&mut self, mailbox_name: &str, status_data_items: &str) -> Result> { + self.run_command_and_parse(&format!("STATUS {} {}", mailbox_name, status_data_items)) + } - /// The STATUS command requests the status of the indicated mailbox. - pub fn status(&mut self, mailbox_name: &str, status_data_items: &str) -> Result> { - self.run_command_and_parse(&format!("STATUS {} {}", mailbox_name, status_data_items)) - } + /// Returns a handle that can be used to block until the state of the currently selected + /// mailbox changes. + pub fn idle(&mut self) -> Result> { + IdleHandle::new(self) + } - /// Returns a handle that can be used to block until the state of the currently selected - /// mailbox changes. - pub fn idle(&mut self) -> Result> { - IdleHandle::new(self) - } + /// The APPEND command adds a mail to a mailbox. + pub fn append(&mut self, folder: &str, content: &[u8]) -> Result> { + try!(self.run_command( + &format!("APPEND \"{}\" {{{}}}", folder, content.len()) + )); + let line = try!(self.readline()); + if !line.starts_with(b"+") { + return Err(Error::Append); + } + try!(self.stream.write_all(content)); + try!(self.stream.write_all(b"\r\n")); + self.read_response() + } - /// The APPEND command adds a mail to a mailbox. - pub fn append(&mut self, folder: &str, content: &[u8]) -> Result> { - try!(self.run_command(&format!("APPEND \"{}\" {{{}}}", folder, content.len()))); - let line = try!(self.readline()); - if !line.starts_with(b"+") { - return Err(Error::Append); - } - try!(self.stream.write_all(content)); - try!(self.stream.write_all(b"\r\n")); - self.read_response() - } + /// Runs a command and checks if it returns OK. + pub fn run_command_and_check_ok(&mut self, command: &str) -> Result<()> { + let lines = try!(self.run_command_and_read_response(command)); + parse_response_ok(lines) + } - /// Runs a command and checks if it returns OK. - pub fn run_command_and_check_ok(&mut self, command: &str) -> Result<()> { - let lines = try!(self.run_command_and_read_response(command)); - parse_response_ok(lines) - } + // Run a command and parse the status response. + pub fn run_command_and_parse(&mut self, command: &str) -> Result> { + let lines = try!(self.run_command_and_read_response(command)); + parse_response(lines) + } - // Run a command and parse the status response. - pub fn run_command_and_parse(&mut self, command: &str) -> Result> { - let lines = try!(self.run_command_and_read_response(command)); - parse_response(lines) - } + /// Runs any command passed to it. + pub fn run_command(&mut self, untagged_command: &str) -> Result<()> { + let command = self.create_command(untagged_command.to_string()); + self.write_line(command.into_bytes().as_slice()) + } - /// Runs any command passed to it. - pub fn run_command(&mut self, untagged_command: &str) -> Result<()> { - let command = self.create_command(untagged_command.to_string()); - self.write_line(command.into_bytes().as_slice()) - } + pub fn run_command_and_read_response(&mut self, untagged_command: &str) -> Result> { + try!(self.run_command(untagged_command)); + self.read_response() + } - pub fn run_command_and_read_response(&mut self, untagged_command: &str) -> Result> { - try!(self.run_command(untagged_command)); - self.read_response() - } + fn read_response(&mut self) -> Result> { + let mut found_tag_line = false; + let start_str = format!("{}{} ", TAG_PREFIX, self.tag); + let mut lines: Vec = Vec::new(); - fn read_response(&mut self) -> Result> { - let mut found_tag_line = false; - let start_str = format!("{}{} ", TAG_PREFIX, self.tag); - let mut lines: Vec = Vec::new(); + while !found_tag_line { + let raw_data = try!(self.readline()); + let line = String::from_utf8(raw_data).unwrap(); + lines.push(line.clone()); + if (&*line).starts_with(&*start_str) { + found_tag_line = true; + } + } - while !found_tag_line { - let raw_data = try!(self.readline()); - let line = String::from_utf8(raw_data).unwrap(); - lines.push(line.clone()); - if (&*line).starts_with(&*start_str) { - found_tag_line = true; - } - } + Ok(lines) + } - Ok(lines) - } + fn read_greeting(&mut self) -> Result<()> { + try!(self.readline()); + Ok(()) + } - fn read_greeting(&mut self) -> Result<()> { - try!(self.readline()); - Ok(()) - } + fn readline(&mut self) -> Result> { + let mut line_buffer: Vec = Vec::new(); + 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 { + line_buffer.push(byte_buffer[0]); + } + } - fn readline(&mut self) -> Result> { - let mut line_buffer: Vec = Vec::new(); - 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 { - line_buffer.push(byte_buffer[0]); - } - } + if self.debug { + let mut line = line_buffer.clone(); + // Remove CRLF + line.truncate(line_buffer.len() - 2); + print!("S: {}\n", String::from_utf8(line).unwrap()); + } - if self.debug { - let mut line = line_buffer.clone(); - // Remove CRLF - line.truncate(line_buffer.len()-2); - print!("S: {}\n", String::from_utf8(line).unwrap()); - } + Ok(line_buffer) + } - Ok(line_buffer) - } + fn create_command(&mut self, command: String) -> String { + self.tag += 1; + let command = format!("{}{} {}", TAG_PREFIX, self.tag, command); + return command; + } - fn create_command(&mut self, command: String) -> String { - self.tag += 1; - let command = format!("{}{} {}", TAG_PREFIX, self.tag, command); - return command; - } - - fn write_line(&mut self, buf: &[u8]) -> Result<()> { - try!(self.stream.write_all(buf)); - try!(self.stream.write_all(&[CR, LF])); - if self.debug { - print!("C: {}\n", String::from_utf8(buf.to_vec()).unwrap()); - } - Ok(()) - } + fn write_line(&mut self, buf: &[u8]) -> Result<()> { + try!(self.stream.write_all(buf)); + try!(self.stream.write_all(&[CR, LF])); + if self.debug { + print!("C: {}\n", String::from_utf8(buf.to_vec()).unwrap()); + } + Ok(()) + } } #[cfg(test)] mod tests { - use super::*; - use super::super::mock_stream::MockStream; - use super::super::mailbox::Mailbox; - use super::super::error::Result; + use super::*; + use super::super::mock_stream::MockStream; + use super::super::mailbox::Mailbox; + use super::super::error::Result; - #[test] - fn read_response() { - let response = "a0 OK Logged in.\r\n"; - let expected_response: Vec = vec![response.to_string()]; - 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"); - } + #[test] + fn read_response() { + let response = "a0 OK Logged in.\r\n"; + let expected_response: Vec = vec![response.to_string()]; + 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" + ); + } - #[test] - fn read_greeting() { - let greeting = "* OK Dovecot ready.\r\n"; - let mock_stream = MockStream::new(greeting.as_bytes().to_vec()); - let mut client = Client::new(mock_stream); - client.read_greeting().unwrap(); - } + #[test] + fn read_greeting() { + let greeting = "* OK Dovecot ready.\r\n"; + let mock_stream = MockStream::new(greeting.as_bytes().to_vec()); + let mut client = Client::new(mock_stream); + client.read_greeting().unwrap(); + } - #[test] - fn readline_delay_read() { - let greeting = "* OK Dovecot ready.\r\n"; - let expected_response: String = greeting.to_string(); - let mock_stream = MockStream::new_read_delay(greeting.as_bytes().to_vec()); - let mut client = Client::new(mock_stream); - let actual_response = String::from_utf8(client.readline().unwrap()).unwrap(); - assert_eq!(expected_response, actual_response); - } + #[test] + fn readline_delay_read() { + let greeting = "* OK Dovecot ready.\r\n"; + let expected_response: String = greeting.to_string(); + let mock_stream = MockStream::new_read_delay(greeting.as_bytes().to_vec()); + let mut client = Client::new(mock_stream); + let actual_response = String::from_utf8(client.readline().unwrap()).unwrap(); + assert_eq!(expected_response, actual_response); + } - #[test] - #[should_panic] - fn readline_err() { - // TODO Check the error test - let mock_stream = MockStream::new_err(); - let mut client = Client::new(mock_stream); - client.readline().unwrap(); - } + #[test] + #[should_panic] + fn readline_err() { + // TODO Check the error test + let mock_stream = MockStream::new_err(); + let mut client = Client::new(mock_stream); + client.readline().unwrap(); + } - #[test] - fn create_command() { - let base_command = "CHECK"; - let mock_stream = MockStream::new(Vec::new()); - let mut imap_stream = Client::new(mock_stream); + #[test] + fn create_command() { + let base_command = "CHECK"; + let mock_stream = MockStream::new(Vec::new()); + let mut imap_stream = Client::new(mock_stream); - 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"); + 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" + ); - 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"); - } + 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" + ); + } - #[test] - fn login() { - let response = b"a1 OK Logged in\r\n".to_vec(); - let username = "username"; - let password = "password"; - let command = format!("a1 LOGIN {} {}\r\n", username, password); - 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"); - } + #[test] + fn login() { + let response = b"a1 OK Logged in\r\n".to_vec(); + let username = "username"; + let password = "password"; + let command = format!("a1 LOGIN {} {}\r\n", username, password); + 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" + ); + } - #[test] - fn logout() { - let response = b"a1 OK Logout completed.\r\n".to_vec(); - let command = format!("a1 LOGOUT\r\n"); - 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"); - } + #[test] + fn logout() { + let response = b"a1 OK Logout completed.\r\n".to_vec(); + let command = format!("a1 LOGOUT\r\n"); + 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" + ); + } - #[test] - fn rename() { - 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 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"); - } + #[test] + fn rename() { + 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 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" + ); + } - #[test] - fn subscribe() { - let response = b"a1 OK SUBSCRIBE completed\r\n".to_vec(); - let mailbox = "INBOX"; - let command = format!("a1 SUBSCRIBE {}\r\n", mailbox); - 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"); - } + #[test] + fn subscribe() { + let response = b"a1 OK SUBSCRIBE completed\r\n".to_vec(); + let mailbox = "INBOX"; + let command = format!("a1 SUBSCRIBE {}\r\n", mailbox); + 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" + ); + } - #[test] - fn unsubscribe() { - let response = b"a1 OK UNSUBSCRIBE completed\r\n".to_vec(); - let mailbox = "INBOX"; - let command = format!("a1 UNSUBSCRIBE {}\r\n", mailbox); - 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"); - } + #[test] + fn unsubscribe() { + let response = b"a1 OK UNSUBSCRIBE completed\r\n".to_vec(); + let mailbox = "INBOX"; + let command = format!("a1 UNSUBSCRIBE {}\r\n", mailbox); + 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" + ); + } - #[test] - fn expunge() { - let response = b"a1 OK EXPUNGE completed\r\n".to_vec(); - 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"); - } + #[test] + fn expunge() { + let response = b"a1 OK EXPUNGE completed\r\n".to_vec(); + 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" + ); + } - #[test] - fn check() { - let response = b"a1 OK CHECK completed\r\n".to_vec(); - 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"); - } + #[test] + fn check() { + let response = b"a1 OK CHECK completed\r\n".to_vec(); + 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" + ); + } - #[test] - fn examine() { - let response = b"* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n\ + #[test] + fn examine() { + let response = b"* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n\ * OK [PERMANENTFLAGS ()] Read-only mailbox.\r\n\ * 1 EXISTS\r\n\ * 1 RECENT\r\n\ * 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(); - let expected_mailbox = Mailbox { - flags: String::from("(\\Answered \\Flagged \\Deleted \\Seen \\Draft)"), - exists: 1, - recent: 1, - unseen: Some(1), - permanent_flags: Some(String::from("()")), - uid_next: Some(2), - 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!(mailbox == expected_mailbox, "Unexpected mailbox returned"); - } + 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("()")), + uid_next: Some(2), + 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!(mailbox == expected_mailbox, "Unexpected mailbox returned"); + } - #[test] - fn select() { - let response = b"* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n\ + #[test] + fn select() { + let response = b"* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n\ * OK [PERMANENTFLAGS (\\* \\Answered \\Flagged \\Deleted \\Draft \\Seen)] Read-only mailbox.\r\n\ * 1 EXISTS\r\n\ * 1 RECENT\r\n\ * 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(); - 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)")), - uid_next: Some(2), - 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!(mailbox == expected_mailbox, "Unexpected mailbox returned"); - } + 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)", + )), + uid_next: Some(2), + 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!(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(); - 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"); - } + #[test] + fn capability() { + let response = b"* CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED\r\n\ + 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" + ); + } - #[test] - fn create() { - let response = b"a1 OK CREATE completed\r\n".to_vec(); - let mailbox_name = "INBOX"; - let command = format!("a1 CREATE {}\r\n", mailbox_name); - 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"); - } + #[test] + fn create() { + let response = b"a1 OK CREATE completed\r\n".to_vec(); + let mailbox_name = "INBOX"; + let command = format!("a1 CREATE {}\r\n", mailbox_name); + 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" + ); + } - #[test] - fn delete() { - let response = b"a1 OK DELETE completed\r\n".to_vec(); - let mailbox_name = "INBOX"; - let command = format!("a1 DELETE {}\r\n", mailbox_name); - 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"); - } + #[test] + fn delete() { + let response = b"a1 OK DELETE completed\r\n".to_vec(); + let mailbox_name = "INBOX"; + let command = format!("a1 DELETE {}\r\n", mailbox_name); + 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" + ); + } - #[test] - fn noop() { - let response = b"a1 OK NOOP completed\r\n".to_vec(); - 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"); - } + #[test] + fn noop() { + let response = b"a1 OK NOOP completed\r\n".to_vec(); + 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" + ); + } - #[test] - fn close() { - let response = b"a1 OK CLOSE completed\r\n".to_vec(); - 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"); - } + #[test] + fn close() { + let response = b"a1 OK CLOSE completed\r\n".to_vec(); + 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" + ); + } - #[test] - fn store() { - generic_store(" ", |mut c, set, query| c.store(set, query)); - } + #[test] + fn store() { + generic_store(" ", |mut c, set, query| c.store(set, query)); + } - #[test] - fn uid_store() { - generic_store(" UID ", |mut c, set, query| c.uid_store(set, query)); - } + #[test] + fn uid_store() { + generic_store(" UID ", |mut c, set, query| c.uid_store(set, query)); + } - fn generic_store(prefix: &str, op: F) - where F: FnOnce(&mut Client, &str, &str) -> Result { + fn generic_store(prefix: &str, op: F) + where + F: FnOnce(&mut Client, &str, &str) -> Result, + { - 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"; + 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] - fn copy() { - generic_copy(" ", |mut c, set, query| c.copy(set, query)) - } + #[test] + fn copy() { + generic_copy(" ", |mut c, set, query| c.copy(set, query)) + } - #[test] - fn uid_copy() { - generic_copy(" UID ", |mut c, set, query| c.uid_copy(set, query)) - } + #[test] + fn uid_copy() { + generic_copy(" UID ", |mut c, set, query| c.uid_copy(set, query)) + } - fn generic_copy(prefix: &str, op: F) - where F: FnOnce(&mut Client, &str, &str) -> Result { + fn generic_copy(prefix: &str, op: F) + where + F: FnOnce(&mut Client, &str, &str) -> Result, + { - generic_with_uid( - "OK COPY completed\r\n", - "COPY", - "2:4", - "MEETING", - prefix, - op, - ); - } + generic_with_uid( + "OK COPY completed\r\n", + "COPY", + "2:4", + "MEETING", + prefix, + op, + ); + } - #[test] - fn fetch() { - generic_fetch(" ", |mut c, seq, query| c.fetch(seq, query)) - } + #[test] + fn fetch() { + generic_fetch(" ", |mut c, seq, query| c.fetch(seq, query)) + } - #[test] - fn uid_fetch() { - generic_fetch(" UID ", |mut c, seq, query| c.uid_fetch(seq, query)) - } + #[test] + fn uid_fetch() { + generic_fetch(" UID ", |mut c, seq, query| c.uid_fetch(seq, query)) + } - fn generic_fetch(prefix: &str, op: F) - where F: FnOnce(&mut Client, &str, &str) -> Result { + fn generic_fetch(prefix: &str, op: F) + where + F: FnOnce(&mut Client, &str, &str) -> Result, + { - 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( - res: &str, - cmd: &str, - seq: &str, - query: &str, - prefix: &str, - op: F) where F: FnOnce(&mut Client, &str, &str) -> Result, - { + fn generic_with_uid(res: &str, cmd: &str, seq: &str, query: &str, prefix: &str, op: F) + where + F: FnOnce(&mut Client, &str, &str) -> Result, + { - 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"); - } + 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" + ); + } } diff --git a/src/error.rs b/src/error.rs index 02df535..9acbbd5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -22,7 +22,7 @@ pub enum Error { // Error parsing a server response. Parse(ParseError), // Error appending a mail - Append + Append, } impl From 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), // 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, } } } diff --git a/src/mailbox.rs b/src/mailbox.rs index 876bf85..6d5f878 100644 --- a/src/mailbox.rs +++ b/src/mailbox.rs @@ -1,32 +1,43 @@ use std::fmt; -#[derive(Eq,PartialEq)] +#[derive(Eq, PartialEq)] pub struct Mailbox { - pub flags: String, - pub exists: u32, - pub recent: u32, - pub unseen: Option, - pub permanent_flags: Option, - pub uid_next: Option, - pub uid_validity: Option + pub flags: String, + pub exists: u32, + pub recent: u32, + pub unseen: Option, + pub permanent_flags: Option, + pub uid_next: Option, + pub uid_validity: Option, } impl Default for Mailbox { - fn default() -> Mailbox { - Mailbox { - flags: "".to_string(), - exists: 0, - recent: 0, - unseen: None, - permanent_flags: None, - uid_next: None, - uid_validity: None - } - } + fn default() -> Mailbox { + Mailbox { + flags: "".to_string(), + exists: 0, + recent: 0, + unseen: None, + permanent_flags: None, + uid_next: 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) + 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 + ) } } diff --git a/src/mock_stream.rs b/src/mock_stream.rs index 3c1c086..5316c23 100644 --- a/src/mock_stream.rs +++ b/src/mock_stream.rs @@ -6,32 +6,32 @@ pub struct MockStream { read_pos: usize, pub written_buf: Vec, err_on_read: bool, - read_delay: usize + read_delay: usize, } impl MockStream { pub fn new(read_buf: Vec) -> 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) -> 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 { + fn read(&mut self, buf: &mut [u8]) -> Result { 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; diff --git a/src/parse.rs b/src/parse.rs index df5c5ca..2790bb8 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -7,7 +7,7 @@ pub fn parse_authenticate_response(line: String) -> Result { 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) -> Result> { //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) -> Result> { pub fn parse_response_ok(lines: Vec) -> 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) -> Result> { 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) -> Result { //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(); } }