fix(all): Fix doc tests in website (#375)

This commit is contained in:
Alexis Mousset
2019-12-09 21:51:08 +00:00
committed by GitHub
parent a90b548b4f
commit 947af0acdd
14 changed files with 84 additions and 48 deletions

View File

@@ -24,7 +24,8 @@ jobs:
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --no-default-features --features=native-tls args: --no-default-features --features=native-tls,builder,r2d2,smtp-transport,file-transport,sendmail-transport
- run: rm target/debug/deps/liblettre-*
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
command: test command: test
@@ -68,7 +69,7 @@ jobs:
with: with:
command: clippy command: clippy
args: -- -D warnings args: -- -D warnings
coverage: coverage:
name: Coverage name: Coverage
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -85,8 +86,8 @@ jobs:
command: test command: test
args: --no-fail-fast args: --no-fail-fast
env: env:
CARGO_INCREMENTAL: '0' CARGO_INCREMENTAL: "0"
RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Zno-landing-pads' RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Zno-landing-pads"
- id: coverage - id: coverage
uses: actions-rs/grcov@v0.1 uses: actions-rs/grcov@v0.1
- name: Coveralls upload - name: Coveralls upload

View File

@@ -38,6 +38,7 @@ webpki = { version = "^0.21", optional = true }
criterion = "^0.3" criterion = "^0.3"
env_logger = "^0.7" env_logger = "^0.7"
glob = "^0.3" glob = "^0.3"
walkdir = "^2"
[[bench]] [[bench]]
harness = false harness = false
@@ -46,12 +47,11 @@ name = "transport_smtp"
[features] [features]
builder = ["email", "mime", "time", "base64", "uuid"] builder = ["email", "mime", "time", "base64", "uuid"]
connection-pool = ["r2d2"] connection-pool = ["r2d2"]
default = ["file-transport", "smtp-transport", "sendmail-transport", "ssl-rustls", "builder"] default = ["file-transport", "smtp-transport", "sendmail-transport", "rustls-tls", "builder"]
file-transport = ["serde", "serde_json"] file-transport = ["serde", "serde_json"]
rustls-tls = ["webpki", "rustls"]
sendmail-transport = [] sendmail-transport = []
smtp-transport = ["bufstream", "base64", "nom", "hostname"] smtp-transport = ["bufstream", "base64", "nom", "hostname"]
ssl-native = ["native-tls"]
ssl-rustls = ["rustls", "webpki"]
unstable = [] unstable = []
[[example]] [[example]]

View File

@@ -44,7 +44,7 @@ lettre = "0.9"
```rust,no_run ```rust,no_run
extern crate lettre; extern crate lettre;
use lettre::{SmtpClient, Transport, Email, mime::TEXT_PLAIN}; use lettre::{SmtpClient, Transport, Email, builder::mime::TEXT_PLAIN};
use std::path::Path; use std::path::Path;
fn main() { fn main() {

View File

@@ -1,6 +1,7 @@
use crate::{error::Error as LettreError, EmailAddress, Envelope, SendableEmail}; use crate::{error::Error as LettreError, EmailAddress, Envelope, SendableEmail};
pub use email::{Address, Header, Mailbox, MimeMessage, MimeMultipartType}; pub use email::{Address, Header, Mailbox, MimeMessage, MimeMultipartType};
use error::Error; use error::Error;
pub use mime;
use mime::Mime; use mime::Mime;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs; use std::fs;

View File

@@ -24,6 +24,8 @@ pub mod sendmail;
pub mod smtp; pub mod smtp;
pub mod stub; pub mod stub;
#[cfg(feature = "builder")]
pub use crate::builder::Email;
use crate::error::EmailResult; use crate::error::EmailResult;
use crate::error::Error; use crate::error::Error;
#[cfg(feature = "file-transport")] #[cfg(feature = "file-transport")]

View File

@@ -3,13 +3,14 @@
use crate::smtp::client::mock::MockStream; use crate::smtp::client::mock::MockStream;
use crate::smtp::error::Error; use crate::smtp::error::Error;
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
use native_tls::{Protocol, TlsConnector, TlsStream}; use native_tls::{TlsConnector, TlsStream};
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
use rustls::{ClientConfig, ClientSession}; use rustls::{ClientConfig, ClientSession};
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
use std::io::ErrorKind; use std::io::ErrorKind;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::net::{Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, TcpStream}; use std::net::{Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, TcpStream};
#[cfg(feature = "rustls")]
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
@@ -45,12 +46,6 @@ impl ClientTlsParameters {
} }
} }
/// Accepted protocols by default.
/// This removes TLS 1.0 and 1.1 compared to tls-native defaults.
/// This is also rustls' default behavior
#[cfg(feature = "native-tls")]
const DEFAULT_TLS_MIN_PROTOCOL: Protocol = Protocol::Tlsv12;
/// Represents the different types of underlying network streams /// Represents the different types of underlying network streams
pub enum NetworkStream { pub enum NetworkStream {
/// Plain TCP stream /// Plain TCP stream
@@ -161,7 +156,7 @@ impl Connector for NetworkStream {
.connector .connector
.connect(context.domain.as_ref(), tcp_stream) .connect(context.domain.as_ref(), tcp_stream)
.map(|tls| NetworkStream::Tls(Box::new(tls))) .map(|tls| NetworkStream::Tls(Box::new(tls)))
.map_err(|e| io::Error::new(ErrorKind::Other, e)), .map_err(|e| Error::Io(io::Error::new(ErrorKind::Other, e))),
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
Some(context) => { Some(context) => {
let domain = webpki::DNSNameRef::try_from_ascii_str(&context.domain)?; let domain = webpki::DNSNameRef::try_from_ascii_str(&context.domain)?;
@@ -183,7 +178,7 @@ impl Connector for NetworkStream {
.connect(tls_parameters.domain.as_ref(), stream.try_clone().unwrap()) .connect(tls_parameters.domain.as_ref(), stream.try_clone().unwrap())
{ {
Ok(tls_stream) => NetworkStream::Tls(Box::new(tls_stream)), Ok(tls_stream) => NetworkStream::Tls(Box::new(tls_stream)),
Err(err) => return Err(io::Error::new(ErrorKind::Other, err)), Err(err) => return Err(Error::Io(io::Error::new(ErrorKind::Other, err))),
}, },
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
NetworkStream::Tcp(ref mut stream) => { NetworkStream::Tcp(ref mut stream) => {

View File

@@ -41,6 +41,7 @@ pub enum Error {
/// Parsing error /// Parsing error
Parsing(nom::error::ErrorKind), Parsing(nom::error::ErrorKind),
/// Invalid hostname /// Invalid hostname
#[cfg(feature = "rustls-tls")]
InvalidDNSName(webpki::InvalidDNSNameError), InvalidDNSName(webpki::InvalidDNSNameError),
} }
@@ -73,6 +74,7 @@ impl StdError for Error {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
Tls(ref err) => err.description(), Tls(ref err) => err.description(),
Parsing(ref err) => err.description(), Parsing(ref err) => err.description(),
#[cfg(feature = "rustls-tls")]
InvalidDNSName(ref err) => err.description(), InvalidDNSName(ref err) => err.description(),
} }
} }
@@ -124,6 +126,7 @@ impl From<FromUtf8Error> for Error {
} }
} }
#[cfg(feature = "rustls-tls")]
impl From<webpki::InvalidDNSNameError> for Error { impl From<webpki::InvalidDNSNameError> for Error {
fn from(err: webpki::InvalidDNSNameError) -> Error { fn from(err: webpki::InvalidDNSNameError) -> Error {
InvalidDNSName(err) InvalidDNSName(err)

View File

@@ -17,8 +17,6 @@ use crate::smtp::authentication::{
Credentials, Mechanism, DEFAULT_ENCRYPTED_MECHANISMS, DEFAULT_UNENCRYPTED_MECHANISMS, Credentials, Mechanism, DEFAULT_ENCRYPTED_MECHANISMS, DEFAULT_UNENCRYPTED_MECHANISMS,
}; };
use crate::smtp::client::net::ClientTlsParameters; use crate::smtp::client::net::ClientTlsParameters;
#[cfg(feature = "native-tls")]
use crate::smtp::client::net::DEFAULT_TLS_MIN_PROTOCOL;
use crate::smtp::client::InnerClient; use crate::smtp::client::InnerClient;
use crate::smtp::commands::*; use crate::smtp::commands::*;
use crate::smtp::error::{Error, SmtpResult}; use crate::smtp::error::{Error, SmtpResult};
@@ -26,7 +24,9 @@ use crate::smtp::extension::{ClientId, Extension, MailBodyParameter, MailParamet
use crate::{SendableEmail, Transport}; use crate::{SendableEmail, Transport};
use log::{debug, info}; use log::{debug, info};
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
use native_tls::TlsConnector; use native_tls::{Protocol, TlsConnector};
#[cfg(feature = "rustls")]
use rustls::ClientConfig;
use std::net::{SocketAddr, ToSocketAddrs}; use std::net::{SocketAddr, ToSocketAddrs};
use std::time::Duration; use std::time::Duration;
@@ -51,6 +51,12 @@ pub const SUBMISSION_PORT: u16 = 587;
/// Default submission over TLS port /// Default submission over TLS port
pub const SUBMISSIONS_PORT: u16 = 465; pub const SUBMISSIONS_PORT: u16 = 465;
/// Accepted protocols by default.
/// This removes TLS 1.0 and 1.1 compared to tls-native defaults.
/// This is also rustls' default behavior
#[cfg(feature = "native-tls")]
const DEFAULT_TLS_MIN_PROTOCOL: Protocol = Protocol::Tlsv12;
/// How to apply TLS to a client connection /// How to apply TLS to a client connection
#[derive(Clone)] #[derive(Clone)]
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
@@ -150,6 +156,16 @@ impl SmtpClient {
) )
} }
#[cfg(feature = "rustls")]
pub fn new_simple(domain: &str) -> Result<SmtpClient, Error> {
let tls_parameters = ClientTlsParameters::new(domain.to_string(), ClientConfig::new());
SmtpClient::new(
(domain, SUBMISSIONS_PORT),
ClientSecurity::Wrapper(tls_parameters),
)
}
/// Creates a new local SMTP client to port 25 /// Creates a new local SMTP client to port 25
pub fn new_unencrypted_localhost() -> Result<SmtpClient, Error> { pub fn new_unencrypted_localhost() -> Result<SmtpClient, Error> {
SmtpClient::new(("localhost", SMTP_PORT), ClientSecurity::None) SmtpClient::new(("localhost", SMTP_PORT), ClientSecurity::None)

View File

@@ -1,23 +1,21 @@
use glob::glob; use std::env::{self, consts::EXE_EXTENSION};
use std::env;
use std::env::consts::EXE_EXTENSION;
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
use walkdir::WalkDir;
#[test] #[test]
fn book_test() { fn book_test() {
let mut book_path = env::current_dir().unwrap(); skeptic_test(Path::new("README.md"));
book_path.push(
Path::new(file!())
.parent()
.unwrap()
.parent()
.unwrap()
.join("../website/content/sending-messages"),
); // For some reasons, calling .parent() once more gives us None...
for md in glob(&format!("{}/*.md", book_path.to_str().unwrap())).unwrap() { for entry in WalkDir::new("website").into_iter().filter(|e| {
skeptic_test(&md.unwrap()); e.as_ref()
.unwrap()
.path()
.extension()
.map(|ex| ex == "md")
.unwrap_or(false)
}) {
skeptic_test(entry.unwrap().path());
} }
} }

View File

@@ -8,9 +8,11 @@ The `email` part builds email messages. For now, it does not support attachments
An email is built using an `EmailBuilder`. The simplest email could be: An email is built using an `EmailBuilder`. The simplest email could be:
```rust ```rust
extern crate lettre_email; # #[cfg(feature = "builder")]
# {
extern crate lettre;
use lettre_email::Email; use lettre::Email;
fn main() { fn main() {
// Create an email // Create an email
@@ -22,9 +24,10 @@ fn main() {
.subject("Hi, Hello world") .subject("Hi, Hello world")
.alternative("<h2>Hi, Hello world.</h2>", "Hi, Hello world.") .alternative("<h2>Hi, Hello world.</h2>", "Hi, Hello world.")
.build(); .build();
assert!(email.is_ok()); assert!(email.is_ok());
} }
# }
``` ```
When the `build` method is called, the `EmailBuilder` will add the missing headers (like When the `build` method is called, the `EmailBuilder` will add the missing headers (like

View File

@@ -5,6 +5,8 @@ The file transport writes the emails to the given directory. The name of the fil
It can be useful for testing purposes, or if you want to keep track of sent messages. It can be useful for testing purposes, or if you want to keep track of sent messages.
```rust ```rust
# #[cfg(feature = "file-transport")]
# {
extern crate lettre; extern crate lettre;
use std::env::temp_dir; use std::env::temp_dir;
@@ -23,10 +25,11 @@ fn main() {
"id".to_string(), "id".to_string(),
"Hello world".to_string().into_bytes(), "Hello world".to_string().into_bytes(),
); );
let result = sender.send(email); let result = sender.send(email);
assert!(result.is_ok()); assert!(result.is_ok());
} }
# }
``` ```
Example result in `/tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.txt`: Example result in `/tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.txt`:

View File

@@ -3,6 +3,8 @@
The sendmail transport sends the email using the local sendmail command. The sendmail transport sends the email using the local sendmail command.
```rust,no_run ```rust,no_run
# #[cfg(feature = "sendmail-transport")]
# {
extern crate lettre; extern crate lettre;
use lettre::sendmail::SendmailTransport; use lettre::sendmail::SendmailTransport;
@@ -17,9 +19,10 @@ fn main() {
"id".to_string(), "id".to_string(),
"Hello world".to_string().into_bytes(), "Hello world".to_string().into_bytes(),
); );
let mut sender = SendmailTransport::new(); let mut sender = SendmailTransport::new();
let result = sender.send(email); let result = sender.send(email);
assert!(result.is_ok()); assert!(result.is_ok());
} }
# }
``` ```

View File

@@ -18,6 +18,8 @@ The relay server can be the local email server, a specific host or a third-party
This is the most basic example of usage: This is the most basic example of usage:
```rust,no_run ```rust,no_run
# #[cfg(feature = "smtp-transport")]
# {
extern crate lettre; extern crate lettre;
use lettre::{SendableEmail, EmailAddress, Transport, Envelope, SmtpClient}; use lettre::{SendableEmail, EmailAddress, Transport, Envelope, SmtpClient};
@@ -31,20 +33,23 @@ fn main() {
"id".to_string(), "id".to_string(),
"Hello world".to_string().into_bytes(), "Hello world".to_string().into_bytes(),
); );
// Open a local connection on port 25 // Open a local connection on port 25
let mut mailer = let mut mailer =
SmtpClient::new_unencrypted_localhost().unwrap().transport(); SmtpClient::new_unencrypted_localhost().unwrap().transport();
// Send the email // Send the email
let result = mailer.send(email); let result = mailer.send(email);
assert!(result.is_ok()); assert!(result.is_ok());
} }
# }
``` ```
#### Complete example #### Complete example
```rust,no_run ```rust,no_run
# #[cfg(feature = "smtp-transport")]
# {
extern crate lettre; extern crate lettre;
use lettre::smtp::authentication::{Credentials, Mechanism}; use lettre::smtp::authentication::{Credentials, Mechanism};
@@ -61,7 +66,7 @@ fn main() {
"id1".to_string(), "id1".to_string(),
"Hello world".to_string().into_bytes(), "Hello world".to_string().into_bytes(),
); );
let email_2 = SendableEmail::new( let email_2 = SendableEmail::new(
Envelope::new( Envelope::new(
Some(EmailAddress::new("user@localhost".to_string()).unwrap()), Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
@@ -70,7 +75,7 @@ fn main() {
"id2".to_string(), "id2".to_string(),
"Hello world a second time".to_string().into_bytes(), "Hello world a second time".to_string().into_bytes(),
); );
// Connect to a remote server on a custom port // Connect to a remote server on a custom port
let mut mailer = SmtpClient::new_simple("server.tld").unwrap() let mut mailer = SmtpClient::new_simple("server.tld").unwrap()
// Set the name sent during EHLO/HELO, default is `localhost` // Set the name sent during EHLO/HELO, default is `localhost`
@@ -83,22 +88,25 @@ fn main() {
.authentication_mechanism(Mechanism::Plain) .authentication_mechanism(Mechanism::Plain)
// Enable connection reuse // Enable connection reuse
.connection_reuse(ConnectionReuseParameters::ReuseUnlimited).transport(); .connection_reuse(ConnectionReuseParameters::ReuseUnlimited).transport();
let result_1 = mailer.send(email_1); let result_1 = mailer.send(email_1);
assert!(result_1.is_ok()); assert!(result_1.is_ok());
// The second email will use the same connection // The second email will use the same connection
let result_2 = mailer.send(email_2); let result_2 = mailer.send(email_2);
assert!(result_2.is_ok()); assert!(result_2.is_ok());
// Explicitly close the SMTP transaction as we enabled connection reuse // Explicitly close the SMTP transaction as we enabled connection reuse
mailer.close(); mailer.close();
} }
# }
``` ```
You can specify custom TLS settings: You can specify custom TLS settings:
```rust,no_run ```rust,no_run
# #[cfg(feature = "native-tls")]
# {
extern crate native_tls; extern crate native_tls;
extern crate lettre; extern crate lettre;
@@ -144,6 +152,7 @@ fn main() {
mailer.close(); mailer.close();
} }
# }
``` ```
#### Lower level #### Lower level
@@ -152,6 +161,8 @@ You can also send commands, here is a simple email transaction without
error handling: error handling:
```rust,no_run ```rust,no_run
# #[cfg(feature = "smtp-transport")]
# {
extern crate lettre; extern crate lettre;
use lettre::EmailAddress; use lettre::EmailAddress;
@@ -175,5 +186,5 @@ fn main() {
let _ = email_client.message(Box::new("Test email".as_bytes())); let _ = email_client.message(Box::new("Test email".as_bytes()));
let _ = email_client.command(QuitCommand); let _ = email_client.command(QuitCommand);
} }
# }
``` ```

View File

@@ -18,7 +18,7 @@ fn main() {
"id".to_string(), "id".to_string(),
"Hello world".to_string().into_bytes(), "Hello world".to_string().into_bytes(),
); );
let mut sender = StubTransport::new_positive(); let mut sender = StubTransport::new_positive();
let result = sender.send(email); let result = sender.send(email);
assert!(result.is_ok()); assert!(result.is_ok());