Compare commits

...

10 Commits

Author SHA1 Message Date
Alexis Mousset
75ab05229a feat(all): v0.9.2 release 2019-06-11 19:58:32 +02:00
Alexis Mousset
393ef8dcd1 Simplify header formatting and fix nightly build (fixes #340) 2019-06-11 19:48:01 +02:00
Alexis Mousset
ceb57edfdd Remove failure crate usage (fixes #331) 2019-05-06 09:36:10 +02:00
Alexis Mousset
cf8f934c56 fix(docs): Broken title syntax in SMTP docs 2019-05-05 21:01:36 +02:00
Alexis Mousset
df949f837e fix(all): Properly override favicon in docs theme 2019-05-05 20:49:12 +02:00
Alexis Mousset
0a3d51dc25 fix(docs): Use doc root and set custom favicon 2019-05-05 20:15:45 +02:00
Alexis Mousset
4828cf4e92 feat(all): Require rust 1.32 2019-05-05 20:07:38 +02:00
Alexis Mousset
c33de49fbb feat(all): Move to mdBook for the docs 2019-05-05 19:45:51 +02:00
Alexis Mousset
4f470a2c3f feat(all): 0.9.1 release 2019-05-05 18:20:30 +02:00
Alexis Mousset
a0c8fb947c fix(email): Re-export mime crate 2019-05-05 18:16:05 +02:00
27 changed files with 192 additions and 107 deletions

View File

@@ -4,7 +4,7 @@ set -xe
cd website cd website
make clean && make make clean && make
echo "lettre.at" > _book/CNAME echo "lettre.at" > _book/html/CNAME
sudo pip install ghp-import sudo pip install ghp-import
ghp-import -n _book ghp-import -n _book/html
git push -f https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages git push -f https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages

View File

@@ -3,7 +3,7 @@ rust:
- stable - stable
- beta - beta
- nightly - nightly
- 1.26.0 - 1.32.0
matrix: matrix:
allow_failures: allow_failures:
- rust: nightly - rust: nightly
@@ -19,13 +19,11 @@ addons:
- gcc - gcc
- binutils-dev - binutils-dev
- libiberty-dev - libiberty-dev
- npm
before_script: before_script:
- smtp-sink 2525 1000& - smtp-sink 2525 1000&
- sudo chgrp -R postdrop /var/spool/postfix/maildrop - sudo chgrp -R postdrop /var/spool/postfix/maildrop
- sudo npm set strict-ssl false && sudo npm install -g gitbook-cli
script: script:
- cargo test --verbose --all --all-features - cargo test --verbose --all --all-features
after_success: after_success:
- ./.build-scripts/codecov.sh - ./.build-scripts/codecov.sh
- '[ "$TRAVIS_BRANCH" = "v0.9.x" ] && [ $TRAVIS_PULL_REQUEST = false ] && ./.build-scripts/site-upload.sh' - '[ "$TRAVIS_RUST_VERSION" = "stable" ] && [ "$TRAVIS_BRANCH" = "v0.9.x" ] && [ $TRAVIS_PULL_REQUEST = false ] && ./.build-scripts/site-upload.sh'

View File

@@ -1,3 +1,19 @@
<a name="v0.9.2"></a>
### v0.9.2 (2019-06-11)
#### Bug Fixes
* **email:**
* Fix compilation with Rust 1.36+ ([393ef8d](https://github.com/lettre/lettre/commit/393ef8dcd1b1c6a6119d0666d5f09b12f50f6b4b))
<a name="v0.9.1"></a>
### v0.9.1 (2019-05-05)
#### Features
* **email:**
* Re-export mime crate ([a0c8fb9](https://github.com/lettre/lettre/commit/a0c8fb9))
<a name="v0.9.0"></a> <a name="v0.9.0"></a>
### v0.9.0 (2019-03-17) ### v0.9.0 (2019-03-17)

View File

@@ -34,7 +34,7 @@ Lettre provides the following features:
## Example ## Example
This library requires Rust 1.26 or newer. This library requires Rust 1.32 or newer.
To use this library, add the following to your `Cargo.toml`: To use this library, add the following to your `Cargo.toml`:
```toml ```toml

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "lettre" name = "lettre"
version = "0.9.0" # remember to update html_root_url version = "0.9.2" # remember to update html_root_url
description = "Email client" description = "Email client"
readme = "README.md" readme = "README.md"
homepage = "http://lettre.at" homepage = "http://lettre.at"
@@ -28,8 +28,6 @@ hostname = { version = "^0.1", optional = true }
serde = { version = "^1.0", optional = true } serde = { version = "^1.0", optional = true }
serde_json = { version = "^1.0", optional = true } serde_json = { version = "^1.0", optional = true }
serde_derive = { version = "^1.0", optional = true } serde_derive = { version = "^1.0", optional = true }
failure = "^0.1"
failure_derive = "^0.1"
fast_chemail = "^0.9" fast_chemail = "^0.9"
r2d2 = { version = "^0.8", optional = true} r2d2 = { version = "^0.8", optional = true}

View File

@@ -1,18 +1,35 @@
use failure; use std::{
error::Error as StdError,
fmt::{self, Display, Formatter},
};
use self::Error::*;
/// Error type for email content /// Error type for email content
#[derive(Fail, Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum Error { pub enum Error {
/// Missing from in envelope /// Missing from in envelope
#[fail(display = "missing source address, invalid envelope")]
MissingFrom, MissingFrom,
/// Missing to in envelope /// Missing to in envelope
#[fail(display = "missing destination address, invalid envelope")]
MissingTo, MissingTo,
/// Invalid email /// Invalid email
#[fail(display = "invalid email address")]
InvalidEmailAddress, InvalidEmailAddress,
} }
impl Display for Error {
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> {
fmt.write_str(&match *self {
MissingFrom => "missing source address, invalid envelope".to_owned(),
MissingTo => "missing destination address, invalid envelope".to_owned(),
InvalidEmailAddress => "invalid email address".to_owned(),
})
}
}
impl StdError for Error {
fn cause(&self) -> Option<&dyn StdError> {
None
}
}
/// Email result type /// Email result type
pub type EmailResult<T> = Result<T, failure::Error>; pub type EmailResult<T> = Result<T, Error>;

View File

@@ -1,40 +1,65 @@
//! Error and result type for file transport //! Error and result type for file transport
use failure; use std::{
error::Error as StdError,
fmt::{self, Display, Formatter},
};
use serde_json; use serde_json;
use std::io; use std::io;
use self::Error::*;
/// An enum of all error kinds. /// An enum of all error kinds.
#[derive(Fail, Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// Internal client error /// Internal client error
#[fail(display = "Internal client error: {}", error)] Client(&'static str),
Client { error: &'static str },
/// IO error /// IO error
#[fail(display = "IO error: {}", error)] Io(io::Error),
Io { error: io::Error },
/// JSON serialization error /// JSON serialization error
#[fail(display = "JSON serialization error: {}", error)] JsonSerialization(serde_json::Error),
JsonSerialization { error: serde_json::Error }, }
impl Display for Error {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
fmt.write_str(self.description())
}
}
impl StdError for Error {
fn description(&self) -> &str {
match *self {
Client(err) => err,
Io(ref err) => err.description(),
JsonSerialization(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&StdError> {
match *self {
Io(ref err) => Some(&*err),
JsonSerialization(ref err) => Some(&*err),
_ => None,
}
}
} }
impl From<io::Error> for Error { impl From<io::Error> for Error {
fn from(err: io::Error) -> Error { fn from(err: io::Error) -> Error {
Error::Io { error: err } Error::Io(err)
} }
} }
impl From<serde_json::Error> for Error { impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Error { fn from(err: serde_json::Error) -> Error {
Error::JsonSerialization { error: err } Error::JsonSerialization(err)
} }
} }
impl From<&'static str> for Error { impl From<&'static str> for Error {
fn from(string: &'static str) -> Error { fn from(string: &'static str) -> Error {
Error::Client { error: string } Error::Client(string)
} }
} }
/// SMTP result type /// SMTP result type
pub type FileResult = Result<(), failure::Error>; pub type FileResult = Result<(), Error>;

View File

@@ -3,7 +3,7 @@
//! This mailer contains the available transports for your emails. //! This mailer contains the available transports for your emails.
//! //!
#![doc(html_root_url = "https://docs.rs/lettre/0.9.0")] #![doc(html_root_url = "https://docs.rs/lettre/0.9.2")]
#![deny( #![deny(
missing_copy_implementations, missing_copy_implementations,
trivial_casts, trivial_casts,
@@ -30,11 +30,8 @@ extern crate serde;
#[cfg(feature = "serde-impls")] #[cfg(feature = "serde-impls")]
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
extern crate failure;
#[cfg(feature = "file-transport")] #[cfg(feature = "file-transport")]
extern crate serde_json; extern crate serde_json;
#[macro_use]
extern crate failure_derive;
extern crate fast_chemail; extern crate fast_chemail;
#[cfg(feature = "connection-pool")] #[cfg(feature = "connection-pool")]
extern crate r2d2; extern crate r2d2;
@@ -49,8 +46,7 @@ pub mod smtp;
pub mod stub; pub mod stub;
use error::EmailResult; use error::EmailResult;
use error::Error as EmailError; use error::Error;
use failure::Error;
use fast_chemail::is_valid_email; use fast_chemail::is_valid_email;
#[cfg(feature = "file-transport")] #[cfg(feature = "file-transport")]
pub use file::FileTransport; pub use file::FileTransport;
@@ -77,7 +73,7 @@ pub struct EmailAddress(String);
impl EmailAddress { impl EmailAddress {
pub fn new(address: String) -> EmailResult<EmailAddress> { pub fn new(address: String) -> EmailResult<EmailAddress> {
if !is_valid_email(&address) && !address.ends_with("localhost") { if !is_valid_email(&address) && !address.ends_with("localhost") {
Err(EmailError::InvalidEmailAddress)?; Err(Error::InvalidEmailAddress)?;
} }
Ok(EmailAddress(address)) Ok(EmailAddress(address))
} }
@@ -127,7 +123,7 @@ impl Envelope {
/// Creates a new envelope, which may fail if `to` is empty. /// Creates a new envelope, which may fail if `to` is empty.
pub fn new(from: Option<EmailAddress>, to: Vec<EmailAddress>) -> EmailResult<Envelope> { pub fn new(from: Option<EmailAddress>, to: Vec<EmailAddress>) -> EmailResult<Envelope> {
if to.is_empty() { if to.is_empty() {
Err(EmailError::MissingTo)?; Err(Error::MissingTo)?;
} }
Ok(Envelope { Ok(Envelope {
forward_path: to, forward_path: to,

View File

@@ -1,30 +1,54 @@
//! Error and result type for sendmail transport //! Error and result type for sendmail transport
use failure; use std::{
error::Error as StdError,
fmt::{self, Display, Formatter},
};
use self::Error::*;
use std::io; use std::io;
/// An enum of all error kinds. /// An enum of all error kinds.
#[derive(Fail, Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// Internal client error /// Internal client error
#[fail(display = "Internal client error: {}", error)] Client(&'static str),
Client { error: &'static str },
/// IO error /// IO error
#[fail(display = "IO error: {}", error)] Io(io::Error),
Io { error: io::Error }, }
impl Display for Error {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
fmt.write_str(self.description())
}
}
impl StdError for Error {
fn description(&self) -> &str {
match *self {
Client(err) => err,
Io(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&StdError> {
match *self {
Io(ref err) => Some(&*err),
_ => None,
}
}
} }
impl From<io::Error> for Error { impl From<io::Error> for Error {
fn from(err: io::Error) -> Error { fn from(err: io::Error) -> Error {
Error::Io { error: err } Error::Io(err)
} }
} }
impl From<&'static str> for Error { impl From<&'static str> for Error {
fn from(string: &'static str) -> Error { fn from(string: &'static str) -> Error {
Error::Client { error: string } Error::Client(string)
} }
} }
/// sendmail result type /// sendmail result type
pub type SendmailResult = Result<(), failure::Error>; pub type SendmailResult = Result<(), Error>;

View File

@@ -72,9 +72,7 @@ impl<'a> Transport<'a> for SendmailTransport {
Ok(()) Ok(())
} else { } else {
// TODO display stderr // TODO display stderr
Err(error::Error::Client { Err(error::Error::Client("The message could not be sent"))?
error: "The message could not be sent",
})?
} }
} }
} }

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "lettre_email" name = "lettre_email"
version = "0.9.0" # remember to update html_root_url version = "0.9.2" # remember to update html_root_url
description = "Email builder" description = "Email builder"
readme = "README.md" readme = "README.md"
homepage = "http://lettre.at" homepage = "http://lettre.at"
@@ -29,5 +29,3 @@ time = "^0.1"
uuid = { version = "^0.7", features = ["v4"] } uuid = { version = "^0.7", features = ["v4"] }
lettre = { version = "^0.9", path = "../lettre", default-features = false } lettre = { version = "^0.9", path = "../lettre", default-features = false }
base64 = "^0.10" base64 = "^0.10"
failure = "^0.1"
failure_derive = "^0.1"

View File

@@ -2,35 +2,52 @@
use lettre; use lettre;
use std::io; use std::io;
use std::{
error::Error as StdError,
fmt::{self, Display, Formatter},
};
use self::Error::*;
/// An enum of all error kinds. /// An enum of all error kinds.
#[derive(Debug, Fail)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// Envelope error /// Envelope error
#[fail(display = "lettre error: {}", error)] Envelope(lettre::error::Error),
Envelope {
/// inner error
error: lettre::error::Error,
},
/// Unparseable filename for attachment /// Unparseable filename for attachment
#[fail(display = "the attachment filename could not be parsed")]
CannotParseFilename, CannotParseFilename,
/// IO error /// IO error
#[fail(display = "IO error: {}", error)] Io(io::Error),
Io { }
/// inner error
error: io::Error, impl Display for Error {
}, fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> {
fmt.write_str(&match *self {
CannotParseFilename => "Could not parse attachment filename".to_owned(),
Io(ref err) => err.to_string(),
Envelope(ref err) => err.to_string(),
})
}
}
impl StdError for Error {
fn cause(&self) -> Option<&dyn StdError> {
match *self {
Envelope(ref err) => Some(err),
Io(ref err) => Some(err),
_ => None,
}
}
} }
impl From<io::Error> for Error { impl From<io::Error> for Error {
fn from(err: io::Error) -> Error { fn from(err: io::Error) -> Error {
Error::Io { error: err } Error::Io(err)
} }
} }
impl From<lettre::error::Error> for Error { impl From<lettre::error::Error> for Error {
fn from(err: lettre::error::Error) -> Error { fn from(err: lettre::error::Error) -> Error {
Error::Envelope { error: err } Error::Envelope(err)
} }
} }

View File

@@ -1,7 +1,7 @@
//! Lettre is a mailer written in Rust. lettre_email provides a simple email builder. //! Lettre is a mailer written in Rust. lettre_email provides a simple email builder.
//! //!
#![doc(html_root_url = "https://docs.rs/lettre_email/0.9.0")] #![doc(html_root_url = "https://docs.rs/lettre_email/0.9.2")]
#![deny( #![deny(
missing_docs, missing_docs,
missing_debug_implementations, missing_debug_implementations,
@@ -13,22 +13,17 @@
unused_import_braces unused_import_braces
)] )]
extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate base64; extern crate base64;
extern crate email as email_format; extern crate email as email_format;
extern crate lettre; extern crate lettre;
extern crate mime;
extern crate time; extern crate time;
extern crate uuid; extern crate uuid;
pub extern crate mime;
pub mod error; pub mod error;
pub use email_format::{Address, Header, Mailbox, MimeMessage, MimeMultipartType}; pub use email_format::{Address, Header, Mailbox, MimeMessage, MimeMultipartType};
use error::Error as EmailError; use error::Error;
use failure::Error;
use lettre::{error::Error as LettreError, EmailAddress, Envelope, SendableEmail}; use lettre::{error::Error as LettreError, EmailAddress, Envelope, SendableEmail};
use mime::Mime; use mime::Mime;
use std::fs; use std::fs;
@@ -136,7 +131,7 @@ impl PartBuilder {
/// Adds a `ContentType` header with the given MIME type /// Adds a `ContentType` header with the given MIME type
pub fn content_type(self, content_type: &Mime) -> PartBuilder { pub fn content_type(self, content_type: &Mime) -> PartBuilder {
self.header(("Content-Type", format!("{}", content_type).as_ref())) self.header(("Content-Type", content_type.to_string()))
} }
/// Adds a child part /// Adds a child part
@@ -263,7 +258,7 @@ impl EmailBuilder {
filename.unwrap_or( filename.unwrap_or(
path.file_name() path.file_name()
.and_then(|x| x.to_str()) .and_then(|x| x.to_str())
.ok_or(EmailError::CannotParseFilename)?, .ok_or(Error::CannotParseFilename)?,
), ),
content_type, content_type,
) )
@@ -308,7 +303,7 @@ impl EmailBuilder {
.body(body) .body(body)
.header(( .header((
"Content-Type", "Content-Type",
format!("{}", mime::TEXT_PLAIN_UTF_8).as_ref(), mime::TEXT_PLAIN_UTF_8.to_string(),
)) ))
.build(); .build();
self.child(text) self.child(text)
@@ -320,7 +315,7 @@ impl EmailBuilder {
.body(body) .body(body)
.header(( .header((
"Content-Type", "Content-Type",
format!("{}", mime::TEXT_HTML_UTF_8).as_ref(), mime::TEXT_HTML_UTF_8.to_string(),
)) ))
.build(); .build();
self.child(html) self.child(html)
@@ -336,7 +331,7 @@ impl EmailBuilder {
.body(body_text) .body(body_text)
.header(( .header((
"Content-Type", "Content-Type",
format!("{}", mime::TEXT_PLAIN_UTF_8).as_ref(), mime::TEXT_PLAIN_UTF_8.to_string(),
)) ))
.build(); .build();
@@ -344,7 +339,7 @@ impl EmailBuilder {
.body(body_html) .body(body_html)
.header(( .header((
"Content-Type", "Content-Type",
format!("{}", mime::TEXT_HTML_UTF_8).as_ref(), mime::TEXT_HTML_UTF_8.to_string(),
)) ))
.build(); .build();
@@ -384,7 +379,7 @@ impl EmailBuilder {
} }
// Add the sender header, if any. // Add the sender header, if any.
if let Some(ref v) = self.sender { if let Some(ref v) = self.sender {
self.message = self.message.header(("Sender", v.to_string().as_ref())); self.message = self.message.header(("Sender", v.to_string()));
} }
// Calculate the envelope // Calculate the envelope
let envelope = match self.envelope { let envelope = match self.envelope {
@@ -416,15 +411,15 @@ impl EmailBuilder {
// if it's an author group, use the first author // if it's an author group, use the first author
Some(mailbox) => Ok(mailbox.address.clone()), Some(mailbox) => Ok(mailbox.address.clone()),
// for an empty author group (the rarest of the rare cases) // for an empty author group (the rarest of the rare cases)
None => Err(EmailError::Envelope { None => Err(Error::Envelope(
error: LettreError::MissingFrom, LettreError::MissingFrom,
}), // empty envelope sender )), // empty envelope sender
}, },
}, },
// if we don't have a from header // if we don't have a from header
None => Err(EmailError::Envelope { None => Err(Error::Envelope(
error: LettreError::MissingFrom, LettreError::MissingFrom,
}), // empty envelope sender )), // empty envelope sender
} }
} }
}?)?); }?)?);
@@ -448,9 +443,9 @@ impl EmailBuilder {
.message .message
.header(Header::new_with_value("From".into(), from).unwrap()); .header(Header::new_with_value("From".into(), from).unwrap());
} else { } else {
Err(EmailError::Envelope { Err(Error::Envelope(
error: LettreError::MissingFrom, LettreError::MissingFrom,
})?; ))?;
} }
if !self.cc.is_empty() { if !self.cc.is_empty() {
self.message = self self.message = self
@@ -476,7 +471,7 @@ impl EmailBuilder {
if !self.date_issued { if !self.date_issued {
self.message = self self.message = self
.message .message
.header(("Date", Tm::rfc822z(&now()).to_string().as_ref())); .header(("Date", Tm::rfc822z(&now()).to_string()));
} }
self.message = self.message.header(("MIME-Version", "1.0")); self.message = self.message.header(("MIME-Version", "1.0"));

1
website/.gitignore vendored
View File

@@ -1,2 +1 @@
node_modules
/_book /_book

View File

@@ -1,13 +1,16 @@
all: depends _book all: depends _book
depends: depends:
gitbook install cargo install --force mdbook --vers "^0.2"
cargo install --force mdbook-linkcheck --vers "^0.2"
serve: serve:
gitbook serve mdbook serve
_book: _book:
gitbook build mdbook build
clean: clean:
rm -rf _book/ rm -rf _book/
.PHONY: _book

View File

@@ -1,10 +0,0 @@
{
"root": "./content",
"plugins": [ "-sharing", "edit-link" ],
"pluginsConfig": {
"edit-link": {
"base": "https://github.com/lettre/lettre/edit/master/website/content",
"label": "Edit"
}
}
}

11
website/book.toml Normal file
View File

@@ -0,0 +1,11 @@
[book]
title = "Lettre"
author = "Alexis Mousset"
description = "The user documentation of the Lettre crate."
[build]
build-dir = "_book"
[output.html]
[output.linkcheck]

View File

@@ -10,7 +10,7 @@ Lettre is an email library that allows creating and sending messages. It provide
The `lettre_email` crate allows you to compose messages, and the `lettre` The `lettre_email` crate allows you to compose messages, and the `lettre`
provide transports to send them. provide transports to send them.
Lettre requires Rust 1.26 or newer. Add the following to your `Cargo.toml`: Lettre requires Rust 1.32 or newer. Add the following to your `Cargo.toml`:
```toml ```toml
[dependencies] [dependencies]

View File

@@ -1,4 +1,4 @@
SMTP Transport #### SMTP Transport
This transport uses the SMTP protocol to send emails over the network (locally or remotely). This transport uses the SMTP protocol to send emails over the network (locally or remotely).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB