diff options
author | Philipp Korber <p.korber@1aim.com> | 2018-11-16 15:46:43 +0100 |
---|---|---|
committer | Philipp Korber <p.korber@1aim.com> | 2018-11-16 15:46:43 +0100 |
commit | 652d6f0ffeee7302a2cb51059bef75d8b0bb50be (patch) | |
tree | c3851592642938172f280f7428d43e08b0fe2cbe /core | |
parent | 0947fe8996149fe20a6d47a793f9555790eb2eae (diff) |
refactor: merged sources of mail-headers,mail-internals,mail-core, mail
Originally it was palaned to do a merge with `--allow-unrelated-history`
but this can not be doesn as `mail-core` has a "invalid" history which
has a merge conflict **with itself**. So even rewinding the history on
a empty repo is not possible.
Instead the code was directly coppied over losing history.
But the history is still available in the different
`history-backup-*` branches. It is just that the past history
is decoupled from the current history.
Diffstat (limited to 'core')
35 files changed, 4752 insertions, 0 deletions
diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 0000000..7f6b7b2 --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "mail-core" +version = "0.6.0-wip" +description = "[internal/mail-api] provides the Mail type for the mail-api crates (inkl. multipart mime bodies, builder and resource type)" +authors = ["Philipp Korber <p.korber@1aim.com>"] +keywords = ["mail-api", "internal"] +categories = [] +license = "MIT OR Apache-2.0" +readme = "./README.md" +documentation = "https://docs.rs/mail-core" +repository = "https://github.com/1aim/mail-core" + +[features] +serde-impl = ["serde", "mail-headers/serde-impl"] +default = ["default_impl_cpupool"] +default_impl_cpupool = ["futures-cpupool"] + +[dependencies] +failure = "0.1.2" +futures = "0.1.24" +log = "0.3.8" +rand = "0.3.15" +vec1 = "1" +mail-internals = { git="https://github.com/1aim/mail-internals" } +mail-headers = { git="https://github.com/1aim/mail-headers" } +chrono = "0.4" +soft-ascii-string = "1.0" +serde = { version="1.0", optional=true, features=["derive"] } +checked_command = "0.2.2" + +[dependencies.mime] +git="https://github.com/1aim/mime" +branch="parser_revamp" +version="0.4.0" + +[dependencies.futures-cpupool] +optional = true +version = "0.1.5" + +[dev-dependencies] +serde_json = "1.0" +serde_test = "1.0.80" + diff --git a/core/README.md b/core/README.md new file mode 100644 index 0000000..c1d04af --- /dev/null +++ b/core/README.md @@ -0,0 +1,94 @@ + +# mail-core + +**Provides the core mail type `Mail` for the `mail` crate.** + +--- + +This crate provides the type called `mail` as well as ways +to create it. It also provides the builder context interface +and the `Resource` type, which is used to represent mail bodies. +Especially such which are attachments or embedded images. + + +# Example + +```rust +extern crate futures; +// Note that the `mail` crate provides a facade re-exporting +// all relevant parts. +extern crate mail_core; +extern crate mail_internals; +#[macro_use] +extern crate mail_headers; + +use std::str; +use futures::Future; + +use mail_internals::MailType; + +// In the facade this is the `headers` module. +use mail_headers::{ + headers::*, + header_components::Domain +}; + +// In the facade this types (and the default_impl module) +// are also exposed at top level +use mail_core::{ + Mail, + default_impl::simple_context, + error::MailError +}; + +fn print_some_mail() -> Result<(), MailError> { + // Domain will implement `from_str` in the future, + // currently it doesn't have a validator/parser. + // So this will become `"example.com".parse()` + let domain = Domain::from_unchecked("example.com".to_owned()); + // Normally you create this _once per application_. + let ctx = simple_context::new(domain, "xqi93".parse().expect("we know it's ascii")) + .expect("this is basically: failed to get cwd from env"); + + let mut mail = Mail::plain_text("Hy there! 😁"); + mail.insert_headers(headers! { + _From: [("I'm Awesome 😁", "bla@examle.com")], + _To: ["unknow@example.com"], + Subject: "Hy there message 😁" + }?); + + // We don't added any think which needs loading but we could have + // and all of it would have been loaded concurrent and async. + let encoded = mail.into_encodable_mail(ctx.clone()) + .wait()? + .encode_into_bytes(MailType::Ascii)?; + + let mail_str = str::from_utf8(&encoded).unwrap(); + println!("{}", mail_str); + Ok(()) +} + +fn main() { + print_some_mail().unwrap() +} +``` + + +Documentation can be [viewed on docs.rs](https://docs.rs/mail-core) +(once it is published). + + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any +additional terms or conditions. diff --git a/core/examples/readme.rs b/core/examples/readme.rs new file mode 100644 index 0000000..5e0cd42 --- /dev/null +++ b/core/examples/readme.rs @@ -0,0 +1,57 @@ +extern crate futures; +// Note that the `mail` crate provides a facade re-exporting +// all relevant parts. +extern crate mail_core; +extern crate mail_internals; +#[macro_use] +extern crate mail_headers; + +use std::str; +use futures::Future; + +use mail_internals::MailType; + +// In the facade this is the `headers` module. +use mail_headers::{ + headers::*, + header_components::Domain +}; + +// In the facade this types (and the default_impl module) +// are also exposed at top level +use mail_core::{ + Mail, + default_impl::simple_context, + error::MailError +}; + +fn print_some_mail() -> Result<(), MailError> { + // Domain will implement `from_str` in the future, + // currently it doesn't have a validator/parser. + // So this will become `"example.com".parse()` + let domain = Domain::from_unchecked("example.com".to_owned()); + // Normally you create this _once per application_. + let ctx = simple_context::new(domain, "xqi93".parse().expect("we know it's ascii")) + .expect("this is basically: failed to get cwd from env"); + + let mut mail = Mail::plain_text("Hy there! 😁", &ctx); + mail.insert_headers(headers! { + _From: [("I'm Awesome 😁", "bla@examle.com")], + _To: ["unknow@example.com"], + Subject: "Hy there message 😁" + }?); + + // We don't added any think which needs loading but we could have + // and all of it would have been loaded concurrent and async. + let encoded = mail.into_encodable_mail(ctx.clone()) + .wait()? + .encode_into_bytes(MailType::Ascii)?; + + let mail_str = str::from_utf8(&encoded).unwrap(); + println!("{}", mail_str); + Ok(()) +} + +fn main() { + print_some_mail().unwrap() +}
\ No newline at end of file diff --git a/core/notes/features/archive_resources.md b/core/notes/features/archive_resources.md new file mode 100644 index 0000000..6cc5071 --- /dev/null +++ b/core/notes/features/archive_resources.md @@ -0,0 +1,34 @@ + +# Feature: Archive Resources + +## Context + +Currently the resources are loaded over a IRI it is possible to make +a iri point _into_ an archive e.g. the Java `jar` scheme does support +it so a program which wants to use e.g. whole templates from a +archive could implement a resource loaded with a `archiev` prefix pointing +into an archive. + +## Problem + +If you point into an archive you would want to 1. open/load it 2. +get all resources you need for the given mail 3. close it. But the +per resource IRI architecture would require you to open/load it +for every Resource in it separately, clever caching can be done to some degree +(while it's open we do not need to open it another time etc.) but has +limitations. + +## Solution / Feature + +let templates define a `resource_preload` and iri which +would point the the archive (but could also be used for other, +similar use cases, e.g. sharing a db connection). + +Add a function to `ResourceLoaderComponent` like `fn use_preload(&self, preload: &IRI) {}` +and `fn stop_using_preload(preload: &IRI) {}` which resource loader, +who can handle archives can use to preload/open an archive. + +### Alternations + +instead of a start/stop preload function have some scope based function, +potentially attached to the _Context_ instead of the `ResourceLoaderComponent`
\ No newline at end of file diff --git a/core/notes/notes.md b/core/notes/notes.md new file mode 100644 index 0000000..1c69749 --- /dev/null +++ b/core/notes/notes.md @@ -0,0 +1,334 @@ + +# Outer Most interface + +something like a Mailer which might implement tokio_servie::Service (if +so multiple parameters are wrapped into a tupple) + +mailer contains information like `from` + +`mailer.send_mails( recipients_data, mail_gen )` + +where recipients_data is a iterable mapping from address to recipient specific data, +e.g. `Vec<(Address, Data)>` + +and mail_gen is something like `trait MailGen { fn gen_mail( from, to, data, bits8support ) -> MailBody; }` + +`MailBody` is not `tokio_smtp::MailBody` but has to implement nessesray contraints, +(e.g. implemnting `toki_smtp::IntoMailBody` not that for the beginning this will be +hard encoded but later one a generic variation allowing `smtp` to be switched out +by something else is also possible`) + +MailGen implementations are not done by hand but implemented ontop of something +like a template spec e.g. `struct TemplateSpec { id_template: TemplateId, additional_appendixes: Vec<Appendix> }` + +Where `TemplateId` can is e.g. `reset_link` leading to the creation of a `html` with alternate `plain` +mail iff there is a `reset_link.html` and a `reset_link.plain` template. A `reset_link.html.data` +folder could be used to define inline (mime related) appendixes like embedded images, +but we might want to have a way to define such embeddigns through the data ( +E.g. by mapping `Data => TemplateEnginData` and replacing `EmbeddedFile` variations +by a new related id and adding the `EmbeddedFile(data)` data to the list of embeddings) + + + +# List of parts possible non-ascii and not ascii encodable + +- local-part (address/addr-spec/local-part) + +# Limitations + +Line length limit: + +SHOULD be no more than 78 chars (excluding CRLF!) +MUST NOT be more than 998 chars (excluding CRLF) + +# Orphan `\n`,`\r` + +MUST NOT occur in header (except for folding) +MUST NOT occur in body (except for newline) + +## Header specific limitations + +- encoded word max length of 75 chars +- spaces around encoed words are ignored?? + + +# Email Address part (a@b.e) + +- there is a `domain-literal` version which does use somthing like `[some_thing]`, + we can use puny code for converting domains into ascii but probably can't use + this with `domain-literal`'s + +- `local-part` is `dot-atom` which has leading and trailing `[CFWS]` so comments are alowed + +- MessageId uses a email address like syntax but without supporting spaces/comments + + +# MIME + +fields containing mime types can have parameters with a `<type>; key=value` style +this is mainly used for `multipart/mixed; boundary=blablabla` and similar. + +You have to make sure the boundary does not appear in any of the "sub-bodies", +this is kinda easy for bodies with e.g. content transfer encoding Base64, +but can be tricky in combination with some other content as normal text +can totally contain the boundary. To prevent this: + +- use long boundary strings +- encode the body with base64 even if it's "just" ascii + - OR check the content and encode parts of it if necessary + +you can have multipart in multipart creating a tree, +make sure you don't mix up the boundaries + + +A body part does not have to have any headers, assume default values if +there is no header, bodies which have no header _have to start with a +blank line_ separating 0 headers from the body. + +Header fields of bodies which do not start with `Content-` _are ignored_! + +Contend types: + +- `mixed`, list of sub-bodies with mixed mime types, might be displayed inline or as appendix + - use >>`Content-Disposition` (RFC 2183)<< to controll this, even through it's not standarized yet (or is it by now?) + - default body mime type is `text/plain` +- `digest` for combining muliple messages of content type `message/rfc822` + - e.g. `(multipar/mixed ("table of content") (multipart/digest "message1", "message2"))` + - `message` (mainly `message/rfc822`) contains _another_ email, e.g. for digest + - wait is there a `multipart/message`?? proably not! +- `alternative` multiple alternative versions of the "same" information + - e.g. `(multipart/alternative (text/plain ...) (text/html ...))` + - _place preferred form last!_ (i.e. increasing order of preference) + - interesting usage with `application/X-FixedRecord`+`application/octet-stream` +- `related` (RFC 2387) all bodies are part of one howl, making no (less) sense if placed alone + - the first part is normally the entry point, but this can be chaged through parameters + - (only relevant for parsing AND interpreting it, but not for generating as we can always use the default) + - Content-ID is used to specify a id on each body respectivly which can be used to refer to it (e.g. in HTML) + - in html use e.g. `<img src="cid:the_content_id@goes.here>....</img>` + - example is `(multipart/relat (text/html ...) (image/jpeg (Content-ID <bala@bal.bla>) ...))` for embedding a image INTO a HTML mail +- `report` +- `signed` (body part + signature part) +- `encrypted` (encryption information part + encrypted data (`application/octet-stream`)) +- `form-data` +- `x-mixed-replace` (for server push, don't use by now there are better ways) +- `byteranges` + + +Example mail structure: + +``` +(multipart/mixed + (multipart/alternative + (text/plain ... ) + (multipart/related + (text/hmtl ... '<img src="cid:contentid@1aim.com"></img>' ... ) + (image/png (Content-ID <contentid@1aim.com>) ... ) + ... )) + (image/png (Content-Disposition attachment) ...) + (image/png (Content-Disposition attachment) ...)) +``` + +Possible alternate structure: + +``` +(multipart/mixed + (multipart/related + + (multipart/alternative + (text/plain ... '[cid:contentid@1aim.com]' ... ) + (text/html ... '<img src="cid:contentid@1aim.com"></img>' ... ) ) + + (image/png (Content-ID <contentid@1aim.com>) ... ) ) + + (image/png (Content-Disposition attachment) ...) + (image/png (Content-Disposition attachment) ...)) +``` + +but I have not seen the `[cid:...]` for text/plain in any standard, through it might be there. +Also if se we might still have a related specific for the html (for html only stuff) so: +- place Embedding in Data in the outer `multipart/related` +- place Embedding returned by the template in inner `multipart/related` + +# Attatchment + +proposed filenames for attachments can be given through parameters of the disposition header + +it does not allow non ascii character there! + +see rfc2231 for more information, it extends some part wrt.: + +- splitting long parameters (e.g. long file names) +- specifying language and character set +- specifying language for encoded words + +# Encoded Words + +extended by rfc2231 + +additional limits in header fields + +header containing encoded words are limited to 76 bytes + +a "big" text chunk can be split in multiple encoded words seperated by b'\r\n ' + +non encoded words and encoded words can apear in the same header field, but +must be seperate by "linear-white-space" (space) which is NOT removed when +decoding encoded words + +encoded words can appear in: + +- `text` sections where `text` is based on RFC 822! (e.g. Content-Description ) + - in context of RFC 5322 this means `unstructured` count's as text +- `comments` (as alternative to `ctext`,`quoted-pair`,`comment` +- `word`'s within a `phrase` + +**Therefor it MUST NOT appear in any structured header field except withing a `comment` or `phrase`!** + +**You have to encode text which looks like an encoded word** + + + +limitations: + +- in comment's no ')',')' and '"' +- in headers no ' ' + + +# Other + +there is no `[CFWS]` after the `:` in Header fields, +but most (all?) of the parts following them are allowed +to start with a `[CFWS]`. (exception is unstructured where +a `CFWS` can be allowed but also MIGHT be part of the +string) + +CFWS -> (un-) foldable whitespace allowing comments +FWS -> (un-) foldable whitespace without comments + + +# Relevant RFCs +5321, 5322, 6854, 3492, 2045, 2046, 2047, 4288, 4289, 2049, 6531, 5890 + +make sure to not use the outdated versions + +RFC7595: Registering URI schemes + + +# Parsing Notes + +be strict when parsing (e.g. only ws and printable in subject line) + +if "some other" strings should still be supported do not do zero +copy, but instead add the data to a new buff _replacing invalid +chars with replacement symbol or just stripping them_ + + +# Non-utf8 Non-Ascci bytes in Mail body + +The mail body can contain non-utf8, non-ascii data (e.g. +utf16 data, images etc.) WITHOUT base64 encoding if +8BITMIME is supported (note there is also BINARY and CHUNKING) + +smtp still considers _the bytes_ corresponding to CR LF and DOT special. + +- there is a line length limit, lines terminate with b'CRLF' +- b'.CRLF' does sill end the body (if preceeded by CRLF, or body starts with it) + - so dot-staching is still done on protocol level + + + +## Hot to handle `obs-` parsings + +we have to be able to parse mails with obsolete syntax (theoretically) +but should never genrate such mails, the encder excepts its underlying +data to be correct, but it might not be if we directly place `obs-` +parsed data there. For many parts this is no problem as the +`obs-` syntax is a lot about having FWS at other positions, +_between components_ (so we won't have a problem with the encoder). +Or some additional obsolete infromations (which we often/allways can just +"skip" over). So we have to check if there are any braking cases and if +we have to not zero copy them when parsing but instead transform them +into a valide representation, in worst case we could add a `not_encodable` +field to some structs. + +# TODO +check if some parts are empty and error if encode is called on them +e.g. empty domain + +make sure trace and resend fields are: + +1. encoded in order (MUST) +2. encoded as blocks (MUST?) +3. encoded before other fields (SHOULD) + +as people may come up with their own trace like fileds, +rule 1 and 2 should appy to all fields + + +make sure trace,resent-* are multi fields + +add a RawUnstructured not doing any encoding, but only validity checking + +# Postponded + +`component::Disposition` should have a `Other` variant, using `Token` (which +means a general extension token type is needed) + +other features like signature, encryption etc. + +check what happens if I "execute" a async/mio/>tokio< +based future in a CPU pool? Does it just do live +polling in the thread? Or does it act more intelligent? +or does it simply fail? + +just before encoding singlepart bodies, resource is resolved, +therefore: + +1. we now have the MediaType + File meta + TransferEncoding +2. add* ContentType header to headers +3. add* ContentTransferEncoding header to headers +4. add* file meta infor to ContentDisposition header if it exists +5. note that >add*< is not modifying Mail, but adds it to the list of headers to encode + + +warn when encoding a Disposition of kind Attachment which's +file_meta has no name set + + +// From RFC 2183: +// NOTE ON PARAMETER VALUE LENGHTS: A short (length <= 78 characters) +// parameter value containing only non-`tspecials' characters SHOULD be +// represented as a single `token'. A short parameter value containing +// only ASCII characters, but including `tspecials' characters, SHOULD +// be represented as `quoted-string'. Parameter values longer than 78 +// characters, or which contain non-ASCII characters, MUST be encoded as +// specified in [RFC 2184]. +provide a gnneral way for encoding header parameter which follow the scheme: +`<mainvalue> *(";" <key>"="<value> )` this are ContentType and ContentDisposition + for now + + +IF Item::Encoded only appears as encoded word, make it Item::Encoded word, +possible checking for "more" validity then noew + + +email::quote => do not escape WSP, and use FWS when encoding +also make quote, generally available for library useers a +create_quoted_string( .. ) + +# Dependencies + +quoted_printable and base64 have some problems: +1. it's speaking of a 76 character limit where it is 78 + it seems they treated the RFC as 78 character including + CRLF where the RFC speaks of 78 characters EXCLUDING + CRLF +2. it's only suited for content transfer encoding the body + as there is a limit of the length of encoded words (75) + which can't be handled by both + +also quoted_printable has another problem: +3. in headers the number of character which can be displayed without + encoding is more limited (e.g. no ' ' ) quoted_printable does not + respect this? (TODO CHECK THIS) +
\ No newline at end of file diff --git a/core/notes/structure.md b/core/notes/structure.md new file mode 100644 index 0000000..775fa47 --- /dev/null +++ b/core/notes/structure.md @@ -0,0 +1,157 @@ +# **Warning: document is not up to date** + +# Naming differences + +(note that `foo/bar` means crate `foo` with feature `bar` enabled) + +- `mail-codec` => `mail-encode` +- `mail-codec-composition` => `mail-encode-compose` +- `mail-codec-composition/smtp` => `mail-tokio-smtp/encode` +- `mail-headers` => `mail-headers/encode` +- `mail-internals` => `mail-core/encode` + + +# Dependency Chart + +(updated) +```ascii + + + mail-smtp mail-templates (maybe at some point)mail-parser +new-tokio-smtp---------/ | | \ | + | | \-handle-bars | + <[mail-core]>----/--------------/-----------------------------------/ + | + | + mail-headers + | + | + mail-internals +``` + +crates marked with a * have both parser and encoder specific parts, +which are opt-in/-out through features. + +# Descriptions + +## Mail-Core + +This crate provides parts used by all other parts using +the `encode`/`parse` features encoding/parsing specific +parts are enabled/disabled. (Currently it's just encoding). + +Parts included are the EncodingBuffer, HeaderMap, Header traits, +encoding traits, bindings to other encoding libraries like +base64, percent-encode and similar. + +## Mail-Headers + +A create providing implementations for the most +mail-internals headers. Like `mail-core` encoding/parsing specific +parts can be enabled/disabled using the `encode`/`decode` +feature. A Header basically consists of a name and a Header component, +which can be encoded and represents the header field. + +Many of those components can be reused at different +points, e.g. `Mailbox`/`MailboxList`/`Email`/`Phrase`/... + + +## Mail-Encode + +This crate provides a basic mechanism to create and +encode mails with includes a Mail types which can +represent any form of mails, from simple text mails +to complex multipart mails. While this API can be +used to represent any mail it is not nessesary the +most convinient to use, as it requires the user to +handle aspects like e.g. which multipart content type +is used, and how they are structured. + +Additional to the Mail struct it provides a mechanism +to encode the Mail struct with the EncodingBuffer provided +by `mail-core` produceing a mail in form of a +string, ascii-string or bytes a required by the consumer. +(the encoder does know about 7bit, 8bit and utf8 i.e. +internationalized mails). + +As a mail can contain all kinds of embedded data (e.g. +atachments, embeded images in a html mail etc.). This +create also provides a way to handle such resources +as part of a mail. + + +## Mail-Encode-Compose + +Is build on-top of `mail-encode` providing traits and mechanism, +to bind in template engines, allowing the API consumer to +create mails based on from, to, subject, template_id and +template_data. The template engine only has to provide +a number of alternate bodies with embeddings/attachments +caused by them. The mail-composition crate takes care of +composing them into a mail multipart tree +(using `multipart/related`,`multipart/mixed`, etc.) + +Currently `mail-composition` does not have explicit support for +e.g. encryption and some other "more special" multipart +bodies. Nevertheless by using the API provided by Mail this +mail can still be created, they do just not jet bind with +the template engine by default. + +The crate also provides a pre-build template engine through a +feature which just needs another template engine to render +mail content bodies (e.g. text/html) and handles all the +other parts, like e.g. providing a logo for an HTML mail +as an embedding (as a side note generation of CID's and +providing them to the template data is already handled) + +# Mail + +A facade for all `mail-*` crates, providing a `encode` +`parse` feature to enable/disable the encoding/parsing +parts, and more fine grained features for more fain +grained control over what is included. This +takes advantage of rust features being additive, e.g. +if one dependency includes `mail/encode` and one `mail/decode` +than rust will only include one time `mail` with both +`encode` and `decode` enabled. + +(this crate does not yet exist) + + +# Mail-Tokio-Smtp + +This crate will provide bindings between the mail +crates and a crate providing tokio-smtp support. +While smtp is mainly a transport protocol this +crate provides bindings to make thinks easy to +use and setup, e.g. the `mail-encode-compose` +library does provide a method `compose_mail(MailSendData) -> Result<Mail>` +(called on a combination of Context and TemplateEngine), +but if you want to use this as a request in a smtp +service to send a mail, there is quite a bit of bind +code doing everything from calling compose_mail, making +it encodable, encoding it and sending the right smtp +commands. + +Currently this functionality lives in `mail-encode-compose` +under the `smtp` feature and use `tokio-smtp` as binding +library and is limited to binding with the mechanism provided +in `mail-encode-compose` but not general `Mail` structs (through +is will change in the future) + + +# Mail-parse + +A crate providing functionality for parsing mails, it +will use a combination of `Arc`/`Rc` and `OwningRef` +to allow a zero-copy parsing of mails, nevertheless +as mail normaly do contain parts further encoded in +e.g. base64 it will provide a mechanism where parts +of the mail can be shadowed by a decoded version +of the part generated on-demand/lazily. + +**This crate currently does not exist, and focus lies +on the encoding based crates** + +Through the crate structure is setup to include parsing +specific parts in the future.
\ No newline at end of file diff --git a/core/src/compose.rs b/core/src/compose.rs new file mode 100644 index 0000000..a928628 --- /dev/null +++ b/core/src/compose.rs @@ -0,0 +1,430 @@ +//! This module provides utilities for composing multipart mails. +//! +//! While the `Mail` type on itself can represent any multipart +//! mail most mails have a certain pattern to their structure, +//! consisting mainly of `multipart/mixed` for attachments, +//! `multipart/alternative` for alternative bodies and +//! `multipart/related` for including embedded resources which +//! can be used in the mail bodies like e.g. a logo. +//! +//! This module provides the needed utilities to more simply +//! create a `Mail` instance which represents this kind of +//! mails. + +//-------------------------------------------------------------\\ +// NOTE: Implementations for creating (composing) mails are || +// split from the type dev, and normal impl blocks and placed || +// in the later part of the file for better readability. || +//-------------------------------------------------------------// + +use media_type::{MULTIPART, ALTERNATIVE, RELATED, MIXED}; +use vec1::Vec1; + +#[cfg(feature="serde")] +use serde::{Serialize, Deserialize}; + |