diff --git a/src/client.rs b/src/client.rs index 8a996e5..a1ea6bf 100644 --- a/src/client.rs +++ b/src/client.rs @@ -161,14 +161,14 @@ pub fn connect_insecure(addr: A) -> Result> /// let client = imap::connect(("imap.example.org", 993), "imap.example.org", &tls).unwrap(); /// # } /// ``` -pub fn connect( +pub fn connect>( addr: A, - domain: &str, + domain: S, ssl_connector: &TlsConnector, ) -> Result>> { match TcpStream::connect(addr) { Ok(stream) => { - let ssl_stream = match TlsConnector::connect(ssl_connector, domain, stream) { + let ssl_stream = match TlsConnector::connect(ssl_connector, domain.as_ref(), stream) { Ok(s) => s, Err(e) => return Err(Error::TlsHandshake(e)), }; @@ -185,16 +185,20 @@ impl Client { /// This will upgrade an IMAP client from using a regular TCP connection to use TLS. /// /// The domain parameter is required to perform hostname verification. - pub fn secure( + pub fn secure>( mut self, - domain: &str, + domain: S, ssl_connector: &TlsConnector, ) -> Result>> { // TODO This needs to be tested self.run_command_and_check_ok("STARTTLS")?; - TlsConnector::connect(ssl_connector, domain, self.conn.stream.into_inner()?) - .map(Client::new) - .map_err(Error::TlsHandshake) + TlsConnector::connect( + ssl_connector, + domain.as_ref(), + self.conn.stream.into_inner()?, + ) + .map(Client::new) + .map_err(Error::TlsHandshake) } } @@ -259,13 +263,13 @@ impl Client { /// } /// # } /// ``` - pub fn login( + pub fn login, P: AsRef>( mut self, - username: &str, - password: &str, + username: U, + password: P, ) -> ::std::result::Result, (Error, Client)> { - let u = ok_or_unauth_client_err!(validate_str(username), self); - let p = ok_or_unauth_client_err!(validate_str(password), self); + let u = ok_or_unauth_client_err!(validate_str(username.as_ref()), self); + let p = ok_or_unauth_client_err!(validate_str(password.as_ref()), self); ok_or_unauth_client_err!( self.run_command_and_check_ok(&format!("LOGIN {} {}", u, p)), self @@ -317,13 +321,13 @@ impl Client { /// }; /// } /// ``` - pub fn authenticate( + pub fn authenticate>( mut self, - auth_type: &str, + auth_type: S, authenticator: &A, ) -> ::std::result::Result, (Error, Client)> { ok_or_unauth_client_err!( - self.run_command(&format!("AUTHENTICATE {}", auth_type)), + self.run_command(&format!("AUTHENTICATE {}", auth_type.as_ref())), self ); self.do_auth_handshake(authenticator) @@ -394,19 +398,25 @@ impl Session { /// [`Connection::run_command_and_read_response`], you *may* see additional untagged `RECENT`, /// `EXISTS`, `FETCH`, and `EXPUNGE` responses. You can get them from the /// `unsolicited_responses` channel of the [`Session`](struct.Session.html). - pub fn select(&mut self, mailbox_name: &str) -> Result { + pub fn select>(&mut self, mailbox_name: S) -> Result { // TODO: also note READ/WRITE vs READ-only mode! - self.run_command_and_read_response(&format!("SELECT {}", validate_str(mailbox_name)?)) - .and_then(|lines| parse_mailbox(&lines[..], &mut self.unsolicited_responses_tx)) + self.run_command_and_read_response(&format!( + "SELECT {}", + validate_str(mailbox_name.as_ref())? + )) + .and_then(|lines| parse_mailbox(&lines[..], &mut self.unsolicited_responses_tx)) } /// The `EXAMINE` command is identical to [`Session::select`] and returns the same output; /// however, the selected mailbox is identified as read-only. No changes to the permanent state /// of the mailbox, including per-user state, will happen in a mailbox opened with `examine`; /// in particular, messagess cannot lose [`Flag::Recent`] in an examined mailbox. - pub fn examine(&mut self, mailbox_name: &str) -> Result { - self.run_command_and_read_response(&format!("EXAMINE {}", validate_str(mailbox_name)?)) - .and_then(|lines| parse_mailbox(&lines[..], &mut self.unsolicited_responses_tx)) + pub fn examine>(&mut self, mailbox_name: S) -> Result { + self.run_command_and_read_response(&format!( + "EXAMINE {}", + validate_str(mailbox_name.as_ref())? + )) + .and_then(|lines| parse_mailbox(&lines[..], &mut self.unsolicited_responses_tx)) } /// Fetch retreives data associated with a set of messages in the mailbox. @@ -467,16 +477,32 @@ impl Session { /// - `RFC822.HEADER`: Functionally equivalent to `BODY.PEEK[HEADER]`. /// - `RFC822.SIZE`: The [RFC-2822](https://tools.ietf.org/html/rfc2822) size of the message. /// - `UID`: The unique identifier for the message. - pub fn fetch(&mut self, sequence_set: &str, query: &str) -> ZeroCopyResult> { - self.run_command_and_read_response(&format!("FETCH {} {}", sequence_set, query)) - .and_then(|lines| parse_fetches(lines, &mut self.unsolicited_responses_tx)) + pub fn fetch(&mut self, sequence_set: S1, query: S2) -> ZeroCopyResult> + where + S1: AsRef, + S2: AsRef, + { + self.run_command_and_read_response(&format!( + "FETCH {} {}", + sequence_set.as_ref(), + query.as_ref() + )) + .and_then(|lines| parse_fetches(lines, &mut self.unsolicited_responses_tx)) } /// Equivalent to [`Session::fetch`], except that all identifiers in `sequence_set` are /// [`Uid`]s. See also the [`UID` command](https://tools.ietf.org/html/rfc3501#section-6.4.8). - pub fn uid_fetch(&mut self, uid_set: &str, query: &str) -> ZeroCopyResult> { - self.run_command_and_read_response(&format!("UID FETCH {} {}", uid_set, query)) - .and_then(|lines| parse_fetches(lines, &mut self.unsolicited_responses_tx)) + pub fn uid_fetch(&mut self, uid_set: S1, query: S2) -> ZeroCopyResult> + where + S1: AsRef, + S2: AsRef, + { + self.run_command_and_read_response(&format!( + "UID FETCH {} {}", + uid_set.as_ref(), + query.as_ref() + )) + .and_then(|lines| parse_fetches(lines, &mut self.unsolicited_responses_tx)) } /// Noop always succeeds, and it does nothing. @@ -511,8 +537,8 @@ impl Session { /// the mailbox UNLESS the new incarnation has a different unique identifier validity value. /// See the description of the [`UID` /// command](https://tools.ietf.org/html/rfc3501#section-6.4.8) for more detail. - pub fn create(&mut self, mailbox_name: &str) -> Result<()> { - self.run_command_and_check_ok(&format!("CREATE {}", validate_str(mailbox_name)?)) + pub fn create>(&mut self, mailbox_name: S) -> Result<()> { + self.run_command_and_check_ok(&format!("CREATE {}", validate_str(mailbox_name.as_ref())?)) } /// The [`DELETE` command](https://tools.ietf.org/html/rfc3501#section-6.3.4) permanently @@ -534,8 +560,8 @@ impl Session { /// incarnation, UNLESS the new incarnation has a different unique identifier validity value. /// See the description of the [`UID` /// command](https://tools.ietf.org/html/rfc3501#section-6.4.8) for more detail. - pub fn delete(&mut self, mailbox_name: &str) -> Result<()> { - self.run_command_and_check_ok(&format!("DELETE {}", validate_str(mailbox_name)?)) + pub fn delete>(&mut self, mailbox_name: S) -> Result<()> { + self.run_command_and_check_ok(&format!("DELETE {}", validate_str(mailbox_name.as_ref())?)) } /// The [`RENAME` command](https://tools.ietf.org/html/rfc3501#section-6.3.5) changes the name @@ -563,8 +589,12 @@ impl Session { /// to a new mailbox with the given name, leaving `INBOX` empty. If the server implementation /// supports inferior hierarchical names of `INBOX`, these are unaffected by a rename of /// `INBOX`. - pub fn rename(&mut self, from: &str, to: &str) -> Result<()> { - self.run_command_and_check_ok(&format!("RENAME {} {}", quote!(from), quote!(to))) + pub fn rename, S2: AsRef>(&mut self, from: S1, to: S2) -> Result<()> { + self.run_command_and_check_ok(&format!( + "RENAME {} {}", + quote!(from.as_ref()), + quote!(to.as_ref()) + )) } /// The [`SUBSCRIBE` command](https://tools.ietf.org/html/rfc3501#section-6.3.6) adds the @@ -575,16 +605,16 @@ impl Session { /// The server may validate the mailbox argument to `SUBSCRIBE` to verify that it exists. /// However, it will not unilaterally remove an existing mailbox name from the subscription /// list even if a mailbox by that name no longer exists. - pub fn subscribe(&mut self, mailbox: &str) -> Result<()> { - self.run_command_and_check_ok(&format!("SUBSCRIBE {}", quote!(mailbox))) + pub fn subscribe>(&mut self, mailbox: S) -> Result<()> { + self.run_command_and_check_ok(&format!("SUBSCRIBE {}", quote!(mailbox.as_ref()))) } /// The [`UNSUBSCRIBE` command](https://tools.ietf.org/html/rfc3501#section-6.3.7) removes the /// specified mailbox name from the server's set of "active" or "subscribed" mailboxes as /// returned by [`Session::lsub`]. This command returns `Ok` only if the unsubscription is /// successful. - pub fn unsubscribe(&mut self, mailbox: &str) -> Result<()> { - self.run_command_and_check_ok(&format!("UNSUBSCRIBE {}", quote!(mailbox))) + pub fn unsubscribe>(&mut self, mailbox: S) -> Result<()> { + self.run_command_and_check_ok(&format!("UNSUBSCRIBE {}", quote!(mailbox.as_ref()))) } /// The [`CAPABILITY` command](https://tools.ietf.org/html/rfc3501#section-6.1.1) requests a @@ -625,8 +655,8 @@ impl Session { /// /// Alternatively, the client may fall back to using just [`Session::expunge`], risking the /// unintended removal of some messages. - pub fn uid_expunge(&mut self, uid_set: &str) -> Result> { - self.run_command_and_read_response(&format!("UID EXPUNGE {}", uid_set)) + pub fn uid_expunge>(&mut self, uid_set: S) -> Result> { + self.run_command_and_read_response(&format!("UID EXPUNGE {}", uid_set.as_ref())) .and_then(|lines| parse_expunge(lines, &mut self.unsolicited_responses_tx)) } @@ -692,16 +722,32 @@ impl Session { /// - `-FLAGS.SILENT `: Equivalent to `-FLAGS`, but without returning a new value. /// /// In all cases, `` is a space-separated list enclosed in parentheses. - pub fn store(&mut self, sequence_set: &str, query: &str) -> ZeroCopyResult> { - self.run_command_and_read_response(&format!("STORE {} {}", sequence_set, query)) - .and_then(|lines| parse_fetches(lines, &mut self.unsolicited_responses_tx)) + pub fn store(&mut self, sequence_set: S1, query: S2) -> ZeroCopyResult> + where + S1: AsRef, + S2: AsRef, + { + self.run_command_and_read_response(&format!( + "STORE {} {}", + sequence_set.as_ref(), + query.as_ref() + )) + .and_then(|lines| parse_fetches(lines, &mut self.unsolicited_responses_tx)) } /// Equivalent to [`Session::store`], except that all identifiers in `sequence_set` are /// [`Uid`]s. See also the [`UID` command](https://tools.ietf.org/html/rfc3501#section-6.4.8). - pub fn uid_store(&mut self, uid_set: &str, query: &str) -> ZeroCopyResult> { - self.run_command_and_read_response(&format!("UID STORE {} {}", uid_set, query)) - .and_then(|lines| parse_fetches(lines, &mut self.unsolicited_responses_tx)) + pub fn uid_store(&mut self, uid_set: S1, query: S2) -> ZeroCopyResult> + where + S1: AsRef, + S2: AsRef, + { + self.run_command_and_read_response(&format!( + "UID STORE {} {}", + uid_set.as_ref(), + query.as_ref() + )) + .and_then(|lines| parse_fetches(lines, &mut self.unsolicited_responses_tx)) } /// The [`COPY` command](https://tools.ietf.org/html/rfc3501#section-6.4.7) copies the @@ -711,14 +757,30 @@ impl Session { /// /// If the `COPY` command is unsuccessful for any reason, the server restores the destination /// mailbox to its state before the `COPY` attempt. - pub fn copy(&mut self, sequence_set: &str, mailbox_name: &str) -> Result<()> { - self.run_command_and_check_ok(&format!("COPY {} {}", sequence_set, mailbox_name)) + pub fn copy, S2: AsRef>( + &mut self, + sequence_set: S1, + mailbox_name: S2, + ) -> Result<()> { + self.run_command_and_check_ok(&format!( + "COPY {} {}", + sequence_set.as_ref(), + mailbox_name.as_ref() + )) } /// Equivalent to [`Session::copy`], except that all identifiers in `sequence_set` are /// [`Uid`]s. See also the [`UID` command](https://tools.ietf.org/html/rfc3501#section-6.4.8). - 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, S2: AsRef>( + &mut self, + uid_set: S1, + mailbox_name: S2, + ) -> Result<()> { + self.run_command_and_check_ok(&format!( + "UID COPY {} {}", + uid_set.as_ref(), + mailbox_name.as_ref() + )) } /// The [`MOVE` command](https://tools.ietf.org/html/rfc6851#section-3.1) takes two @@ -751,11 +813,15 @@ impl Session { /// orphaned). The server will generally not leave any message in both mailboxes (it would be /// bad for a partial failure to result in a bunch of duplicate messages). This is true even /// if the server returns with [`Error::No`]. - pub fn mv(&mut self, sequence_set: &str, mailbox_name: &str) -> Result<()> { + pub fn mv, S2: AsRef>( + &mut self, + sequence_set: S1, + mailbox_name: S2, + ) -> Result<()> { self.run_command_and_check_ok(&format!( "MOVE {} {}", - sequence_set, - validate_str(mailbox_name)? + sequence_set.as_ref(), + validate_str(mailbox_name.as_ref())? )) } @@ -763,11 +829,15 @@ impl Session { /// [`Uid`]s. See also the [`UID` command](https://tools.ietf.org/html/rfc3501#section-6.4.8) /// and the [semantics of `MOVE` and `UID /// MOVE`](https://tools.ietf.org/html/rfc6851#section-3.3). - pub fn uid_mv(&mut self, uid_set: &str, mailbox_name: &str) -> Result<()> { + pub fn uid_mv, S2: AsRef>( + &mut self, + uid_set: S1, + mailbox_name: S2, + ) -> Result<()> { self.run_command_and_check_ok(&format!( "UID MOVE {} {}", - uid_set, - validate_str(mailbox_name)? + uid_set.as_ref(), + validate_str(mailbox_name.as_ref())? )) } @@ -877,11 +947,15 @@ impl Session { /// - `UNSEEN`: The number of messages which do not have [`Flag::Seen`] set. /// /// `data_times` is a space-separated list enclosed in parentheses. - pub fn status(&mut self, mailbox_name: &str, data_items: &str) -> Result { + pub fn status, S2: AsRef>( + &mut self, + mailbox_name: S1, + data_items: S2, + ) -> Result { self.run_command_and_read_response(&format!( "STATUS {} {}", - validate_str(mailbox_name)?, - data_items + validate_str(mailbox_name.as_ref())?, + data_items.as_ref() )) .and_then(|lines| parse_mailbox(&lines[..], &mut self.unsolicited_responses_tx)) } @@ -927,8 +1001,13 @@ impl Session { /// Specifically, the server will generally notify the client immediately via an untagged /// `EXISTS` response. If the server does not do so, the client MAY issue a `NOOP` command (or /// failing that, a `CHECK` command) after one or more `APPEND` commands. - pub fn append(&mut self, mailbox: &str, content: &[u8]) -> Result<()> { - self.run_command(&format!("APPEND \"{}\" {{{}}}", mailbox, content.len()))?; + pub fn append, B: AsRef<[u8]>>(&mut self, mailbox: S, content: B) -> Result<()> { + let content = content.as_ref(); + self.run_command(&format!( + "APPEND \"{}\" {{{}}}", + mailbox.as_ref(), + content.len() + ))?; let mut v = Vec::new(); self.readline(&mut v)?; if !v.starts_with(b"+") { @@ -984,28 +1063,28 @@ impl Session { /// /// - `BEFORE `: Messages whose internal date (disregarding time and timezone) is earlier than the specified date. /// - `SINCE `: Messages whose internal date (disregarding time and timezone) is within or later than the specified date. - pub fn search(&mut self, query: &str) -> Result> { - self.run_command_and_read_response(&format!("SEARCH {}", query)) + pub fn search>(&mut self, query: S) -> Result> { + self.run_command_and_read_response(&format!("SEARCH {}", query.as_ref())) .and_then(|lines| parse_ids(&lines, &mut self.unsolicited_responses_tx)) } /// Equivalent to [`Session::search`], except that the returned identifiers /// are [`Uid`] instead of [`Seq`]. See also the [`UID` /// command](https://tools.ietf.org/html/rfc3501#section-6.4.8). - pub fn uid_search(&mut self, query: &str) -> Result> { - self.run_command_and_read_response(&format!("UID SEARCH {}", query)) + pub fn uid_search>(&mut self, query: S) -> Result> { + self.run_command_and_read_response(&format!("UID SEARCH {}", query.as_ref())) .and_then(|lines| parse_ids(&lines, &mut self.unsolicited_responses_tx)) } // these are only here because they are public interface, the rest is in `Connection` /// Runs a command and checks if it returns OK. - pub fn run_command_and_check_ok(&mut self, command: &str) -> Result<()> { + pub fn run_command_and_check_ok>(&mut self, command: S) -> Result<()> { self.run_command_and_read_response(command).map(|_| ()) } /// Runs any command passed to it. - pub fn run_command(&mut self, untagged_command: &str) -> Result<()> { - self.conn.run_command(untagged_command) + pub fn run_command>(&mut self, untagged_command: S) -> Result<()> { + self.conn.run_command(untagged_command.as_ref()) } /// Run a raw IMAP command and read back its response. @@ -1014,8 +1093,12 @@ impl Session { /// a selected mailbox whose status has changed. See the note on [unilateral server responses /// in RFC 3501](https://tools.ietf.org/html/rfc3501#section-7). This means that you *may* see /// additional untagged `RECENT`, `EXISTS`, `FETCH`, and `EXPUNGE` responses! - pub fn run_command_and_read_response(&mut self, untagged_command: &str) -> Result> { - self.conn.run_command_and_read_response(untagged_command) + pub fn run_command_and_read_response>( + &mut self, + untagged_command: S, + ) -> Result> { + self.conn + .run_command_and_read_response(untagged_command.as_ref()) } }