melib: add README.md and email module doco
parent
a1e3f269de
commit
e407b1e224
|
@ -8,10 +8,11 @@ build = "build.rs"
|
||||||
|
|
||||||
homepage = "https://meli.delivery"
|
homepage = "https://meli.delivery"
|
||||||
repository = "https://git.meli.delivery/meli/meli.git"
|
repository = "https://git.meli.delivery/meli/meli.git"
|
||||||
description = "backend mail client library"
|
description = "mail library"
|
||||||
keywords = ["mail", "mua", "maildir", "imap"]
|
keywords = ["mail", "mua", "maildir", "imap", "jmap"]
|
||||||
categories = [ "email"]
|
categories = [ "email", "parser-implementations"]
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "melib"
|
name = "melib"
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
# melib
|
||||||
|
|
||||||
|
[![GitHub license](https://img.shields.io/github/license/meli/meli)](https://github.com/meli/meli/blob/master/COPYING) [![Crates.io](https://img.shields.io/crates/v/melib)](https://crates.io/crates/melib) [![docs.rs](https://docs.rs/melib/badge.svg)](https://docs.rs/melib)
|
||||||
|
|
||||||
|
Library for handling mail.
|
||||||
|
|
||||||
|
## optional features
|
||||||
|
|
||||||
|
| feature flag | dependencies | notes |
|
||||||
|
| ---------------------- | ----------------------------------- | ------------------------ |
|
||||||
|
| `imap_backend` | `native-tls` | |
|
||||||
|
| `deflate_compression` | `flate2` | for use with IMAP |
|
||||||
|
| `jmap_backend` | `isahc`, `native-tls`, `serde_json` | |
|
||||||
|
| `maildir_backend` | `notify`, `memmap` | |
|
||||||
|
| `mbox_backend` | `notify`, `memmap` | |
|
||||||
|
| `smtp` | `native-tls`, `base64` | async SMTP communication |
|
||||||
|
|
||||||
|
## Example: Parsing bytes into an `Envelope`
|
||||||
|
|
||||||
|
An `Envelope` represents the information you can get from an email's headers
|
||||||
|
and body structure. Addresses in `To`, `From` fields etc are parsed into
|
||||||
|
`Address` types.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use melib::{Attachment, Envelope};
|
||||||
|
|
||||||
|
let raw_mail = r#"From: "some name" <some@address.com>
|
||||||
|
To: "me" <myself@i.tld>
|
||||||
|
Cc:
|
||||||
|
Subject: =?utf-8?Q?gratuitously_encoded_subject?=
|
||||||
|
Message-ID: <h2g7f.z0gy2pgaen5m@address.com>
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: multipart/mixed; charset="utf-8";
|
||||||
|
boundary="bzz_bzz__bzz__"
|
||||||
|
|
||||||
|
This is a MIME formatted message with attachments. Use a MIME-compliant client to view it properly.
|
||||||
|
--bzz_bzz__bzz__
|
||||||
|
|
||||||
|
hello world.
|
||||||
|
--bzz_bzz__bzz__
|
||||||
|
Content-Type: image/gif; name="test_image.gif"; charset="utf-8"
|
||||||
|
Content-Disposition: attachment
|
||||||
|
Content-Transfer-Encoding: base64
|
||||||
|
|
||||||
|
R0lGODdhKAAXAOfZAAABzAADzQAEzgQFtBEAxAAGxBcAxwALvRcFwAAPwBcLugATuQEUuxoNuxYQ
|
||||||
|
sxwOvAYVvBsStSAVtx8YsRUcuhwhth4iuCQsyDAwuDc1vTc3uDg4uT85rkc9ukJBvENCvURGukdF
|
||||||
|
wUVKt0hLuUxPvVZSvFlYu1hbt2BZuFxdul5joGhqlnNuf3FvlnBvwXJyt3Jxw3N0oXx1gH12gV99
|
||||||
|
z317f3N7spFxwHp5wH99gYB+goF/g25+26tziIOBhWqD3oiBjICAuudkjIN+zHeC2n6Bzc1vh4eF
|
||||||
|
iYaBw8F0kImHi4KFxYyHmIWIvI2Lj4uIvYaJyY+IuJGMi5iJl4qKxZSMmIuLxpONnpGPk42NvI2M
|
||||||
|
1LKGl46OvZePm5ORlZiQnJqSnpaUmLyJnJuTn5iVmZyUoJGVyZ2VoZSVw5iXoZmWrO18rJiUyp6W
|
||||||
|
opuYnKaVnZ+Xo5yZncaMoaCYpJiaqo+Z2Z2annuf5qGZpa2WoJybpZmayZ2Z0KCZypydrZ6dp6Cd
|
||||||
|
oZ6a0aGay5ucy5+eqKGeouWMgp+b0qKbzKCfqdqPnp2ezaGgqqOgpKafqrScpp+gz6ajqKujr62j
|
||||||
|
qayksKmmq62lsaiosqqorOyWnaqqtKeqzLGptaurta2rr7Kqtq+ssLOrt6+uuLGusuqhfbWtubCv
|
||||||
|
ubKvs7GwurOwtPSazbevu+ali7SxtbiwvOykjLOyvLWytuCmqOankrSzvbazuLmyvrW0vre0uba1
|
||||||
|
wLi1ury0wLm2u721wbe3wbq3vMC2vLi4wr+3w7m5w8C4xLi6yry6vsG5xbu7xcC6zMK6xry8xry+
|
||||||
|
u8O7x729x8C9wb++yMG+wsO+vMK/w8a+y8e/zMnBzcXH18nL2///////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
/////////////////////////////////////////////////////ywAAAAAKAAXAAAI/gBP4Cjh
|
||||||
|
IYMLEh0w4EgBgsMLEyFGFBEB5cOFABgzatS4AVssZAOsLOHCxooVMzCyoNmzaBOkJlS0VEDyZMjG
|
||||||
|
mxk3XOMF60CDBgsoPABK9KcDCRImPCiQYAECAgQCRMU4VSrGCjFarBgUSJCgQ10FBTrkNRCfPnz4
|
||||||
|
dA3UNa1btnDZqgU7Ntqzu3ej2X2mFy9eaHuhNRtMGJrhwYYN930G2K7eaNIY34U2mfJkwpgzI9Yr
|
||||||
|
GBqwR2KSvAlMOXHnw5pTNzPdLNoWIWtU9XjGjDEYS8LAlFm1SrVvzIKj5TH0KpORSZOryPgCZgqL
|
||||||
|
Ob+jG0YVRBErUrOiiGJ8KxgtYsh27xWL/tswnTtEbsiRVYdJNMHk4yOGhswGjR88UKjQ9Ey+/8TL
|
||||||
|
XKKGGn7Akph/8XX2WDTTcAYfguVt9hhrEPqmzIOJ3VUheb48WJiHG6amC4i+WVJKKCimqGIoYxyj
|
||||||
|
WWK8kKjaJ9bA18sxvXjYhourmbbMMrjI+OIn1QymDCVXANGFK4S1gQw0PxozzC+33FLLKUJq9gk1
|
||||||
|
gyWDhyNwrMLkYGUEM4wvuLRiCiieXIJJJVlmJskcZ9TZRht1lnFGGmTMkMoonVQSSSOFAGJHHI0w
|
||||||
|
ouiijDaaCCGQRgrpH3q4QYYXWDihxBE+7KCDDjnUIEVAADs=
|
||||||
|
--bzz_bzz__bzz__--"#;
|
||||||
|
|
||||||
|
let envelope = Envelope::from_bytes(raw_mail.as_bytes(), None).expect("Could not parse mail");
|
||||||
|
assert_eq!(envelope.subject().as_ref(), "gratuitously encoded subject");
|
||||||
|
assert_eq!(envelope.message_id_display().as_ref(), "<h2g7f.z0gy2pgaen5m@address.com>");
|
||||||
|
|
||||||
|
let body = envelope.body_bytes(raw_mail.as_bytes());
|
||||||
|
assert_eq!(body.content_type().to_string().as_str(), "multipart/mixed");
|
||||||
|
|
||||||
|
let body_text = body.text();
|
||||||
|
assert_eq!(body_text.as_str(), "hello world.");
|
||||||
|
|
||||||
|
let subattachments: Vec<Attachment> = body.attachments();
|
||||||
|
assert_eq!(subattachments.len(), 3);
|
||||||
|
assert_eq!(subattachments[2].content_type().name().unwrap(), "test_image.gif");
|
||||||
|
```
|
|
@ -20,8 +20,75 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Email parsing, handling, sending etc.
|
* Email parsing and composing.
|
||||||
|
*
|
||||||
|
* # Parsing bytes into an `Envelope`
|
||||||
|
*
|
||||||
|
* An [`Envelope`](Envelope) represents the information you can get from an email's headers and body
|
||||||
|
* structure. Addresses in `To`, `From` fields etc are parsed into [`Address`](email::address::Address) types.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* use melib::{Attachment, Envelope};
|
||||||
|
*
|
||||||
|
* let raw_mail = r#"From: "some name" <some@address.com>
|
||||||
|
* To: "me" <myself@i.tld>
|
||||||
|
* Cc:
|
||||||
|
* Subject: =?utf-8?Q?gratuitously_encoded_subject?=
|
||||||
|
* Message-ID: <h2g7f.z0gy2pgaen5m@address.com>
|
||||||
|
* MIME-Version: 1.0
|
||||||
|
* Content-Type: multipart/mixed; charset="utf-8";
|
||||||
|
* boundary="bzz_bzz__bzz__"
|
||||||
|
*
|
||||||
|
* This is a MIME formatted message with attachments. Use a MIME-compliant client to view it properly.
|
||||||
|
* --bzz_bzz__bzz__
|
||||||
|
*
|
||||||
|
* hello world.
|
||||||
|
* --bzz_bzz__bzz__
|
||||||
|
* Content-Type: image/gif; name="test_image.gif"; charset="utf-8"
|
||||||
|
* Content-Disposition: attachment
|
||||||
|
* Content-Transfer-Encoding: base64
|
||||||
|
*
|
||||||
|
* R0lGODdhKAAXAOfZAAABzAADzQAEzgQFtBEAxAAGxBcAxwALvRcFwAAPwBcLugATuQEUuxoNuxYQ
|
||||||
|
* sxwOvAYVvBsStSAVtx8YsRUcuhwhth4iuCQsyDAwuDc1vTc3uDg4uT85rkc9ukJBvENCvURGukdF
|
||||||
|
* wUVKt0hLuUxPvVZSvFlYu1hbt2BZuFxdul5joGhqlnNuf3FvlnBvwXJyt3Jxw3N0oXx1gH12gV99
|
||||||
|
* z317f3N7spFxwHp5wH99gYB+goF/g25+26tziIOBhWqD3oiBjICAuudkjIN+zHeC2n6Bzc1vh4eF
|
||||||
|
* iYaBw8F0kImHi4KFxYyHmIWIvI2Lj4uIvYaJyY+IuJGMi5iJl4qKxZSMmIuLxpONnpGPk42NvI2M
|
||||||
|
* 1LKGl46OvZePm5ORlZiQnJqSnpaUmLyJnJuTn5iVmZyUoJGVyZ2VoZSVw5iXoZmWrO18rJiUyp6W
|
||||||
|
* opuYnKaVnZ+Xo5yZncaMoaCYpJiaqo+Z2Z2annuf5qGZpa2WoJybpZmayZ2Z0KCZypydrZ6dp6Cd
|
||||||
|
* oZ6a0aGay5ucy5+eqKGeouWMgp+b0qKbzKCfqdqPnp2ezaGgqqOgpKafqrScpp+gz6ajqKujr62j
|
||||||
|
* qayksKmmq62lsaiosqqorOyWnaqqtKeqzLGptaurta2rr7Kqtq+ssLOrt6+uuLGusuqhfbWtubCv
|
||||||
|
* ubKvs7GwurOwtPSazbevu+ali7SxtbiwvOykjLOyvLWytuCmqOankrSzvbazuLmyvrW0vre0uba1
|
||||||
|
* wLi1ury0wLm2u721wbe3wbq3vMC2vLi4wr+3w7m5w8C4xLi6yry6vsG5xbu7xcC6zMK6xry8xry+
|
||||||
|
* u8O7x729x8C9wb++yMG+wsO+vMK/w8a+y8e/zMnBzcXH18nL2///////////////////////////
|
||||||
|
* ////////////////////////////////////////////////////////////////////////////
|
||||||
|
* /////////////////////////////////////////////////////ywAAAAAKAAXAAAI/gBP4Cjh
|
||||||
|
* IYMLEh0w4EgBgsMLEyFGFBEB5cOFABgzatS4AVssZAOsLOHCxooVMzCyoNmzaBOkJlS0VEDyZMjG
|
||||||
|
* mxk3XOMF60CDBgsoPABK9KcDCRImPCiQYAECAgQCRMU4VSrGCjFarBgUSJCgQ10FBTrkNRCfPnz4
|
||||||
|
* dA3UNa1btnDZqgU7Ntqzu3ej2X2mFy9eaHuhNRtMGJrhwYYN930G2K7eaNIY34U2mfJkwpgzI9Yr
|
||||||
|
* GBqwR2KSvAlMOXHnw5pTNzPdLNoWIWtU9XjGjDEYS8LAlFm1SrVvzIKj5TH0KpORSZOryPgCZgqL
|
||||||
|
* Ob+jG0YVRBErUrOiiGJ8KxgtYsh27xWL/tswnTtEbsiRVYdJNMHk4yOGhswGjR88UKjQ9Ey+/8TL
|
||||||
|
* XKKGGn7Akph/8XX2WDTTcAYfguVt9hhrEPqmzIOJ3VUheb48WJiHG6amC4i+WVJKKCimqGIoYxyj
|
||||||
|
* WWK8kKjaJ9bA18sxvXjYhourmbbMMrjI+OIn1QymDCVXANGFK4S1gQw0PxozzC+33FLLKUJq9gk1
|
||||||
|
* gyWDhyNwrMLkYGUEM4wvuLRiCiieXIJJJVlmJskcZ9TZRht1lnFGGmTMkMoonVQSSSOFAGJHHI0w
|
||||||
|
* ouiijDaaCCGQRgrpH3q4QYYXWDihxBE+7KCDDjnUIEVAADs=
|
||||||
|
* --bzz_bzz__bzz__--"#;
|
||||||
|
*
|
||||||
|
* let envelope = Envelope::from_bytes(raw_mail.as_bytes(), None).expect("Could not parse mail");
|
||||||
|
* assert_eq!(envelope.subject().as_ref(), "gratuitously encoded subject");
|
||||||
|
* assert_eq!(envelope.message_id_display().as_ref(), "<h2g7f.z0gy2pgaen5m@address.com>");
|
||||||
|
*
|
||||||
|
* let body = envelope.body_bytes(raw_mail.as_bytes());
|
||||||
|
* assert_eq!(body.content_type().to_string().as_str(), "multipart/mixed");
|
||||||
|
*
|
||||||
|
* let body_text = body.text();
|
||||||
|
* assert_eq!(body_text.as_str(), "hello world.");
|
||||||
|
*
|
||||||
|
* let subattachments: Vec<Attachment> = body.attachments();
|
||||||
|
* assert_eq!(subattachments.len(), 3);
|
||||||
|
* assert_eq!(subattachments[2].content_type().name().unwrap(), "test_image.gif");
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub mod address;
|
pub mod address;
|
||||||
pub mod attachment_types;
|
pub mod attachment_types;
|
||||||
pub mod attachments;
|
pub mod attachments;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*! Encoding/decoding of attachments */
|
||||||
use crate::email::{
|
use crate::email::{
|
||||||
address::StrBuilder,
|
address::StrBuilder,
|
||||||
parser::{self, BytesExt},
|
parser::{self, BytesExt},
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*! Compose a `Draft`, with MIME and attachment support */
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::email::attachment_types::{
|
use crate::email::attachment_types::{
|
||||||
Charset, ContentTransferEncoding, ContentType, MultipartType,
|
Charset, ContentTransferEncoding, ContentType, MultipartType,
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*! Wrapper type `HeaderName` for case-insensitive comparisons */
|
||||||
use crate::error::MeliError;
|
use crate::error::MeliError;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*! Parsing of rfc2369/rfc2919 `List-*` headers */
|
||||||
use super::parser;
|
use super::parser;
|
||||||
use super::Envelope;
|
use super::Envelope;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*! Parsing of `mailto` addresses */
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*! Parsers for email. See submodules */
|
||||||
use crate::error::{MeliError, Result, ResultIntoMeliError};
|
use crate::error::{MeliError, Result, ResultIntoMeliError};
|
||||||
use nom::{
|
use nom::{
|
||||||
branch::alt,
|
branch::alt,
|
||||||
|
@ -281,6 +282,7 @@ pub fn mail(input: &[u8]) -> Result<(Vec<(&[u8], &[u8])>, &[u8])> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod dates {
|
pub mod dates {
|
||||||
|
/*! Date values in headers */
|
||||||
use super::generic::*;
|
use super::generic::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::datetime::UnixTimestamp;
|
use crate::datetime::UnixTimestamp;
|
||||||
|
@ -505,6 +507,7 @@ pub mod dates {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod generic {
|
pub mod generic {
|
||||||
|
/*! Generally useful parser combinators */
|
||||||
use super::*;
|
use super::*;
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn byte_in_slice<'a>(slice: &'static [u8]) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], u8> {
|
pub fn byte_in_slice<'a>(slice: &'static [u8]) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], u8> {
|
||||||
|
@ -1205,6 +1208,7 @@ List-Archive: <http://www.host.com/list/archive/> (Web Archive)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod headers {
|
pub mod headers {
|
||||||
|
/*! Email headers */
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn headers(input: &[u8]) -> IResult<&[u8], Vec<(&[u8], &[u8])>> {
|
pub fn headers(input: &[u8]) -> IResult<&[u8], Vec<(&[u8], &[u8])>> {
|
||||||
|
@ -1465,6 +1469,7 @@ pub mod headers {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod attachments {
|
pub mod attachments {
|
||||||
|
/*! Email attachments */
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::email::address::*;
|
use crate::email::address::*;
|
||||||
use crate::email::attachment_types::{ContentDisposition, ContentDispositionKind};
|
use crate::email::attachment_types::{ContentDisposition, ContentDispositionKind};
|
||||||
|
@ -1731,6 +1736,7 @@ pub mod attachments {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod encodings {
|
pub mod encodings {
|
||||||
|
/*! Email encodings (quoted printable, MIME) */
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::email::attachment_types::Charset;
|
use crate::email::attachment_types::Charset;
|
||||||
use data_encoding::BASE64_MIME;
|
use data_encoding::BASE64_MIME;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*! Verification of OpenPGP signatures */
|
||||||
use crate::email::parser::BytesExt;
|
use crate::email::parser::BytesExt;
|
||||||
use crate::email::{
|
use crate::email::{
|
||||||
attachment_types::{ContentType, MultipartType},
|
attachment_types::{ContentType, MultipartType},
|
||||||
|
@ -26,7 +27,10 @@ use crate::email::{
|
||||||
};
|
};
|
||||||
use crate::{MeliError, Result};
|
use crate::{MeliError, Result};
|
||||||
|
|
||||||
/// rfc3156
|
/// Convert raw attachment to the form needed for signature verification ([rfc3156](https://tools.ietf.org/html/rfc3156))
|
||||||
|
///
|
||||||
|
/// ## rfc3156
|
||||||
|
/// ```text
|
||||||
/// Upon receipt of a signed message, an application MUST:
|
/// Upon receipt of a signed message, an application MUST:
|
||||||
///
|
///
|
||||||
/// (1) Convert line endings to the canonical <CR><LF> sequence before
|
/// (1) Convert line endings to the canonical <CR><LF> sequence before
|
||||||
|
@ -35,7 +39,7 @@ use crate::{MeliError, Result};
|
||||||
/// (2) Pass both the signed data and its associated content headers
|
/// (2) Pass both the signed data and its associated content headers
|
||||||
/// along with the OpenPGP signature to the signature verification
|
/// along with the OpenPGP signature to the signature verification
|
||||||
/// service.
|
/// service.
|
||||||
///
|
/// ```
|
||||||
pub fn convert_attachment_to_rfc_spec(input: &[u8]) -> Vec<u8> {
|
pub fn convert_attachment_to_rfc_spec(input: &[u8]) -> Vec<u8> {
|
||||||
if input.is_empty() {
|
if input.is_empty() {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
|
@ -125,8 +129,8 @@ pub fn verify_signature(a: &Attachment) -> Result<(Vec<u8>, &[u8])> {
|
||||||
};
|
};
|
||||||
Ok((signed_part, signature))
|
Ok((signed_part, signature))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => Err(MeliError::new(
|
||||||
unreachable!("Should not give non-signed attachments to this function");
|
"Should not give non-signed attachments to this function",
|
||||||
}
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue