summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp Korber <p.korber@1aim.com>2018-11-19 17:57:44 +0100
committerPhilipp Korber <p.korber@1aim.com>2018-11-19 17:57:44 +0100
commitbada4eb94b86f3aae99d4fcf2a504bd3dffe5f54 (patch)
tree4eb4eed39da42f897d6ed2fd36098317aeb849b6
parentad91e471abde1a1806baa11b0ea01e6b155394a1 (diff)
chore(new-api) scratched out new api
- The deps (mail-base/common/headers) changed and have some of the functionality previous in this crate directly and with a changed (improved) interface. - The previous approach had some problems.
-rw-r--r--Cargo.toml26
-rw-r--r--src/askama_engine/error.rs38
-rw-r--r--src/askama_engine/mod.rs316
-rw-r--r--src/builder_extension.rs223
-rw-r--r--src/compositor/builder.rs198
-rw-r--r--src/compositor/impl_compose.rs162
-rw-r--r--src/compositor/mod.rs297
-rw-r--r--src/error.rs270
-rw-r--r--src/lib.rs472
-rw-r--r--src/resource/impl_inspect.rs278
-rw-r--r--src/resource/mod.rs421
-rw-r--r--src/template_engine.rs199
12 files changed, 335 insertions, 2565 deletions
diff --git a/Cargo.toml b/Cargo.toml
index e13195e..8caa4cf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,28 +1,31 @@
[package]
name = "mail-template"
version = "0.2.0"
-description = "[internal/mail-api] provides a \"composition through template\" api for the Mail type from the mail-api crates"
+description = "[mail] provides a way to create bind string template engines to produce mails"
authors = ["Philipp Korber <p.korber@1aim.com>"]
-keywords = ["mail-api", "internal"]
+keywords = ["mail-api", "template"]
categories = []
license = "MIT OR Apache-2.0"
readme = "./README.md"
documentation = "https://docs.rs/mail-template"
repository = "https://github.com/1aim/mail-template"
+[features]
+default = []
+handlebars-bindings = []
[dependencies]
-mail-types = { git="https://github.com/1aim/mail-types" }
-mail-common = { git="https://github.com/1aim/mail-common" }
-mail-headers = { git="https://github.com/1aim/mail-headers" }
-mail-derive = { git="https://github.com/1aim/mail-derive" }
+mail-core = { git="https://github.com/1aim/mail" }
+mail-internals = { git="https://github.com/1aim/mail" }
+mail-headers = { git="https://github.com/1aim/mail" }
failure = "0.1.1"
futures = "0.1.14"
vec1 = "1.0"
soft-ascii-string = "1.0"
-serde = { version="1.0.64", optional=true }
-
+serde = { version="1", optional=true }
+toml = "0.4.8"
+galemu = "0.2.2"
[dependencies.mime]
@@ -30,13 +33,6 @@ git="https://github.com/1aim/mime"
branch="parser_revamp"
version="0.4.0"
-[dependencies.askama]
-version = "0.6.4"
-optional = true
[dev-dependencies]
-[features]
-default = []
-askama-engine = ["askama"]
-serialize-to-content-id = ["serde"] \ No newline at end of file
diff --git a/src/askama_engine/error.rs b/src/askama_engine/error.rs
deleted file mode 100644
index 8edffd9..0000000
--- a/src/askama_engine/error.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-use std::fmt::{self, Debug, Display};
-use std::sync::Mutex;
-use askama;
-
-/// a wrapper needed as askama is not Sync
-#[derive(Fail)]
-pub struct AskamaError {
- inner: Mutex<askama::Error>
-}
-
-impl AskamaError {
-
- pub fn inner(&self) -> &Mutex<askama::Error> {
- &self.inner
- }
-}
-
-const POISON_MSG: &str = "<Debug/Display of crate::askama::Error paniced previously>";
-macro_rules! impl_fmt {
- ($($trait:ident),*) => ($(
- impl $trait for AskamaError {
- fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
- match self.inner.lock() {
- Ok(inner_error) => $trait::fmt(&*inner_error, fter),
- Err(_err) => write!(fter, "{}", POISON_MSG)
- }
- }
- }
- )*);
-}
-
-impl_fmt!(Debug, Display);
-
-impl From<askama::Error> for AskamaError {
- fn from(err: askama::Error) -> Self {
- AskamaError { inner: Mutex::new(err) }
- }
-} \ No newline at end of file
diff --git a/src/askama_engine/mod.rs b/src/askama_engine/mod.rs
deleted file mode 100644
index 2bd0ae5..0000000
--- a/src/askama_engine/mod.rs
+++ /dev/null
@@ -1,316 +0,0 @@
-use vec1::Vec1;
-
-use askama;
-use headers::components::MediaType;
-use mail::{Resource, Context};
-
-use ::{TemplateEngine, MailParts, BodyPart, EmbeddedWithCId};
-mod error;
-pub use self::error::*;
-
-pub trait AskamaMailTemplate: askama::Template {
-
- fn media_type(&self) -> MediaType;
-
- /// Implement this to have alternate bodies, e.g. a alternate text body for an html body
- ///
- /// A simple way to bind another template to an data type is by wrapping a reference of
- /// the original type into it.
- ///
- /// # Example
- ///
- /// ```rust
- /// # #[macro_use]
- /// # extern crate askama;
- /// # extern crate mail_template;
- /// # extern crate mail_headers;
- /// # mod mail { pub use mail_template::askama_engine as askama; pub use mail_headers::components::MediaType; }
- /// use std::ops::Deref;
- /// use std::borrow::Cow;
- /// use askama::Template;
- /// use mail::askama::AskamaMailTemplate;
- /// use mail::MediaType;
- ///
- /// #[derive(Template)]
- /// #[template(source = "<h2>Hy {{ name }}</h2>", ext="html")]
- /// struct HtmlHy {
- /// name: &'static str
- /// }
- ///
- /// impl AskamaMailTemplate for HtmlHy {
- /// fn media_type(&self) -> MediaType {
- /// MediaType::parse("text/html; charset=utf-8").unwrap()
- /// }
- ///
- /// fn alternate_template<'a>(&'a self) -> Option<Box<AskamaMailTemplate + 'a>> {
- /// // theoretically we could circumvent the boxing by returning a &Trait
- /// // but this would require transmuting `&HtmlHy` to `&TextHy` so we don't
- /// // do this
- /// Some(Box::new(TextHy(self)))
- /// }
- /// }
- ///
- /// #[derive(Template)]
- /// #[template(source = "Hy {{ name }}, use html please", ext="txt")]
- /// struct TextHy<'a>(&'a HtmlHy);
- ///
- /// /// we implement deref so that we can use the fields
- /// /// of `HtmlHy` without indirection, e.g. use `name`
- /// /// instead of `inner.name`
- /// impl<'a> Deref for TextHy<'a> {
- /// type Target = HtmlHy;
- ///
- /// fn deref(&self) -> &Self::Target {
- /// self.0
- /// }
- /// }
- ///
- /// impl<'a> AskamaMailTemplate for TextHy<'a> {
- /// fn media_type(&self) -> MediaType {
- /// MediaType::parse("text/plain; charset=utf-8").unwrap()
- /// }
- /// }
- ///
- /// fn main() {
- /// let hy = HtmlHy { name: "Liz" };
- ///
- /// let rendered = hy.render().unwrap();
- /// assert_eq!(rendered, "<h2>Hy Liz</h2>");
- /// let rendered = hy.alternate_template().unwrap().render().unwrap();
- /// assert_eq!(rendered, "Hy Liz, use html please");
- /// }
- /// ```
- ///
- fn alternate_template<'a>(&'a self) -> Option<Box<AskamaMailTemplate + 'a>> {
- None
- }
-
- fn attachments(&self) -> Vec<Resource> {
- Vec::new()
- }
-}
-
-
-pub struct AskamaTemplateEngine;
-
-
-impl<C, D> TemplateEngine<C, D> for AskamaTemplateEngine
- where C: Context, D: AskamaMailTemplate
-{
- type TemplateId = ();
- type Error = AskamaError;
-
- fn use_template(&self, _id: &(), data: &D, ctx: &C) -> Result<MailParts, Self::Error> {
- let mut state = State::new(ctx);
- state.render_bodies::<Self::Error>(data)?;
- let (alternative_bodies, attachments) = state.destruct();
-
- Ok(MailParts {
- alternative_bodies,
- attachments,
- shared_embeddings: Vec::new(),
- })
- }
-}
-
-struct State<'a, C: 'a> {
- ctx: &'a C,
- bodies: Vec<BodyPart>,
- attachments: Vec<EmbeddedWithCId>
-}
-
-
-impl<'a, C: 'a> State<'a, C>
- where C: Context
-{
- fn new(ctx: &'a C) -> Self {
- State {
- ctx,
- bodies: Vec::new(),
- attachments: Vec::new()
- }
- }
-
- fn render_bodies<E>(
- &mut self,
- template: &AskamaMailTemplate,
- ) -> Result<(), E>
- where E: From<askama::Error>
- {
- let string = template.render()?;
- let media_type = template.media_type();
- let resource = Resource::sourceless(media_type, string);
- self.bodies.push(BodyPart {
- resource: resource,
- embeddings: Vec::new()
- });
-
- for attachment in template.attachments() {
- self.attachments.push(EmbeddedWithCId::attachment(attachment, self.ctx));
- }
-
- let sub = template.alternate_template();
- if let Some(alt) = sub {
- self.render_bodies::<E>(&*alt)?;
- }
- Ok(())
- }
-
- /// # Panics
- ///
- /// if render_bodies was not called at last once successfully
- fn destruct(self) -> (Vec1<BodyPart>, Vec<EmbeddedWithCId>) {
- let State { bodies, attachments, ctx:_ } = self;
- let bodies = Vec1::from_vec(bodies).expect("[BUG] should have at last one body");
- (bodies, attachments)
- }
-}
-
-
-#[cfg(test)]
-mod test {
- use std::ops::Deref;
-
- use futures::Future;
- use soft_ascii_string::SoftAsciiString;
- use askama::Template;
-
- use mail::Context;
- use mail::default_impl::simple_context;
- use headers::components::Domain;
- use headers::HeaderTryFrom;
-
- use ::{InspectEmbeddedResources, Embedded};
- use super::*;
- //TODO test with alternate bodies and attachments
-
- #[derive(InspectEmbeddedResources)]
- struct Person {
- name: &'static str,
- name_prefix: &'static str,
- avatar: Embedded
- }
-
- #[derive(Template, InspectEmbeddedResources)]
- #[template(
- source="<img src=\"cid:{{ avatar.content_id().unwrap().as_str() }}\"><h2>Dear {{name_prefix}} {{name}}</h2>",
- ext="html")]
- // #[askama_mail(media_type = "text/html; charset=utf-8")]
- // #[askama_mail(alternate=TextGreeting)]
- struct HtmlGreeting<'a> {
- person: &'a mut Person
- }
-
- impl<'a> Deref for HtmlGreeting<'a> {
- type Target = Person;
-
- fn deref(&self) -> &Self::Target {
- self.person
- }
- }
-
- impl<'a> AskamaMailTemplate for HtmlGreeting<'a> {
- fn media_type(&self) -> MediaType {
- MediaType::parse("text/html; charset=utf-8").unwrap()
- }
-
- fn attachments(&self) -> Vec<Resource> {
- vec![ Resource::sourceless_from_string("hy"), Resource::sourceless_from_string("ho") ]
- }
-
- fn alternate_template<'e>(&'e self) -> Option<Box<AskamaMailTemplate + 'e>> {
- Some(Box::new(TextGreeting::from(self)))
- }
- }
-
-
-
- #[derive(Template)]
- #[template(source="Dear {{name_prefix}} {{name}}", ext="txt")]
- // #[askama_mail(media_type = "text/plain; charset=utf-8")]
- // #[askama_mail(wraps=HtmlGreeting)]
- struct TextGreeting<'a> {
- inner: &'a HtmlGreeting<'a>
- }
-
- impl<'a> AskamaMailTemplate for TextGreeting<'a> {
- fn media_type(&self) -> MediaType {
- MediaType::parse("text/plain; charset=utf-8").unwrap()
- }
-
- fn attachments(&self) -> Vec<Resource> {
- vec![ Resource::sourceless_from_string("so") ]
- }
- }
-
- //auto-gen from wraps
- impl<'a> Deref for TextGreeting<'a> {
- type Target = HtmlGreeting<'a>;
-
- fn deref(&self) -> &Self::Target {
- self.inner
- }
- }
-
- //auto-gen from wraps
- impl<'a> From<&'a HtmlGreeting<'a>> for TextGreeting<'a> {
- fn from(inner: &'a HtmlGreeting<'a>) -> Self {
- TextGreeting { inner }
- }
- }
-
- fn ctx() -> impl Context {
- let domain = Domain::try_from("bla.test").unwrap();
- let unique = SoftAsciiString::from_unchecked("dq-9c2e");
- simple_context::new(domain, unique).unwrap()
- }
-
- #[test]
- fn use_template_works_as_expected() {
- let ctx = ctx();
-
- let mut person = Person {
- name: "Liz",
- name_prefix: "Prof. Dr. Ex. Gd. Frk.",
- avatar: Embedded::attachment(Resource::sourceless_from_string("should be an image"))
- };
-
- let mut data = HtmlGreeting { person: &mut person };
- // this is normally done by the compose mail parts
- // this is also why HtmlGreeting takes a &mut Person
- // instead of a &Person
- let mut nr_visited = 0;
- data.inspect_resources_mut(&mut |emb: &mut Embedded| {
- emb.assure_content_id(&ctx);
- nr_visited += 1;
- });
- assert_eq!(nr_visited, 1);
-
- // this engine binds the template through the data, so no Id
- let id = &();
- let res = AskamaTemplateEngine.use_template(id, &data, &ctx);
-
- let MailParts {
- alternative_bodies,
- attachments,
- shared_embeddings
- } = res.unwrap();
-
- assert_eq!(alternative_bodies.len(), 2);
- assert_eq!(attachments.len(), 3);
- assert_eq!(shared_embeddings.len(), 0);
-
- let first = alternative_bodies.first();
- assert_eq!(first.embeddings.len(), 0);
- let res = first.resource.create_loading_future(ctx.clone()).wait().unwrap();
- let stringed = String::from_utf8(res.access().as_slice().to_owned()).unwrap();
- assert!(stringed.starts_with("<img src=3D\"cid:dq-9c2e."));
- assert!(stringed.ends_with("@bla.test\"><h2>Dear Prof. Dr. Ex. G=\r\nd. Frk. Liz</h2>"));
-
- let last = alternative_bodies.last();
- assert_eq!(last.embeddings.len(), 0);
- let res = last.resource.create_loading_future(ctx.clone()).wait().unwrap();
- let stringed = String::from_utf8(res.access().as_slice().to_owned()).unwrap();
- assert_eq!(stringed, "Dear Prof. Dr. Ex. Gd. Frk. Liz")
- }
-} \ No newline at end of file
diff --git a/src/builder_extension.rs b/src/builder_extension.rs
deleted file mode 100644
index 9956ccd..0000000
--- a/src/builder_extension.rs
+++ /dev/null
@@ -1,223 +0,0 @@
-use media_type::{MULTIPART, ALTERNATIVE, RELATED, MIXED};
-use vec1::Vec1;
-
-
-use headers::{HeaderMap, ContentId, ContentDisposition};
-use headers::components::{Disposition, MediaType};
-use mail::{Resource, Mail, Builder};
-use mail::error::OtherBuilderErrorKind;
-
-use ::resource::EmbeddedWithCId;
-use ::error::{ExtendedBuilderError, ExtendedBuilderErrorKind};
-
-
-/// A mail body likely created by a template engine
-#[derive(Debug)]
-pub struct BodyPart {
- /// a body created by a template
- pub resource: Resource,
-
- /// embeddings added by the template engine
- ///
- /// It is a mapping of the name under which a embedding had been made available in the
- /// template engine to the embedding (which has to contain a CId, as it already
- /// was used in the template engine and CIds are used to link to the content which should
- /// be embedded)
- pub embeddings: Vec<EmbeddedWithCId>,
-
-}
-
-/// Ext. Trait which adds helper methods to the Builder type.
-///
-pub trait BuilderExt {
-
- fn create_alternate_bodies<HM>(
- bodies: Vec1<BodyPart>,
- header: HM
- ) -> Result<Mail, ExtendedBuilderError>
- where HM: Into<Option<HeaderMap>>;
-
-
- fn create_mail_body<HM>(
- body: BodyPart,
- headers: HM
- ) -> Result<Mail, ExtendedBuilderError>
- where HM: Into<Option<HeaderMap>>;
-
- fn create_with_attachments<HM>(
- body: Mail,
- attachments: Vec<EmbeddedWithCId>,
- headers: HM
- ) -> Result<Mail, ExtendedBuilderError>
- where HM: Into<Option<HeaderMap>>;
-
- fn create_body_from_resource<HM>(
- resource: Resource,
- headers: HM
- ) -> Result<Mail, ExtendedBuilderError>
- where HM: Into<Option<HeaderMap>>;
-
- fn create_body_with_embeddings<HM, EMB>(
- sub_body: Mail,
- embeddings: EMB,
- headers: HM
- ) -> Result<Mail, ExtendedBuilderError>
- where HM: Into<Option<HeaderMap>>,
- EMB: Iterator<Item=EmbeddedWithCId> + ExactSizeIterator;
-
- fn create_alternate_bodies_with_embeddings<HM, EMB>(
- bodies: Vec1<BodyPart>,
- embeddings: EMB,
- header: HM
- ) -> Result<Mail, ExtendedBuilderError>
- where HM: Into<Option<HeaderMap>>,
- EMB: Iterator<Item=EmbeddedWithCId> + ExactSizeIterator;
-}
-
-
-
-impl BuilderExt for Builder {
-
- fn create_alternate_bodies<HM>(
- bodies: Vec1<BodyPart>,
- headers: HM
- ) -> Result<Mail, ExtendedBuilderError>
- where HM: Into<Option<HeaderMap>>
- {
- let bodies = bodies;
-
- match bodies.len() {
- 0 => return Err(OtherBuilderErrorKind::EmptyMultipartBody.into()),
- 1 => return Self::create_mail_body(bodies.into_vec().pop().unwrap(), headers),
- _n => {}
- }
-
- let mut builder = Builder::multipart(MediaType::new(MULTIPART, ALTERNATIVE)?)?;
-
- if let Some(headers) = headers.into() {
- builder = builder.headers(headers)?;
- }
-
- for body in bodies {
- builder = builder.body(Self::create_mail_body(body, None)?)?;
- }
-
- Ok(builder.build()?)
- }
-
- fn create_alternate_bodies_with_embeddings<HM, EMB>(
- bodies: Vec1<BodyPart>,
- embeddings: EMB,
- headers: HM
- ) -> Result<Mail, ExtendedBuilderError>
- where HM: Into<Option<HeaderMap>>,
- EMB: Iterator<Item=EmbeddedWithCId> + ExactSizeIterator
- {
- match embeddings.len() {
- 0 => {
- Self::create_alternate_bodies(bodies, headers)
- },
- _n => {
- Self::create_body_with_embeddings(
- Self::create_alternate_bodies(bodies, None)?,
- embeddings,
- headers
- )
- }
- }
- }
-
- fn create_mail_body<HM>(
- body: BodyPart,
- headers: HM
- ) -> Result<Mail, ExtendedBuilderError>
- where HM: Into<Option<HeaderMap>>
- {
- let BodyPart { resource, embeddings } = body;
- if embeddings.len() > 0 {
- Self::create_body_with_embeddings(
- Self::create_body_from_resource(resource, None)?,
- embeddings.into_iter(),
- headers
- )
- } else {
- Self::create_body_from_resource(resource, headers)
- }
- }
-
- fn create_body_from_resource<HM>(
- resource: Resource,
- headers: HM
- ) -> Result<Mail, ExtendedBuilderError>
- where HM: Into<Option<HeaderMap>>
- {
- let mut builder = Builder::singlepart(resource);
- if let Some(headers) = headers.into() {
- builder = builder.headers(headers)?;
- }
- Ok(builder.build()?)
- }
-
- fn create_body_with_embeddings<HM, EMB>(
- sub_body: Mail,
- embeddings: EMB,
- headers: HM
- ) -> Result<Mail, ExtendedBuilderError>
- where HM: Into<Option<HeaderMap>>,
- EMB: Iterator<Item=EmbeddedWithCId> + ExactSizeIterator
- {
-
- if embeddings.len() == 0 {
- return Err(ExtendedBuilderErrorKind::EmbeddingMissing.into());
- }
-
- let mut builder = Builder::multipart(MediaType::new(MULTIPART,RELATED)?)?;
-
- if let Some(headers) = headers.into() {
- builder = builder.headers(headers)?;
- }
-
-
- builder = builder.body(sub_body)?;
- for embedding in embeddings {
- let (content_id, resource) = embedding.into();
- builder = builder.body(
- Self::create_body_from_resource(resource , headers! {
- ContentId: content_id,
- ContentDisposition: Disposition::inline()
- }?)?
- )?;
- }
- Ok(builder.build()?)
- }
-
-
- fn create_with_attachments<HM>(
- body: Mail,
- attachments: Vec<EmbeddedWithCId>,
- headers: HM
- ) -> Result<Mail, ExtendedBuilderError>
- where HM: Into<Option<HeaderMap>>
- {
-
- let mut builder = Builder::multipart(MediaType::new(MULTIPART, MIXED)?)?;
-
- if let Some(headers) = headers.into() {
- builder = builder.headers(headers)?;
- }
-
- builder = builder.body(body)?;
-
- for attachment in attachments {
- builder = builder.body(Self::create_body_from_resource(
- attachment.into(),
- headers! {
- ContentDisposition: Disposition::attachment()
- }?
- )?)?;
- }
-
- Ok(builder.build()?)
- }
-}
-
diff --git a/src/compositor/builder.rs b/src/compositor/builder.rs
deleted file mode 100644
index 91a03f9..0000000
--- a/src/compositor/builder.rs
+++ /dev/null
@@ -1,198 +0,0 @@
-use std::borrow::{ToOwned, Cow};
-use std::fmt::{self, Debug};
-
-use vec1::Vec1;
-
-use headers::components::{
- Mailbox, MailboxList,
-};
-
-use ::resource::InspectEmbeddedResources;
-use ::error::{MailSendDataError, MailSendDataErrorKind, WithSource, WithSourceExt};
-
-use super::MailSendData;
-
-/// Builder to create `MailSendData`
-pub struct MailSendDataBuilder<'a, TId: ?Sized + 'a, D>
- where TId: ToOwned + Debug, TId::Owned: Debug, D: InspectEmbeddedResources + Debug
-{
- sender: Option<Mailbox>,
- from: Vec<Mailbox>,
- to: Vec<Mailbox>,
- subject: Option<String>,
- template_id: Option<Cow<'a, TId>>,
- data: Option<D>
-}
-
-
-
-
-
-impl<'a, TId: ?Sized + 'a, D> Debug for MailSendDataBuilder<'a, TId, D>
- where TId: ToOwned + Debug, TId::Owned: Debug, D: InspectEmbeddedResources + Debug
-{
- fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
- fter.debug_struct("MailSendData")
- .field("sender", &self.sender)
- .field("from", &self.from)
- .field("to", &self.to)
- .field("subject", &self.subject)
- .field("template_id", &self.template_id)
- .field("data", &self.data)
- .finish()
- }
-}
-
-// Sadly I can not used derive(Default) (it want's a bound on TId)
-// if the deriviate create is stable, I could use them for that
-impl<'a, TId: ?Sized + 'a, D> Default for MailSendDataBuilder<'a, TId, D>
- where TId: ToOwned + Debug, TId::Owned: Debug, D: InspectEmbeddedResources + Debug
-{
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl<'a, TId: ?Sized + 'a, D> MailSendDataBuilder<'a, TId, D>
- where TId: ToOwned + Debug, TId::Owned: Debug, D: InspectEmbeddedResources + Debug
-{
- pub fn new() -> Self {
- MailSendDataBuilder {
- sender: None,
- from: Vec::new(),
- to: Vec::new(),
- subject: None,
- template_id: None,
- data: None
- }
- }
-
- /// adds a Mailbox to the list of from addresses
- pub fn add_from(&mut self, mb: Mailbox) -> &mut Self {
- self.from.push(mb);
- self
- }
-
- /// add a Mailbox to the list of to addresses
- pub fn add_to(&mut self, mb: Mailbox) -> &mut Self {
- self.to.push(mb);
- self
- }
-
- /// set the sender to the given mailbox and inserts it into the front of the from Mailboxes
- ///
- /// If a sender was set before it will be override, _but it still will be in the
- /// from MailboxList_.
- pub fn sender(&mut self, mb: Mailbox) -> &mut Self {
- self.sender = Some(mb.clone());
- self.from.insert(0, mb);
- self
- }
-
- /// sets the subject as a string
- ///
- /// If a subject was set previously it will be overwritten.
- pub fn subject<I>(&mut self, sbj: I) -> &mut Self
- where I: Into<String>
- {
- self.subject = Some(sbj.into());
- self
- }
-
- /// sets the template_id (borrowed form)
- ///
- /// If a template_id was set previously it will be overwritten.
- pub fn template(&mut self, tid: &'a TId) -> &mut Self {
- self.template_id = Some(Cow::Borrowed(tid));
- self
- }
-
- /// sets the template_id (owned form)
- ///
- /// If a template_id was set previously it will be overwritten.
- pub fn owned_template(&mut self, tid: <TId as ToOwned>::Owned) -> &mut Self {
- self.template_id = Some(Cow::Owned(tid));
- self
- }
-
- /// sets the template_id (cow form)
- ///
- /// If a template_id was set previously it will be overwritten.
- pub fn cow_template(&mut self, tid: Cow<'a, TId>) -> &mut Self {
- self.template_id = Some(tid);
- self
- }
-
-
- /// sets the data
- ///
- /// If data was set previously it will be overwritten.
- pub fn data(&mut self, data: D) -> &mut Self {
- self.data = Some(data);
- self
- }
-
- //TODO provide custom error
- /// create `MailSendData` from this builder if possible.
- ///
- /// If there is only one mailbox in from no sender needs
- /// to be set.
- ///
- /// # Error
- ///
- /// Cases in which an error is returned:
- ///
- /// - no data, template_id, from or to was set
- /// - more than one from was set, but no sender was set
- pub fn build(self)
- -> Result<MailSendData<'a, TId, D>, WithSource<MailSendDataError, Self>>
- {
- match self.check_fields_are_set() {
- Ok(_) => {},
- Err(err) => return Err(err.with_source(self))
- }
-
- if self.from.len() > 1 && self.sender.is_none() {
- return Err(MailSendDataError
- ::from(MailSendDataErrorKind::MultiFromButNoSender)
- .with_source(self));
- }
-
-
- //UNWRAP_SAFE..: we already checked that there is data
- let from = Vec1::from_vec(self.from).unwrap();
- let to = Vec1::from_vec(self.to).unwrap();
- let subject = self.subject.unwrap();
- let template_id = self.template_id.unwrap();
- let data = self.data.unwrap();
-
- Ok(MailSendData {
- sender: self.sender,
- from: MailboxList(from),
- to: MailboxList(to),
- subject,
- template_id,
- data
- })
- }
-
- fn check_fields_are_set(&self) -> Result<(), MailSendDataError> {
- use self::MailSendDataErrorKind::*;
- let kind =
- if self.from.is_empty() {
- MissingFrom
- } else if self.to.is_empty() {
- MissingTo
- } else if self.subject.is_none() {
- MissingSubject
- } else if self.template_id.is_none() {
- MissingTemplateId
- } else if self.data.is_none() {
- MissingTemplateData
- } else {
- return Ok(());
- };
-
- Err(MailSendDataError::from(kind))
- }
-}
diff --git a/src/compositor/impl_compose.rs b/src/compositor/impl_compose.rs
deleted file mode 100644
index 8856885..0000000
--- a/src/compositor/impl_compose.rs
+++ /dev/null
@@ -1,162 +0,0 @@
-use std::borrow::Cow;
-use std::marker::PhantomData;
-use vec1::Vec1;
-
-use headers::{
- HeaderMap, HeaderTryFrom,
- _From, _To, Subject, Sender
-};
-use headers::components::Unstructured;
-use mail::{Mail, Builder, Context};
-
-use ::resource::{
- Embedded, EmbeddedWithCId,
- InspectEmbeddedResources, Disposition
-};
-use ::builder_extension::{
- BodyPart, BuilderExt
-};
-use ::template_engine::{
- TemplateEngine, MailParts
-};
-use ::error::CompositionError;
-
-use super::MailSendData;
-
-pub(crate) fn compose_mail<'a, C, E, D>(
- ctx: &C,
- engine: &E,
- send_data: MailSendData<'a, E::TemplateId, D>
-) -> Result<Mail, CompositionError<E::Error>>
- where C: Context, E: TemplateEngine<C, D>, D: InspectEmbeddedResources
-{
- (Ctx { ctx, engine, _p: PhantomData }).compose_mail(send_data)
-}
-
-struct Ctx<'a, 'b, C: 'a, E: 'b, D>
- where C: Context, E: TemplateEngine<C, D>, D: InspectEmbeddedResources
-{
- ctx: &'a C,
- engine: &'b E,
- _p: PhantomData<D>
-}
-
-impl<'a, 'b, C: 'a, E: 'b, D> Copy for Ctx<'a, 'b, C, E, D>
- where C: Context, E: TemplateEngine<C, D>, D: InspectEmbeddedResources
-{}
-
-impl<'a, 'b, C: 'a, E: 'b, D> Clone for Ctx<'a, 'b, C, E, D>
- where C: Context, E: TemplateEngine<C, D>, D: InspectEmbeddedResources
-{
- fn clone(&self) -> Self {
- Ctx { ctx: self.ctx, engine: self.engine, _p: self._p }
- }
-}
-
-impl<'a, 'b, C, E, D> Ctx<'a, 'b, C, E, D>
- where C: Context, E: TemplateEngine<C, D>, D: InspectEmbeddedResources
-{
-
- fn compose_mail(self, send_data: MailSendData<E::TemplateId, D>)
- -> Result<Mail, CompositionError<E::Error>>
- {
- //compose display name => create Address with display name;
- let (core_headers, data, template_id) = self.process_mail_send_data(send_data)?;
-
- let MailParts { alternative_bodies, shared_embeddings, attachments }
- = self.use_template_engine(&*template_id, data)?;
-
- let mail = self.build_mail(alternative_bodies, shared_embeddings.into_iter(),
- attachments, core_headers)?;
-
- Ok(mail)
- }
-
- fn process_mail_send_data<'n>(
- self,
- send_data:
- MailSendData<'n, E::TemplateId, D>
- ) -> Result<(
- HeaderMap,
- D,
- Cow<'n, E::TemplateId>
- ), CompositionError<E::Error>>
- where D: InspectEmbeddedResources
- {
- let (sender, from_mailboxes, to_mailboxes, subject, template_id, data)
- = send_data.destruct();
-
- // The subject header field
- let subject = Unstructured::try_from( subject )?;
-
- // creating the header map
- let mut core_headers: HeaderMap = headers! {
- //NOTE: if we support multiple mailboxes in _From we have to
- // ensure Sender is used _iff_ there is more than one from
- _From: from_mailboxes,
- _To: to_mailboxes,
- Subject: subject
- }?;
-
- // add sender header if needed
- if let Some(sender) = sender {
- core_headers.insert(Sender, sender)?;
- }
-
- Ok((core_headers, data, template_id))
- }
-
- fn use_template_engine(
- self,
- template_id: &E::TemplateId,
- //TODO change to &D?
- data: D
- ) -> Result<MailParts, CompositionError<E::Error>>
- where D: InspectEmbeddedResources
- {
- let mut data = data;
- let mut embeddings = Vec::new();
- let mut attachments = Vec::new();
-
- data.inspect_resources_mut(&mut |embedded: &mut Embedded| {
- let embedded_wcid = embedded.assure_content_id_and_copy(self.ctx);
- match embedded_wcid.disposition() {
- Disposition::Inline => embeddings.push(embedded_wcid),
- Disposition::Attachment => attachments.push(embedded_wcid)
- }
- });
-
- let mut mail_parts = self.engine
- .use_template(template_id, &data, self.ctx)
- .map_err(|err| CompositionError::Template(err))?;
-
- mail_parts.attach