From 3cf89935afc361c2c75fbd6a89717faa6e469778 Mon Sep 17 00:00:00 2001 From: Manuel Pelloni Date: Tue, 1 Sep 2020 21:58:34 +0200 Subject: [PATCH] Improve documentation --- src/address.rs | 2 +- src/lib.rs | 19 +++-- src/message/header/mailbox.rs | 12 +-- src/message/mailbox/types.rs | 6 -- src/message/mod.rs | 6 +- src/transport/file/mod.rs | 77 +++++++++++++++--- src/transport/sendmail/mod.rs | 52 ++++++++++-- src/transport/smtp/mod.rs | 148 +++------------------------------- 8 files changed, 146 insertions(+), 176 deletions(-) diff --git a/src/address.rs b/src/address.rs index 665c82c..d212188 100644 --- a/src/address.rs +++ b/src/address.rs @@ -169,7 +169,7 @@ impl Display for AddressError { } #[cfg(feature = "serde")] -pub mod serde { +mod serde { use crate::address::Address; use serde::{ de::{Deserializer, Error as DeError, MapAccess, Visitor}, diff --git a/src/lib.rs b/src/lib.rs index faa55b6..7f5cfc2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,10 @@ //! * **sendmail-transport**: Transport over SMTP //! * **rustls-tls**: TLS support with the `rustls` crate //! * **native-tls**: TLS support with the `native-tls` crate +//! * **tokio02**: Allow to asyncronously send emails using tokio 0.2.x +//! * **tokio02-rustls-tls**: Async TLS support with the `rustls` crate using tokio 0.2 +//! * **tokio02-native-tls**: Async TLS support with the `native-tls` crate using tokio 0.2 +//! * **async-std1**: Allow to asyncronously send emails using async-std 1.x (SMTP isn't supported yet) //! * **r2d2**: Connection pool for SMTP transport //! * **tracing**: Logging using the `tracing` crate //! * **serde**: Serialization/Deserialization of entities @@ -203,8 +207,10 @@ mod test { #[test] fn envelope_from_headers() { - let from = Mailboxes::new().with("kayo@example.com".parse().unwrap()); - let to = Mailboxes::new().with("amousset@example.com".parse().unwrap()); + let mut from = Mailboxes::new(); + from.push("kayo@example.com".parse().unwrap()); + let mut to = Mailboxes::new(); + to.push("amousset@example.com".parse().unwrap()); let mut headers = Headers::new(); headers.set(header::From(from)); @@ -222,9 +228,11 @@ mod test { #[test] fn envelope_from_headers_sender() { - let from = Mailboxes::new().with("kayo@example.com".parse().unwrap()); + let mut from = Mailboxes::new(); + from.push("kayo@example.com".parse().unwrap()); let sender = Mailbox::new(None, "kayo2@example.com".parse().unwrap()); - let to = Mailboxes::new().with("amousset@example.com".parse().unwrap()); + let mut to = Mailboxes::new(); + to.push("amousset@example.com".parse().unwrap()); let mut headers = Headers::new(); headers.set(header::From(from)); @@ -243,7 +251,8 @@ mod test { #[test] fn envelope_from_headers_no_to() { - let from = Mailboxes::new().with("kayo@example.com".parse().unwrap()); + let mut from = Mailboxes::new(); + from.push("kayo@example.com".parse().unwrap()); let sender = Mailbox::new(None, "kayo2@example.com".parse().unwrap()); let mut headers = Headers::new(); diff --git a/src/message/header/mailbox.rs b/src/message/header/mailbox.rs index 913c755..b946e5d 100644 --- a/src/message/header/mailbox.rs +++ b/src/message/header/mailbox.rs @@ -169,7 +169,8 @@ mod test { #[test] fn format_single_without_name() { - let from = Mailboxes::new().with("kayo@example.com".parse().unwrap()); + let mut from = Mailboxes::new(); + from.push("kayo@example.com".parse().unwrap()); let mut headers = Headers::new(); headers.set(From(from)); @@ -179,7 +180,8 @@ mod test { #[test] fn format_single_with_name() { - let from = Mailboxes::new().with("K. ".parse().unwrap()); + let mut from = Mailboxes::new(); + from.push("K. ".parse().unwrap()); let mut headers = Headers::new(); headers.set(From(from)); @@ -189,9 +191,9 @@ mod test { #[test] fn format_multi_without_name() { - let from = Mailboxes::new() - .with("kayo@example.com".parse().unwrap()) - .with("pony@domain.tld".parse().unwrap()); + let mut from = Mailboxes::new(); + from.push("kayo@example.com".parse().unwrap()); + from.push("pony@domain.tld".parse().unwrap()); let mut headers = Headers::new(); headers.set(From(from)); diff --git a/src/message/mailbox/types.rs b/src/message/mailbox/types.rs index 305d20c..bdcccc0 100644 --- a/src/message/mailbox/types.rs +++ b/src/message/mailbox/types.rs @@ -113,12 +113,6 @@ impl Mailboxes { Mailboxes(Vec::new()) } - /// Add mailbox to a list - pub fn with(mut self, mbox: Mailbox) -> Self { - self.0.push(mbox); - self - } - /// Add mailbox to a list pub fn push(&mut self, mbox: Mailbox) { self.0.push(mbox); diff --git a/src/message/mod.rs b/src/message/mod.rs index 9b26444..d7c6b0d 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -12,7 +12,7 @@ //! //! The easiest way how we can create email message with simple string. //! -//! ```rust +//! ```rust,no_run //! # extern crate lettre; //! use lettre::message::Message; //! @@ -44,7 +44,7 @@ //! //! The more complex way is using MIME contents. //! -//! ```rust +//! ```rust,no_run //! # extern crate lettre; //! use lettre::message::{header, Message, SinglePart, Part}; //! @@ -82,7 +82,7 @@ //! //! And more advanced way of building message by using multipart MIME contents. //! -//! ```rust +//! ```rust,no_run //! # extern crate lettre; //! use lettre::message::{header, Message, MultiPart, SinglePart, Part}; //! diff --git a/src/transport/file/mod.rs b/src/transport/file/mod.rs index 6c463a3..6d0a87b 100644 --- a/src/transport/file/mod.rs +++ b/src/transport/file/mod.rs @@ -1,16 +1,10 @@ //! The file transport writes the emails to the given directory. The name of the file will be -//! `message_id.txt`. -//! It can be useful for testing purposes, or if you want to keep track of sent messages. -//! -//! #### File Transport -//! -//! The file transport writes the emails to the given directory. The name of the file will be //! `message_id.json`. //! It can be useful for testing purposes, or if you want to keep track of sent messages. //! -//! ```rust -//! # #[cfg(feature = "file-transport")] -//! # { +//! ## Sync example +//! +//! ```rust,no_run //! use std::env::temp_dir; //! use lettre::{Transport, Envelope, Message, FileTransport}; //! @@ -26,20 +20,77 @@ //! //! let result = sender.send(&email); //! assert!(result.is_ok()); +//! ``` +//! +//! ## Async tokio 0.2 +//! +//! ```rust,no_run +//! # #[cfg(feature = "tokio02")] +//! # async fn run() { +//! use std::env::temp_dir; +//! use lettre::{Tokio02Transport, Envelope, Message, FileTransport}; +//! +//! // Write to the local temp directory +//! let sender = FileTransport::new(temp_dir()); +//! let email = Message::builder() +//! .from("NoBody ".parse().unwrap()) +//! .reply_to("Yuin ".parse().unwrap()) +//! .to("Hei ".parse().unwrap()) +//! .subject("Happy new year") +//! .body("Be happy!") +//! .unwrap(); +//! +//! let result = sender.send(email).await; +//! assert!(result.is_ok()); //! # } //! ``` //! -//! Example result in `/tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.json`: +//! ## Async async-std 1.x +//! +//! ```rust,no_run +//! # #[cfg(feature = "async-std1")] +//! # async fn run() { +//! use std::env::temp_dir; +//! use lettre::{AsyncStd1Transport, Envelope, Message, FileTransport}; +//! +//! // Write to the local temp directory +//! let sender = FileTransport::new(temp_dir()); +//! let email = Message::builder() +//! .from("NoBody ".parse().unwrap()) +//! .reply_to("Yuin ".parse().unwrap()) +//! .to("Hei ".parse().unwrap()) +//! .subject("Happy new year") +//! .body("Be happy!") +//! .unwrap(); +//! +//! let result = sender.send(email).await; +//! assert!(result.is_ok()); +//! # } +//! ``` +//! +//! --- +//! +//! Example result //! //! ```json -//! TODO +//! { +//! "envelope": { +//! "forward_path": [ +//! "hei@domain.tld" +//! ], +//! "reverse_path": "nobody@domain.tld" +//! }, +//! "raw_message": null, +//! "message": "From: NoBody \r\nReply-To: Yuin \r\nTo: Hei \r\nSubject: Happy new year\r\nDate: Tue, 18 Aug 2020 22:50:17 GMT\r\n\r\nBe happy!" +//! } //! ``` +pub use self::error::Error; #[cfg(feature = "async-std1")] use crate::AsyncStd1Transport; #[cfg(feature = "tokio02")] use crate::Tokio02Transport; -use crate::{transport::file::error::Error, Envelope, Transport}; +use crate::{Envelope, Transport}; #[cfg(any(feature = "async-std1", feature = "tokio02"))] use async_trait::async_trait; use std::{ @@ -48,7 +99,7 @@ use std::{ }; use uuid::Uuid; -pub mod error; +mod error; type Id = String; diff --git a/src/transport/sendmail/mod.rs b/src/transport/sendmail/mod.rs index fb8e828..65d8803 100644 --- a/src/transport/sendmail/mod.rs +++ b/src/transport/sendmail/mod.rs @@ -1,12 +1,8 @@ //! The sendmail transport sends the email using the local sendmail command. //! -//! #### Sendmail Transport -//! -//! The sendmail transport sends the email using the local sendmail command. +//! ## Sync example //! //! ```rust,no_run -//! # #[cfg(feature = "sendmail-transport")] -//! # { //! use lettre::{Message, Envelope, Transport, SendmailTransport}; //! //! let email = Message::builder() @@ -20,14 +16,56 @@ //! let sender = SendmailTransport::new(); //! let result = sender.send(&email); //! assert!(result.is_ok()); +//! ``` +//! +//! ## Async tokio 0.2 example +//! +//!```rust,no_run +//! # #[cfg(feature = "tokio02")] +//! # async fn run() { +//! use lettre::{Message, Envelope, Tokio02Transport, SendmailTransport}; +//! +//! let email = Message::builder() +//! .from("NoBody ".parse().unwrap()) +//! .reply_to("Yuin ".parse().unwrap()) +//! .to("Hei ".parse().unwrap()) +//! .subject("Happy new year") +//! .body("Be happy!") +//! .unwrap(); +//! +//! let sender = SendmailTransport::new(); +//! let result = sender.send(email).await; +//! assert!(result.is_ok()); +//! # } +//! ``` +//! +//! ## Async async-std 1.x example +//! +//!```rust,no_run +//! # #[cfg(feature = "async-std1")] +//! # async fn run() { +//! use lettre::{Message, Envelope, AsyncStd1Transport, SendmailTransport}; +//! +//! let email = Message::builder() +//! .from("NoBody ".parse().unwrap()) +//! .reply_to("Yuin ".parse().unwrap()) +//! .to("Hei ".parse().unwrap()) +//! .subject("Happy new year") +//! .body("Be happy!") +//! .unwrap(); +//! +//! let sender = SendmailTransport::new(); +//! let result = sender.send(email).await; +//! assert!(result.is_ok()); //! # } //! ``` +pub use self::error::Error; #[cfg(feature = "async-std1")] use crate::AsyncStd1Transport; #[cfg(feature = "tokio02")] use crate::Tokio02Transport; -use crate::{transport::sendmail::error::Error, Envelope, Transport}; +use crate::{Envelope, Transport}; #[cfg(any(feature = "async-std1", feature = "tokio02"))] use async_trait::async_trait; use std::{ @@ -37,7 +75,7 @@ use std::{ process::{Command, Stdio}, }; -pub mod error; +mod error; const DEFAUT_SENDMAIL: &str = "/usr/sbin/sendmail"; diff --git a/src/transport/smtp/mod.rs b/src/transport/smtp/mod.rs index 94ed6c9..b473656 100644 --- a/src/transport/smtp/mod.rs +++ b/src/transport/smtp/mod.rs @@ -8,8 +8,8 @@ //! It implements the following extensions: //! //! * 8BITMIME ([RFC 6152](https://tools.ietf.org/html/rfc6152)) -//! * AUTH ([RFC 4954](http://tools.ietf.org/html/rfc4954)) with PLAIN, LOGIN and XOAUTH2 mechanisms -//! * STARTTLS ([RFC 2487](http://tools.ietf.org/html/rfc2487)) +//! * AUTH ([RFC 4954](https://tools.ietf.org/html/rfc4954)) with PLAIN, LOGIN and XOAUTH2 mechanisms +//! * STARTTLS ([RFC 2487](https://tools.ietf.org/html/rfc2487)) //! //! #### SMTP Transport //! @@ -17,12 +17,12 @@ //! //! It is designed to be: //! -//! * Secured: email are encrypted by default -//! * Modern: unicode support for email content and sender/recipient addresses when compatible +//! * Secured: emails are encrypted by default +//! * Modern: unicode support for email contents and sender/recipient addresses when compatible //! * Fast: supports connection reuse and pooling //! //! This client is designed to send emails to a relay server, and should *not* be used to send -//! emails directly to the destination. +//! emails directly to the destination server. //! //! The relay server can be the local email server, a specific host or a third-party service. //! @@ -31,7 +31,7 @@ //! This is the most basic example of usage: //! //! ```rust,no_run -//! # #[cfg(feature = "smtp-transport")] +//! # #[cfg(any(feature = "native-tls", feature = "rustls-tls"))] //! # { //! use lettre::{Message, Transport, SmtpTransport}; //! @@ -43,138 +43,15 @@ //! .body("Be happy!") //! .unwrap(); //! -//! // Create local transport on port 25 -//! let sender = SmtpTransport::unencrypted_localhost(); -//! // Send the email on local relay +//! // Create TLS transport on port 465 +//! let sender = SmtpTransport::relay("smtp.example.com") +//! .expect("relay valid") +//! .build(); +//! // Send the email via remote relay //! let result = sender.send(&email); -//! //! assert!(result.is_ok()); //! # } //! ``` -//! -//! #### Complete example -//! -//! ```todo -//! # #[cfg(feature = "smtp-transport")] -//! # { -//! use lettre::transport::smtp::authentication::{Credentials, Mechanism}; -//! use lettre::{Email, Envelope, Transport, SmtpClient}; -//! use lettre::transport::smtp::extension::ClientId; -//! -//! let email_1 = Email::new( -//! Envelope::new( -//! Some(EmailAddress::new("user@localhost".to_string()).unwrap()), -//! vec![EmailAddress::new("root@localhost".to_string()).unwrap()], -//! ).unwrap(), -//! "id1".to_string(), -//! "Hello world".to_string().into_bytes(), -//! ); -//! -//! let email_2 = Email::new( -//! Envelope::new( -//! Some(EmailAddress::new("user@localhost".to_string()).unwrap()), -//! vec![EmailAddress::new("root@localhost".to_string()).unwrap()], -//! ).unwrap(), -//! "id2".to_string(), -//! "Hello world a second time".to_string().into_bytes(), -//! ); -//! -//! // Connect to a remote server on a custom port -//! let mut mailer = SmtpClient::new_simple("server.tld").unwrap() -//! // Set the name sent during EHLO/HELO, default is `localhost` -//! .hello_name(ClientId::Domain("my.hostname.tld".to_string())) -//! // Add credentials for authentication -//! .credentials(Credentials::new("username".to_string(), "password".to_string())) -//! // Enable SMTPUTF8 if the server supports it -//! .smtp_utf8(true) -//! // Configure expected authentication mechanism -//! .authentication_mechanism(Mechanism::Plain) -//! // Enable connection reuse -//! .connection_reuse(ConnectionReuseParameters::ReuseUnlimited).transport(); -//! -//! let result_1 = mailer.send(&email_1); -//! assert!(result_1.is_ok()); -//! -//! // The second email will use the same connection -//! let result_2 = mailer.send(&email_2); -//! assert!(result_2.is_ok()); -//! -//! // Explicitly close the SMTP transaction as we enabled connection reuse -//! mailer.close(); -//! # } -//! ``` -//! -//! You can specify custom TLS settings: -//! -//! ```todo -//! # #[cfg(feature = "native-tls")] -//! # { -//! use lettre::{ -//! ClientSecurity, ClientTlsParameters, EmailAddress, Envelope, -//! Email, SmtpClient, Transport, -//! }; -//! use lettre::transport::smtp::authentication::{Credentials, Mechanism}; -//! use lettre::transport::smtp::ConnectionReuseParameters; -//! use native_tls::{Protocol, TlsConnector}; -//! -//! let email = Email::new( -//! Envelope::new( -//! Some(EmailAddress::new("user@localhost".to_string()).unwrap()), -//! vec![EmailAddress::new("root@localhost".to_string()).unwrap()], -//! ).unwrap(), -//! "message_id".to_string(), -//! "Hello world".to_string().into_bytes(), -//! ); -//! -//! let mut tls_builder = TlsConnector::builder(); -//! tls_builder.min_protocol_version(Some(Protocol::Tlsv10)); -//! let tls_parameters = -//! ClientTlsParameters::new( -//! "smtp.example.com".to_string(), -//! tls_builder.build().unwrap() -//! ); -//! -//! let mut mailer = SmtpClient::new( -//! ("smtp.example.com", 465), ClientSecurity::Wrapper(tls_parameters) -//! ).unwrap() -//! .authentication_mechanism(Mechanism::Login) -//! .credentials(Credentials::new( -//! "example_username".to_string(), "example_password".to_string() -//! )) -//! .connection_reuse(ConnectionReuseParameters::ReuseUnlimited) -//! .transport(); -//! -//! let result = mailer.send(&email); -//! -//! assert!(result.is_ok()); -//! -//! mailer.close(); -//! # } -//! ``` -//! -//! #### Lower level -//! -//! You can also send commands, here is a simple email transaction without -//! error handling: -//! -//! ```rust,no_run -//! # #[cfg(feature = "smtp-transport")] -//! # { -//! use lettre::transport::smtp::{SMTP_PORT, extension::ClientId, commands::*, client::SmtpConnection}; -//! -//! let hello = ClientId::Domain("my_hostname".to_string()); -//! let mut client = SmtpConnection::connect(&("localhost", SMTP_PORT), None, &hello, None).unwrap(); -//! client.command( -//! Mail::new(Some("user@example.com".parse().unwrap()), vec![]) -//! ).unwrap(); -//! client.command( -//! Rcpt::new("user@example.org".parse().unwrap(), vec![]) -//! ).unwrap(); -//! client.command(Data).unwrap(); -//! client.message("Test email".as_bytes()).unwrap(); -//! client.command(Quit).unwrap(); -//! # } -//! ``` use std::time::Duration; @@ -189,7 +66,6 @@ use crate::transport::smtp::client::TlsParameters; use crate::transport::smtp::{ authentication::{Credentials, Mechanism, DEFAULT_MECHANISMS}, client::SmtpConnection, - error::Error, extension::ClientId, response::Response, }; @@ -200,7 +76,7 @@ mod async_transport; pub mod authentication; pub mod client; pub mod commands; -pub mod error; +mod error; pub mod extension; #[cfg(feature = "r2d2")] pub mod pool;