Repository: stalwartlabs/mail-send (language: rust)

Issue:
impl IntoMessage for Message
I need the exact conversion method as defined here: https://github.com/stalwartlabs/mail-send/blob/d97850d0c93fc9502097c2073f361458b54f5cf5/src/smtp/message.rs#L283-L285 but from a `mail_parser::Message`. Would you accept a PR based on the same logic, behind a `mail-parser` cargo feature?


Discussion / hints:
Sure!

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index 37ffd9a..91ffc05 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@ doctest = false
 smtp-proto = { version = "0.1" }
 mail-auth = { version = "0.3", optional = true }
 mail-builder = { version = "0.3", optional = true }
+mail-parser = { version = "0.9", optional = true }
 base64 = "0.21"
 rand = { version = "0.8.5", optional = true }
 md5 = { version = "0.7.0", optional = true }
@@ -33,8 +34,9 @@ tokio = { version = "1.16", features = ["net", "io-util", "time", "rt-multi-thre
 env_logger = "0.10.0"
 
 [features]
-default = ["digest-md5", "cram-md5", "builder", "dkim"]
+default = ["digest-md5", "cram-md5", "builder", "parser", "dkim"]
 builder = ["mail-builder"]
+parser = ["mail-parser"]
 dkim = ["mail-auth"]
 digest-md5 = ["md5", "rand"]
 cram-md5 = ["md5"]
diff --git a/src/smtp/message.rs b/src/smtp/message.rs
index 46726ce..d0b6722 100644
--- a/src/smtp/message.rs
+++ b/src/smtp/message.rs
@@ -18,6 +18,8 @@ use mail_builder::{
     headers::{address, HeaderType},
     MessageBuilder,
 };
+#[cfg(feature = "parser")]
+use mail_parser::{HeaderName, HeaderValue};
 use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
 
 use crate::SmtpClient;
@@ -348,3 +350,68 @@ impl<'x, 'y> IntoMessage<'x> for MessageBuilder<'y> {
         })
     }
 }
+
+#[cfg(feature = "parser")]
+impl<'x> IntoMessage<'x> for mail_parser::Message<'x> {
+    fn into_message(self) -> crate::Result<Message<'x>> {
+        let mut mail_from = None;
+        let mut rcpt_to = std::collections::HashSet::new();
+
+        let find_address = |addr: &mail_parser::Addr| -> Option<String> {
+            addr.address
+                .as_ref()
+                .filter(|address| !address.trim().is_empty())
+                .map(|address| address.trim().to_string())
+        };
+
+        for header in self.headers() {
+            match &header.name {
+                HeaderName::From => match header.value() {
+                    HeaderValue::Address(mail_parser::Address::List(addrs)) => {
+                        if let Some(email) = addrs.iter().find_map(find_address) {
+                            mail_from = email.to_string().into();
+                        }
+                    }
+                    HeaderValue::Address(mail_parser::Address::Group(groups)) => {
+                        if let Some(grps) = groups.first() {
+                            if let Some(email) = grps.addresses.iter().find_map(find_address) {
+                                mail_from = email.to_string().into();
+                            }
+                        }
+                    }
+                    _ => (),
+                },
+                HeaderName::To | HeaderName::Cc | HeaderName::Bcc => match header.value() {
+                    HeaderValue::Address(mail_parser::Address::List(addrs)) => {
+                        rcpt_to.extend(addrs.iter().filter_map(find_address));
+                    }
+                    HeaderValue::Address(mail_parser::Address::Group(grps)) => {
+                        rcpt_to.extend(
+                            grps.iter()
+                                .flat_map(|grp| grp.addresses.iter())
+                                .filter_map(find_address),
+                        );
+                    }
+                    _ => (),
+                },
+                _ => (),
+            };
+        }
+
+        if rcpt_to.is_empty() {
+            return Err(crate::Error::MissingRcptTo);
+        }
+
+        Ok(Message {
+            mail_from: mail_from.ok_or(crate::Error::MissingMailFrom)?.into(),
+            rcpt_to: rcpt_to
+                .into_iter()
+                .map(|email| Address {
+                    email: email.into(),
+                    parameters: Parameters::default(),
+                })
+                .collect(),
+            body: self.raw_message,
+        })
+    }
+}


Test patch: